or run

tessl search
Log in

Version

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
mavenpkg:maven/io.quarkus/quarkus-resteasy-reactive@3.15.x

docs

index.md
tile.json

tessl/maven-io-quarkus--quarkus-resteasy-reactive

tessl install tessl/maven-io-quarkus--quarkus-resteasy-reactive@3.15.0

A Jakarta REST implementation utilizing build time processing and Vert.x for high-performance REST endpoints with reactive capabilities in cloud-native environments.

filters.mddocs/reference/

Filters

Quarkus REST provides declarative request and response filters for cross-cutting concerns like authentication, authorization, logging, header manipulation, and request/response transformation.

Import

import org.jboss.resteasy.reactive.server.ServerRequestFilter;
import org.jboss.resteasy.reactive.server.ServerResponseFilter;
import org.jboss.resteasy.reactive.server.WithFormRead;
import jakarta.ws.rs.Priorities;
import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.container.ContainerResponseContext;
import jakarta.ws.rs.core.*;

@ServerRequestFilter

Declarative request filter executed before the resource method. Can abort request processing by returning a Response.

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ServerRequestFilter {
    int priority() default Priorities.USER;  // Execution priority
    boolean preMatching() default false;     // Execute before resource matching
    boolean nonBlocking() default false;     // Force non-blocking execution
    boolean readBody() default false;        // Deprecated: Read body before filter
}

Supported Parameter Types

Filter methods can inject the following parameters:

  • ContainerRequestContext - Request context
  • UriInfo - URI information
  • HttpHeaders - HTTP headers
  • Request - HTTP request
  • ResourceInfo - Resource method info (full)
  • SimpleResourceInfo - Resource method info (lightweight)

Supported Return Types

  • void - Cannot abort request
  • Response - Non-null aborts with response
  • RestResponse<T> - Non-null aborts with response
  • Optional<Response> - Present value aborts with response
  • Optional<RestResponse<T>> - Present value aborts with response
  • Uni<Void> - Async, cannot abort
  • Uni<Response> - Async, non-null aborts
  • Uni<RestResponse<T>> - Async, non-null aborts

@ServerResponseFilter

Declarative response filter executed after the resource method. Can modify the response.

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ServerResponseFilter {
    int priority() default Priorities.USER;  // Execution priority
}

Supported Parameter Types

  • ContainerRequestContext - Request context
  • ContainerResponseContext - Response context (required for modification)
  • ResourceInfo - Resource method info (full)
  • UriInfo - URI information
  • SimpleResourceInfo - Resource method info (lightweight)
  • Throwable - Exception if thrown, null otherwise

Supported Return Types

  • void - Standard synchronous filter
  • Uni<Void> - Async filter

@WithFormRead

Forces form body to be read before filter or endpoint execution.

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface WithFormRead {
}

Request Filter Patterns

Authentication Filter

@ServerRequestFilter(priority = Priorities.AUTHENTICATION)
public Optional<RestResponse<String>> authenticate(HttpHeaders headers) {
    String authHeader = headers.getHeaderString("Authorization");

    if (authHeader == null || !authHeader.startsWith("Bearer ")) {
        return Optional.of(RestResponse.status(401, "Missing or invalid token"));
    }

    String token = authHeader.substring(7);
    if (!tokenService.isValid(token)) {
        return Optional.of(RestResponse.status(401, "Invalid token"));
    }

    return Optional.empty();  // Continue request processing
}

Authorization Filter

@ServerRequestFilter(priority = Priorities.AUTHORIZATION)
public Optional<Response> authorize(UriInfo uriInfo, SecurityContext securityContext) {
    String path = uriInfo.getPath();

    if (path.startsWith("/admin") && !securityContext.isUserInRole("ADMIN")) {
        return Optional.of(Response.status(403).entity("Forbidden").build());
    }

    return Optional.empty();
}

Logging Filter

@ServerRequestFilter
public void logRequest(UriInfo uriInfo, HttpHeaders headers) {
    String method = headers.getHeaderString("X-HTTP-Method-Override");
    String path = uriInfo.getPath();
    logger.info("Request: {} {}", method, path);
}

Pre-Matching Filter

Executes before resource method matching, useful for request transformation:

@ServerRequestFilter(preMatching = true)
public void preprocessRequest(ContainerRequestContext requestContext) {
    // Modify URI before matching
    String path = requestContext.getUriInfo().getPath();
    if (path.startsWith("/v1/")) {
        requestContext.setRequestUri(
            requestContext.getUriInfo().getBaseUri(),
            URI.create(path.replace("/v1/", "/api/"))
        );
    }
}

Conditional Filter

@ServerRequestFilter
public Optional<Response> conditionalFilter(
    UriInfo uriInfo,
    SimpleResourceInfo resourceInfo
) {
    // Only apply to specific resources
    if (resourceInfo.getResourceClass().equals(SecureResource.class)) {
        // Apply security check
        if (!checkSecurity()) {
            return Optional.of(Response.status(403).build());
        }
    }

    return Optional.empty();
}

Form Body Access

@ServerRequestFilter
@WithFormRead
public Optional<Response> validateForm(ContainerRequestContext context) {
    // Form body is read and available
    MultivaluedMap<String, String> formParams =
        context.getUriInfo().getQueryParameters();

    if (!formParams.containsKey("required_field")) {
        return Optional.of(Response.status(400)
            .entity("Missing required field")
            .build());
    }

    return Optional.empty();
}

Async Request Filter

@ServerRequestFilter(priority = Priorities.AUTHENTICATION)
public Uni<Optional<RestResponse<String>>> authenticateAsync(HttpHeaders headers) {
    String token = headers.getHeaderString("Authorization");

    if (token == null) {
        return Uni.createFrom().item(
            Optional.of(RestResponse.status(401, "Missing token"))
        );
    }

    return tokenService.validateAsync(token)
        .map(valid -> valid
            ? Optional.empty()
            : Optional.of(RestResponse.status(401, "Invalid token"))
        );
}

Response Filter Patterns

Add Response Headers

@ServerResponseFilter
public void addHeaders(ContainerResponseContext responseContext) {
    responseContext.getHeaders().add("X-Custom-Header", "value");
    responseContext.getHeaders().add("X-Response-Time", System.currentTimeMillis());
}

CORS Filter

@ServerResponseFilter
public void corsFilter(ContainerResponseContext response) {
    response.getHeaders().add("Access-Control-Allow-Origin", "*");
    response.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
    response.getHeaders().add("Access-Control-Allow-Headers", "Content-Type, Authorization");
    response.getHeaders().add("Access-Control-Max-Age", "3600");
}

Logging Response

@ServerResponseFilter
public void logResponse(
    ContainerRequestContext request,
    ContainerResponseContext response,
    Throwable thrown
) {
    String path = request.getUriInfo().getPath();
    int status = response.getStatus();

    if (thrown != null) {
        logger.error("Request failed: {} - {}", path, thrown.getMessage());
    } else {
        logger.info("Response: {} - {}", path, status);
    }
}

Modify Response Entity

@ServerResponseFilter
public void wrapResponse(ContainerResponseContext response) {
    if (response.hasEntity() && response.getStatus() == 200) {
        Object entity = response.getEntity();

        // Wrap entity in envelope
        ResponseEnvelope envelope = new ResponseEnvelope();
        envelope.setData(entity);
        envelope.setTimestamp(Instant.now());

        response.setEntity(envelope);
    }
}

Error Response Filter

@ServerResponseFilter
public void handleErrors(
    ContainerResponseContext response,
    Throwable thrown
) {
    if (thrown != null) {
        ErrorResponse error = new ErrorResponse();
        error.setMessage(thrown.getMessage());
        error.setTimestamp(Instant.now());

        response.setStatus(500);
        response.setEntity(error);
    }
}

Conditional Response Filter

@ServerResponseFilter
public void conditionalModification(
    ContainerResponseContext response,
    ResourceInfo resourceInfo
) {
    // Only modify responses from specific resources
    if (resourceInfo.getResourceClass().equals(ApiResource.class)) {
        response.getHeaders().add("X-API-Version", "1.0");
    }
}

Async Response Filter

@ServerResponseFilter
public Uni<Void> asyncFilter(ContainerResponseContext response) {
    return metricsService.recordResponseAsync(response.getStatus())
        .replaceWithVoid();
}

Filter Priority

Filters execute in priority order. Use jakarta.ws.rs.Priorities constants:

public class Priorities {
    public static final int AUTHENTICATION = 1000;
    public static final int AUTHORIZATION = 2000;
    public static final int HEADER_DECORATOR = 3000;
    public static final int ENTITY_CODER = 4000;
    public static final int USER = 5000;
}

Request filters: Lower priority executes first (1000 before 5000) Response filters: Higher priority executes first (5000 before 1000)

@ServerRequestFilter(priority = Priorities.AUTHENTICATION)  // Executes first
public Optional<Response> authenticate() { ... }

@ServerRequestFilter(priority = Priorities.AUTHORIZATION)   // Executes second
public Optional<Response> authorize() { ... }

@ServerRequestFilter(priority = Priorities.USER)            // Executes last
public void customFilter() { ... }

Global vs Local Filters

Global Filters

Defined at the class level (outside resource classes) and apply to all requests:

@ApplicationScoped
public class GlobalFilters {

    @ServerRequestFilter
    public Optional<Response> globalAuth(HttpHeaders headers) {
        // Applies to all requests
    }
}

Local Filters

Defined within resource classes and only apply to that resource:

@Path("/items")
public class ItemResource {

    @ServerRequestFilter
    public Optional<Response> localAuth(HttpHeaders headers) {
        // Only applies to /items/* endpoints
    }

    @GET
    public List<Item> list() { ... }
}

Aborting Requests

Request filters can abort processing by returning a non-empty response:

// Abort with Response
@ServerRequestFilter
public Optional<Response> filter() {
    if (shouldAbort()) {
        return Optional.of(Response.status(403).build());
    }
    return Optional.empty();
}

// Abort with RestResponse
@ServerRequestFilter
public Optional<RestResponse<String>> filter() {
    if (shouldAbort()) {
        return Optional.of(RestResponse.status(403, "Forbidden"));
    }
    return Optional.empty();
}

// Cannot abort (void return)
@ServerRequestFilter
public void filter() {
    // Can only modify request, cannot abort
}

Non-Blocking Filters

By default, filters execute on the I/O thread. Use nonBlocking = false to force worker thread execution:

@ServerRequestFilter(nonBlocking = false)
public Optional<Response> blockingFilter() {
    // Executes on worker thread
    // Safe for blocking operations
}

For reactive filters, use Uni return type:

@ServerRequestFilter
public Uni<Optional<Response>> reactiveFilter() {
    return someAsyncOperation()
        .map(result -> result.isValid()
            ? Optional.empty()
            : Optional.of(Response.status(400).build())
        );
}