REST framework offering the route model to define non blocking endpoints
—
Comprehensive interface for accessing and manipulating HTTP request and response objects, providing both low-level Vert.x access and high-level convenience methods.
Primary interface providing convenient access to HTTP request/response context with both low-level Vert.x access and high-level convenience methods.
/**
* Convenient wrapper around Vert.x RoutingContext for route methods
* Provides access to request/response objects and convenience methods
*/
public interface RoutingExchange {
/**
* Access to underlying Vert.x RoutingContext
* @return The Vert.x routing context
*/
io.vertx.ext.web.RoutingContext context();
/**
* Access to HTTP request object
* @return Vert.x HTTP server request
*/
io.vertx.core.http.HttpServerRequest request();
/**
* Access to HTTP response object
* @return Vert.x HTTP server response
*/
io.vertx.core.http.HttpServerResponse response();
/**
* Get request parameter by name
* @param name Parameter name
* @return Optional parameter value
*/
Optional<String> getParam(String name);
/**
* Get HTTP header by name
* @param name Header name (accepts CharSequence for flexibility)
* @return Optional header value
*/
Optional<String> getHeader(CharSequence name);
/**
* Set response status to 200 and return response object for chaining
* Must call HttpServerResponse.end() to complete the response
* @return The HTTP response object for chaining
*/
io.vertx.core.http.HttpServerResponse ok();
/**
* Set response status to 200, write content, and end response
* @param chunk Response content to write
*/
void ok(String chunk);
/**
* Set response status to 500 and return response object for chaining
* Must call HttpServerResponse.end() to complete the response
* @return The HTTP response object for chaining
*/
io.vertx.core.http.HttpServerResponse serverError();
/**
* Set response status to 404 and return response object for chaining
* Must call HttpServerResponse.end() to complete the response
* @return The HTTP response object for chaining
*/
io.vertx.core.http.HttpServerResponse notFound();
}Usage Examples:
import io.quarkus.vertx.web.RoutingExchange;
import io.vertx.core.http.HttpHeaders;
@ApplicationScoped
public class ContextExamples {
// Basic context access
@Route(path = "/info", methods = HttpMethod.GET)
public String getRequestInfo(RoutingExchange exchange) {
String method = exchange.request().method().name();
String path = exchange.request().path();
String userAgent = exchange.getHeader("User-Agent").orElse("Unknown");
return String.format("Method: %s, Path: %s, UA: %s", method, path, userAgent);
}
// Response manipulation
@Route(path = "/custom-response", methods = HttpMethod.GET)
public void customResponse(RoutingExchange exchange) {
exchange.response()
.putHeader("X-Custom-Header", "MyValue")
.putHeader("Cache-Control", "no-cache");
exchange.ok("Custom response with headers");
}
// Conditional responses
@Route(path = "/conditional/:id", methods = HttpMethod.GET)
public void conditionalResponse(@Param("id") String id, RoutingExchange exchange) {
if (id == null || id.trim().isEmpty()) {
exchange.notFound().end("Item not found");
return;
}
try {
long itemId = Long.parseLong(id);
if (itemId <= 0) {
exchange.serverError().end("Invalid item ID");
return;
}
exchange.ok("Item: " + itemId);
} catch (NumberFormatException e) {
exchange.serverError().end("Invalid item ID format");
}
}
// Query parameter access
@Route(path = "/search", methods = HttpMethod.GET)
public void search(RoutingExchange exchange) {
String query = exchange.getParam("q").orElse("");
String limit = exchange.getParam("limit").orElse("10");
String offset = exchange.getParam("offset").orElse("0");
if (query.isEmpty()) {
exchange.ok("No search query provided");
return;
}
exchange.ok(String.format("Search: %s (limit: %s, offset: %s)",
query, limit, offset));
}
}Direct access to Vert.x objects for advanced request/response manipulation.
// Access patterns for low-level operations
io.vertx.ext.web.RoutingContext context = exchange.context();
io.vertx.core.http.HttpServerRequest request = exchange.request();
io.vertx.core.http.HttpServerResponse response = exchange.response();Advanced Usage Examples:
@ApplicationScoped
public class AdvancedContextExamples {
// File upload handling
@Route(path = "/upload", methods = HttpMethod.POST, type = Route.HandlerType.BLOCKING)
public String handleFileUpload(RoutingExchange exchange) {
io.vertx.ext.web.RoutingContext context = exchange.context();
// Enable body handler for file uploads (configure in application)
Set<io.vertx.ext.web.FileUpload> uploads = context.fileUploads();
if (uploads.isEmpty()) {
return exchange.ok("No files uploaded");
}
StringBuilder result = new StringBuilder("Uploaded files:\n");
for (io.vertx.ext.web.FileUpload upload : uploads) {
result.append("- ").append(upload.fileName())
.append(" (").append(upload.size()).append(" bytes)\n");
}
return exchange.ok(result.toString());
}
// Cookie handling
@Route(path = "/set-cookie", methods = HttpMethod.POST)
public String setCookie(@Body CookieData cookieData, RoutingExchange exchange) {
io.vertx.core.http.HttpServerResponse response = exchange.response();
response.addCookie(io.vertx.core.http.Cookie.cookie(
cookieData.getName(),
cookieData.getValue()
).setMaxAge(3600).setPath("/"));
return exchange.ok("Cookie set: " + cookieData.getName());
}
// Session handling
@Route(path = "/session", methods = HttpMethod.GET)
public String handleSession(RoutingExchange exchange) {
io.vertx.ext.web.RoutingContext context = exchange.context();
io.vertx.ext.web.Session session = context.session();
if (session == null) {
return exchange.ok("No session available");
}
String visitCount = session.get("visitCount");
int count = visitCount != null ? Integer.parseInt(visitCount) : 0;
count++;
session.put("visitCount", String.valueOf(count));
return exchange.ok("Visit count: " + count);
}
// Request body streaming
@Route(path = "/stream-body", methods = HttpMethod.POST, type = Route.HandlerType.NORMAL)
public Uni<String> streamRequestBody(RoutingExchange exchange) {
io.vertx.core.http.HttpServerRequest request = exchange.request();
return request.body()
.map(buffer -> {
int size = buffer.length();
String preview = buffer.getString(0, Math.min(100, size));
return exchange.ok("Received " + size + " bytes, preview: " + preview);
});
}
}
// Supporting classes
public class CookieData {
private String name;
private String value;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getValue() { return value; }
public void setValue(String value) { this.value = value; }
}Comprehensive response manipulation including status codes, headers, and content types.
@ApplicationScoped
public class ResponseExamples {
// Custom status codes
@Route(path = "/custom-status/:code", methods = HttpMethod.GET)
public String customStatus(@Param("code") String statusCode, RoutingExchange exchange) {
try {
int code = Integer.parseInt(statusCode);
exchange.response().setStatusCode(code);
return exchange.ok("Status set to: " + code);
} catch (NumberFormatException e) {
return exchange.serverError();
}
}
// CORS headers
@Route(path = "/cors-enabled", methods = HttpMethod.GET)
public String corsEnabled(RoutingExchange exchange) {
io.vertx.core.http.HttpServerResponse response = exchange.response();
response.putHeader("Access-Control-Allow-Origin", "*")
.putHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE")
.putHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
return exchange.ok("CORS headers set");
}
// Content-Type manipulation
@Route(path = "/xml-response", methods = HttpMethod.GET)
public String xmlResponse(RoutingExchange exchange) {
exchange.response().putHeader("Content-Type", "application/xml");
return exchange.ok("<?xml version=\"1.0\"?><root><message>Hello XML</message></root>");
}
// Cache control
@Route(path = "/cached-resource", methods = HttpMethod.GET)
public String cachedResource(RoutingExchange exchange) {
exchange.response()
.putHeader("Cache-Control", "public, max-age=3600")
.putHeader("ETag", "\"resource-v1\"");
return exchange.ok("This response is cacheable");
}
}Comprehensive error handling using the RoutingExchange interface.
@ApplicationScoped
public class ErrorHandlingExamples {
// Validation with custom error responses
@Route(path = "/validate/:id", methods = HttpMethod.GET)
public String validateId(@Param("id") String id, RoutingExchange exchange) {
if (id == null || id.trim().isEmpty()) {
exchange.response().setStatusCode(400);
return exchange.ok("Bad Request: ID is required");
}
try {
long numericId = Long.parseLong(id);
if (numericId <= 0) {
exchange.response().setStatusCode(400);
return exchange.ok("Bad Request: ID must be positive");
}
return exchange.ok("Valid ID: " + numericId);
} catch (NumberFormatException e) {
exchange.response().setStatusCode(400);
return exchange.ok("Bad Request: ID must be numeric");
}
}
// Authentication check
@Route(path = "/protected-resource", methods = HttpMethod.GET)
public String protectedResource(RoutingExchange exchange) {
String authHeader = exchange.getHeader("Authorization").orElse("");
if (!authHeader.startsWith("Bearer ")) {
exchange.response().setStatusCode(401)
.putHeader("WWW-Authenticate", "Bearer");
return exchange.ok("Unauthorized: Bearer token required");
}
String token = authHeader.substring(7);
if (!isValidToken(token)) {
exchange.response().setStatusCode(403);
return exchange.ok("Forbidden: Invalid token");
}
return exchange.ok("Access granted to protected resource");
}
private boolean isValidToken(String token) {
// Token validation logic
return token.length() > 10;
}
}Install with Tessl CLI
npx tessl i tessl/maven-io-quarkus--quarkus-reactive-routes