docs
reference
tessl install tessl/maven-io-quarkus--quarkus-rest@3.15.0A Jakarta REST implementation utilizing build time processing and Vert.x for high-performance REST endpoints with reactive programming support, security integration, and cloud-native features.
Quarkus REST provides simplified annotations for implementing request and response filters without needing to implement JAX-RS ContainerRequestFilter or ContainerResponseFilter interfaces directly. The framework generates filter implementations from annotated methods with automatic parameter injection.
Process requests before they reach endpoint methods using method-level filter annotations with automatic parameter injection and flexible return types.
package org.jboss.resteasy.reactive.server;
/**
* Annotation for simplified request filtering.
* When applied to a method, generates a ContainerRequestFilter implementation.
*
* Supported method parameters (any order):
* - ContainerRequestContext
* - UriInfo
* - HttpHeaders
* - Request
* - ResourceInfo
* - SimpleResourceInfo
*
* Supported return types:
* - void: No blocking ops, cannot abort
* - Response or RestResponse: No blocking ops, aborts if non-null
* - Optional<Response> or Optional<RestResponse>: No blocking ops, aborts if present
* - Uni<Void>: Reactive, cannot abort
* - Uni<Response> or Uni<RestResponse>: Reactive, aborts if non-null
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface ServerRequestFilter {
/** Priority for filter execution (default: Priorities.USER) */
int priority() default Priorities.USER;
/** Whether filter runs before resource matching */
boolean preMatching() default false;
/** Run on event-loop even when target method is blocking */
boolean nonBlocking() default false;
/** @deprecated Use @WithFormRead instead */
@Deprecated
boolean readBody() default false;
}Usage Examples:
import org.jboss.resteasy.reactive.server.ServerRequestFilter;
import jakarta.ws.rs.core.UriInfo;
import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.core.Response;
public class CustomFilters {
// Simple void filter - no abort capability
@ServerRequestFilter
public void logRequest(UriInfo uriInfo) {
System.out.println("Request to: " + uriInfo.getPath());
}
// Filter that can abort with Response
@ServerRequestFilter(priority = 100)
public Response checkAuth(HttpHeaders headers) {
String token = headers.getHeaderString("Authorization");
if (token == null) {
return Response.status(401).entity("Missing token").build();
}
return null; // Continue processing
}
// Filter with Optional for conditional abort
@ServerRequestFilter
public Optional<Response> validateApiKey(UriInfo uriInfo, HttpHeaders headers) {
if (uriInfo.getPath().startsWith("/api/")) {
String key = headers.getHeaderString("X-API-Key");
if (!isValidKey(key)) {
return Optional.of(Response.status(403).build());
}
}
return Optional.empty();
}
// Reactive filter with Uni
@ServerRequestFilter
public Uni<RestResponse<String>> asyncValidation(UriInfo uriInfo) {
return validateAsync(uriInfo.getPath())
.onItem().transform(valid ->
valid ? null : RestResponse.status(403, "Invalid request")
);
}
// Pre-matching filter (runs before resource matching)
@ServerRequestFilter(preMatching = true, priority = 1)
public void earlyFilter(ContainerRequestContext context) {
// Runs before resource matching
}
private boolean isValidKey(String key) {
return key != null && key.startsWith("sk-");
}
private Uni<Boolean> validateAsync(String path) {
return Uni.createFrom().item(true);
}
}Modify responses after endpoint execution using method-level filter annotations with automatic parameter injection.
package org.jboss.resteasy.reactive.server;
/**
* Annotation for simplified response filtering.
* When applied to a method, generates a ContainerResponseFilter implementation.
*
* Supported method parameters (any order):
* - ContainerRequestContext
* - ContainerResponseContext
* - ResourceInfo
* - UriInfo
* - SimpleResourceInfo
* - Throwable (the thrown exception, or null if no exception)
*
* Supported return types:
* - void: Synchronous filtering
* - Uni<Void>: Reactive filtering
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface ServerResponseFilter {
/** Priority for filter execution (default: Priorities.USER) */
int priority() default Priorities.USER;
}Usage Examples:
import org.jboss.resteasy.reactive.server.ServerResponseFilter;
import jakarta.ws.rs.container.ContainerResponseContext;
import jakarta.ws.rs.core.UriInfo;
public class ResponseFilters {
// Add headers to all responses
@ServerResponseFilter
public void addHeaders(ContainerResponseContext response) {
response.getHeaders().add("X-Custom-Header", "MyValue");
response.getHeaders().add("X-Powered-By", "Quarkus REST");
}
// Conditional header based on path
@ServerResponseFilter
public void addCorsHeaders(UriInfo uriInfo, ContainerResponseContext response) {
if (uriInfo.getPath().startsWith("/api/")) {
response.getHeaders().add("Access-Control-Allow-Origin", "*");
}
}
// Handle exceptions in response filter
@ServerResponseFilter
public void logErrors(Throwable throwable, ContainerResponseContext response) {
if (throwable != null) {
System.err.println("Request failed with: " + throwable.getMessage());
response.getHeaders().add("X-Error-Logged", "true");
}
}
// Reactive response filtering
@ServerResponseFilter
public Uni<Void> asyncLogging(ContainerResponseContext response) {
return logResponseAsync(response.getStatus())
.replaceWithVoid();
}
private Uni<Void> logResponseAsync(int status) {
return Uni.createFrom().voidItem();
}
}Map exceptions to HTTP responses using simplified annotation-based approach with support for both global and resource-local exception handling.
package org.jboss.resteasy.reactive.server;
/**
* Annotation for simplified exception mapping.
* When applied to a method, generates an ExceptionMapper implementation.
*
* Can be used:
* - On non-Resource class methods: Global exception handling
* - On Resource class methods: Resource-local exception handling (takes precedence)
*
* Supported method parameters (exception type required, others optional in any order):
* - The exception type being handled (required)
* - ContainerRequestContext
* - UriInfo
* - HttpHeaders
* - Request
* - ResourceInfo
* - SimpleResourceInfo
*
* Supported return types:
* - Response
* - Uni<Response>
* - RestResponse
* - Uni<RestResponse>
*
* Exception type inference:
* - If value() not set, exception type is inferred from method parameter
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface ServerExceptionMapper {
/** Exception type(s) to handle (optional if deducible from parameters) */
Class<? extends Throwable>[] value() default {};
/** Priority for exception mapper execution (default: Priorities.USER) */
int priority() default Priorities.USER;
}Usage Examples:
import org.jboss.resteasy.reactive.server.ServerExceptionMapper;
import org.jboss.resteasy.reactive.RestResponse;
import jakarta.ws.rs.core.Response;
// Global exception mappers
public class GlobalExceptionMappers {
// Inferred exception type from parameter
@ServerExceptionMapper
public Response mapIllegalArgument(IllegalArgumentException ex) {
return Response.status(400)
.entity("Invalid input: " + ex.getMessage())
.build();
}
// Explicit exception type
@ServerExceptionMapper(value = NullPointerException.class, priority = 100)
public RestResponse<String> mapNullPointer(NullPointerException ex) {
return RestResponse.status(500, "Null value encountered");
}
// Multiple exception types
@ServerExceptionMapper({IOException.class, TimeoutException.class})
public Response mapIOErrors(Exception ex) {
return Response.status(503)
.entity("Service temporarily unavailable")
.build();
}
// With additional context parameters
@ServerExceptionMapper
public Response mapValidation(ValidationException ex, UriInfo uriInfo) {
return Response.status(400)
.entity("Validation failed for " + uriInfo.getPath() + ": " + ex.getMessage())
.build();
}
// Reactive exception mapping
@ServerExceptionMapper
public Uni<RestResponse<ErrorDetails>> mapAsync(CustomException ex) {
return logErrorAsync(ex)
.map(logged -> RestResponse.status(500, new ErrorDetails(ex)));
}
private Uni<Boolean> logErrorAsync(Exception ex) {
return Uni.createFrom().item(true);
}
}
// Resource-local exception mapper (takes precedence for this resource)
@Path("/users")
public class UserResource {
@GET
@Path("/{id}")
public User getUser(@PathParam("id") long id) {
if (id < 0) {
throw new IllegalArgumentException("Invalid user ID");
}
return findUser(id);
}
// Only handles exceptions from this resource
@ServerExceptionMapper
public Response handleLocalException(IllegalArgumentException ex) {
return Response.status(400)
.entity("Invalid user ID format")
.build();
}
private User findUser(long id) {
return new User();
}
}
class ValidationException extends Exception {
public ValidationException(String message) { super(message); }
}
class CustomException extends Exception {
public CustomException(String message) { super(message); }
}
class ErrorDetails {
public ErrorDetails(Exception ex) {}
}
class User {}Additional annotations for controlling filter behavior and form handling.
package org.jboss.resteasy.reactive.server;
/**
* Forces form body reading and parsing before filters/endpoints execute.
* Use when filters need access to form data.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
@interface WithFormRead {}
/**
* Unwraps exception before passing to exception mapper.
* Useful for handling wrapped exceptions like CompletionException.
* Applied at class level (exception class or filter/mapper class).
*
* When an exception of the configured type is thrown and no ExceptionMapper exists,
* RESTEasy Reactive attempts to locate an ExceptionMapper for the cause.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface UnwrapException {
/** Exception types to unwrap (if not set, uses the annotated exception class) */
Class<? extends Exception>[] value() default {};
}
/**
* Lightweight alternative to ResourceInfo for injecting into filters.
* Provides resource class, method name, and parameter types without reflective lookup.
* Can be injected anywhere ResourceInfo can be injected.
*/
interface SimpleResourceInfo {
/** Get the resource class that is the target of a request */
Class<?> getResourceClass();
/** Get the name of the resource method that is the target of a request */
String getMethodName();
/** Get the parameter types of the resource method that is the target of a request */
Class<?>[] parameterTypes();
}Usage Examples:
import org.jboss.resteasy.reactive.server.WithFormRead;
import org.jboss.resteasy.reactive.server.UnwrapException;
import org.jboss.resteasy.reactive.server.ServerRequestFilter;
import org.jboss.resteasy.reactive.server.ServerExceptionMapper;
import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.MultivaluedMap;
import java.util.concurrent.CompletionException;
public class AdvancedFilters {
// Force form reading before filter - method level
@ServerRequestFilter
@WithFormRead
public Response validateFormData(ContainerRequestContext context) {
// Form data is already parsed and available
MultivaluedMap<String, String> formParams = context.getUriInfo()
.getQueryParameters(); // Form data accessible
return null;
}
// Class-level WithFormRead applies to all filter methods
@WithFormRead
public class FormValidationFilters {
@ServerRequestFilter
public Response checkFormSize(ContainerRequestContext context) {
// Form is already read for all methods in this class
return null;
}
@ServerRequestFilter
public Response validateRequiredFields(ContainerRequestContext context) {
// Form data available here too
return null;
}
}
// Unwrap CompletionException to access root cause
@UnwrapException(CompletionException.class)
public class MyExceptionMappers {
@ServerExceptionMapper
public Response handleWrapped(RuntimeException ex) {
// ex is the unwrapped root cause, not CompletionException
return Response.status(500)
.entity("Error: " + ex.getMessage())
.build();
}
}
// Unwrap multiple exception types
@ServerExceptionMapper
@UnwrapException({CompletionException.class, ExecutionException.class})
public Response handleMultipleWrapped(RuntimeException ex) {
// Unwraps either CompletionException or ExecutionException
return Response.status(500)
.entity("Unwrapped error: " + ex.getClass().getSimpleName())
.build();
}
// Without UnwrapException - handles wrapper
@ServerExceptionMapper
public Response handleCompletionException(CompletionException ex) {
// Must manually unwrap
Throwable cause = ex.getCause();
return Response.status(500)
.entity("Wrapped error: " + cause.getMessage())
.build();
}
// Applied to resource endpoint
@POST
@Path("/submit")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@WithFormRead // Ensures form is read before any filters
public Response submitForm(@FormParam("data") String data) {
return Response.ok("Received: " + data).build();
}
}All filter classes support CDI injection. Constructor injection is recommended for injecting beans into filters.
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
@ApplicationScoped
public class SecurityFilter {
private final AuthService authService;
private final AuditLogger auditLogger;
// CDI constructor injection
@Inject
public SecurityFilter(AuthService authService, AuditLogger auditLogger) {
this.authService = authService;
this.auditLogger = auditLogger;
}
@ServerRequestFilter(priority = 1000)
public Uni<RestResponse<String>> authenticate(HttpHeaders headers) {
String token = headers.getHeaderString("Authorization");
return authService.validateToken(token)
.onItem().transformToUni(valid -> {
if (!valid) {
return Uni.createFrom().item(
RestResponse.status(401, "Invalid token")
);
}
return Uni.createFrom().nullItem();
})
.onItem().invoke(() -> auditLogger.logAccess(token));
}
}
interface AuthService {
Uni<Boolean> validateToken(String token);
}
interface AuditLogger {
void logAccess(String token);
}Filters execute based on priority values (lower values execute first for request filters, higher values execute first for response filters):
Priorities.AUTHENTICATION (1000): Authentication filtersPriorities.AUTHORIZATION (2000): Authorization filtersPriorities.HEADER_DECORATOR (3000): Header manipulationPriorities.ENTITY_CODER (4000): Content encoding/decodingPriorities.USER (5000): Default user filterspublic class PriorityExample {
@ServerRequestFilter(priority = Priorities.AUTHENTICATION)
public Response authenticate(HttpHeaders headers) {
// Runs first (priority 1000)
return null;
}
@ServerRequestFilter(priority = Priorities.AUTHORIZATION)
public Response authorize(HttpHeaders headers) {
// Runs second (priority 2000)
return null;
}
@ServerRequestFilter(priority = Priorities.USER)
public void log(UriInfo uriInfo) {
// Runs later (priority 5000)
}
}