CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-io-quarkus--quarkus-reactive-routes

REST framework offering the route model to define non blocking endpoints

Pending
Overview
Eval results
Files

request-context.mddocs/

Request Context and Exchange

Comprehensive interface for accessing and manipulating HTTP request and response objects, providing both low-level Vert.x access and high-level convenience methods.

Capabilities

RoutingExchange Interface

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));
    }
}

Low-Level Vert.x Access

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; }
}

Response Status and Headers

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");
    }
}

Error Handling

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

docs

index.md

parameter-injection.md

reactive-streaming.md

request-context.md

request-filtering.md

route-declaration.md

tile.json