EzyHTTP Annotations
Updated at 1783438159000EzyHTTP uses annotations to declare controllers, request mappings, request arguments, converters, interceptors, exception handlers, and a few startup/runtime extension points.
This article summarizes the annotations currently provided by EzyHTTP and explains how they are used at runtime.
| Annotation | Target | Description |
|---|---|---|
Api | Class, Method | Marks mapped URIs as API URIs so they can be recognized by the request URI manager. |
ApplicationBootstrap | Class | Marks the application entry used to initialize and start the server runtime. |
Async | Method | Marks a request handler as asynchronous. |
Authenticated | Class, Method | Marks mapped URIs that require authentication. |
Authenticatable | Class, Method | Marks mapped URIs that can be handled in an authentication-aware way without necessarily requiring authentication. |
BodyConvert | Class | Registers a request/response body converter for a content type. |
ComponentClasses | Class | Adds explicit component classes to the application context. |
ComponentsScan | Class | Adds packages to scan for EzyHTTP/EzyFox components. |
Controller | Class | Declares a request controller and optionally sets its root URI. |
Description | Class, Method, Field, Parameter, Constructor | Provides descriptive metadata that tools can read at runtime. |
DoDelete | Method | Maps a method to an HTTP DELETE URI. |
DoGet | Method | Maps a method to an HTTP GET URI; also supports alternate GET URIs. |
DoPost | Method | Maps a method to an HTTP POST URI. |
DoPut | Method | Maps a method to an HTTP PUT URI. |
ExceptionHandler | Class | Registers a global exception handler class. |
EzyConfigurationAfterApplicationReady | Class | Runs configuration logic after the application has started. |
GraphQLQuery | Class, Method | Names a GraphQL query data fetcher or query method in the GraphQL module. |
Interceptor | Class | Registers a request interceptor and optionally sets its priority. |
PathVariable | Parameter | Binds a URI path variable to a Java method parameter. |
PropertiesSources | Class | Adds property files to the application context. |
RequestArgument | Parameter | Binds a custom request argument to a Java method parameter. |
RequestBody | Parameter | Binds and deserializes the request body. |
RequestCookie | Parameter | Binds a cookie value to a Java method parameter. |
RequestHeader | Parameter | Binds a request header to a Java method parameter. |
RequestParam | Parameter | Binds a query/form parameter to a Java method parameter. |
Service | Class | Registers a service singleton in the bean context. |
StringConvert | Class | Registers string conversion logic used by path variables, params, headers, and cookies. |
TryCatch | Method | Maps one or more exception classes to a handler method. |
Request mapping annotations
The core request mapping model is built from
@Controller on a class and one of @DoGet, @DoPost, @DoPut, or @DoDelete on public methods.@Controller("/api/v1/books") public class BookController { @DoGet("/{bookId}") public BookResponse getBook(@PathVariable("bookId") long bookId) { // implementation } @DoPost(value = "/add", responseType = "application/json") public BookResponse addBook(@RequestBody AddBookRequest request) { // implementation } }
@Controller accepts either value or uri. If both are empty, the default root URI is /. If a URI does not start with /, EzyHTTP normalizes it by adding /.The HTTP method annotations also accept either
value or uri. If both are empty, the method is mapped to the controller root URI.@Controller("/health") public class HealthController { @DoGet public String health() { return "OK"; } }
The example above maps
GET /health.@DoGet additionally supports otherUris, which creates extra GET mappings for the same handler:@DoGet(value = "/profile", otherUris = {"/me", "/account"}) public UserProfile getProfile() { // implementation }
All four HTTP method annotations support:
-
accept: the accepted request body content types. -
responseType: the response body content type. If it is empty, EzyHTTP usesapplication/json.
API, authentication and authenticatable URIs
@Api, @Authenticated, and @Authenticatable can be placed on a controller class or on an individual handler method.When placed on a controller, the flag applies to all request handler methods in that controller. When placed on a method, it applies only to that method.
@Api @Authenticated @Controller("/api/v1/users") public class UserController { @DoGet("/{userId}") public UserResponse getUser(@PathVariable long userId) { // implementation } }
For every mapped URI, EzyHTTP stores metadata in the request URI manager. That makes it possible for interceptors to check whether a matched URI is an API URI, authenticated URI, or authenticatable URI.
@Interceptor public class AuthenticationInterceptor implements RequestInterceptor { private final RequestURIManager requestURIManager; @Override public boolean preHandle(RequestArguments arguments, Method handler) { boolean required = requestURIManager.isAuthenticatedURI( arguments.getMethod(), arguments.getUriTemplate() ); if (!required) { return true; } // verify authentication return true; } }
EzyHTTP also registers the slash and non-slash variants of flagged URIs. For example, a mapped API URI
/api/v1/users is recognized together with /api/v1/users/.@Authenticated means the URI requires authentication. @Authenticatable is a softer marker: it lets middleware recognize that the URI supports authentication-related handling, but the annotation itself does not force a request to be rejected. The actual decision still belongs to your interceptor or controller logic.Request argument annotations
EzyHTTP can bind different parts of a request into handler method parameters:
@DoGet("/{bookId}") public BookResponse getBook( @PathVariable("bookId") long bookId, @RequestParam(value = "includeAuthor", defaultValue = "false") boolean includeAuthor, @RequestHeader(value = "X-Request-Id", defaultValue = "") String requestId, @RequestCookie(value = "visitorId", defaultValue = "") String visitorId ) { // implementation }
The request argument annotations behave as follows:
-
@PathVariable: reads a variable from the URI template, such as{bookId}. -
@RequestParam: reads a query or form parameter. -
@RequestHeader: reads a request header. -
@RequestCookie: reads a cookie value. -
@RequestBody: reads the request body and deserializes it by content type. -
@RequestArgument: reads a custom argument fromRequestArguments.
@RequestParam, @RequestHeader, and @RequestCookie support value, name, and defaultValue. EzyHTTP resolves the key from value first, then name, then the parameter index. The built-in default value is the literal string "null".
@PathVariable supports value. If it is empty, EzyHTTP falls back to the parameter index.@DoPost("/orders") public OrderResponse createOrder(@RequestBody CreateOrderRequest request) { // implementation }
For
@RequestBody, the request must provide a content type. EzyHTTP selects a registered body deserializer for that content type. If deserialization fails, EzyHTTP raises a body deserialization exception that can be handled by @TryCatch or a global exception handler.Converters
@BodyConvert registers a body converter for a content type. Body converters are used to serialize response bodies and deserialize request bodies.@BodyConvert("application/xml") public class XmlBodyConverter implements BodyConverter { @Override public byte[] serialize(Object body) throws IOException { // convert object to XML bytes } @Override public <T> T deserialize(BodyData bodyData, Class<T> bodyType) throws IOException { // convert XML body to Java object } }
EzyHTTP already registers common converters for JSON, form data, plain text, HTML text, and multipart form data. Use
@BodyConvert when your application needs another content type or custom serialization behavior.@StringConvert registers string conversion logic. It is used when converting string values from path variables, request parameters, headers, and cookies into Java types.@StringConvert public class CustomStringConverter extends DefaultStringDeserializer { public CustomStringConverter() { mappers.put(List.class, value -> Arrays.asList(value.split(","))); } }
Components, services and properties
EzyHTTP builds its application context by scanning packages and registering singleton components.
@ComponentsScan adds packages to scan. If its value is empty, EzyHTTP scans the package of the annotated class.@ComponentsScan({ "com.example.app", "com.example.shared" }) public class ApplicationConfig {}
@ComponentClasses adds explicit classes to the application context:@ComponentClasses({ MailService.class, AuditService.class }) public class ApplicationConfig {}
@PropertiesSources adds property files:@PropertiesSources({ "application.properties", "application.yaml" }) public class ApplicationConfig {}
@Service registers a service singleton. It supports both value and name for naming the service; if no name is supplied, the standard bean naming rule is used.@Service("bookService") public class BookService { // business logic }
Startup annotations
@ApplicationBootstrap marks the application entry that EzyHTTP uses to initialize and start the underlying server runtime.@ApplicationBootstrap public class MyApplicationBootstrap implements ApplicationEntry { @Override public void init() { // prepare runtime } @Override public void start() { // start server } }
After the application starts, EzyHTTP looks for singleton objects annotated with
@EzyConfigurationAfterApplicationReady. If the object implements EzyBeanConfig, its config() method is called.@EzyConfigurationAfterApplicationReady(priority = 10) public class CacheWarmupConfig implements EzyBeanConfig { @Override public void config() { // run after application ready } }
Objects are sorted by
priority in ascending order before they run.Interceptors
@Interceptor registers a request interceptor. Interceptors can run logic before and after a request handler.@Interceptor(priority = 100) public class LoggingInterceptor implements RequestInterceptor { @Override public boolean preHandle(RequestArguments arguments, Method handler) { return true; } @Override public void postHandle(RequestArguments arguments, Method handler) { // after handler } }
Interceptors are sorted by
priority in ascending order. Returning false from preHandle stops the request from continuing to the handler.Async handlers
@Async marks a handler method that works with servlet async processing.@Async @DoGet("/files/{fileName}") public void download( RequestArguments arguments, @PathVariable("fileName") String fileName ) throws Exception { // start async processing and write response later }
This is useful for long-running operations such as upload, download, or streaming. When an exception happens after async processing has started, EzyHTTP completes the async context during exception handling.
Exception handling
There are two common patterns for exception handling.
Inside a controller, you can add methods annotated with
@TryCatch:@Controller("/books") public class BookController { @DoGet("/{bookId}") public BookResponse getBook(@PathVariable long bookId) { // implementation } @TryCatch({BookNotFoundException.class}) public ErrorResponse handleBookNotFound(BookNotFoundException e) { return new ErrorResponse("book_not_found"); } }
For global exception handling, create a class annotated with
@ExceptionHandler and add public methods annotated with @TryCatch:@ExceptionHandler public class GlobalExceptionHandler { @TryCatch({IllegalArgumentException.class, HttpBadRequestException.class}) public ErrorResponse handleBadRequest(Exception e) { return new ErrorResponse("bad_request"); } }
@TryCatch accepts one or more exception classes. EzyHTTP maps those exception classes to the annotated method. The response type for exception handlers is JSON by default.GraphQLQuery
@GraphQLQuery belongs to the EzyHTTP GraphQL module. It can be used to name a GraphQL data fetcher or query method.@GraphQLQuery(value = "book", group = "library") public class BookGraphQLFetcher implements GraphQLDataFetcher { @Override public Object getData( RequestArguments arguments, GraphQLQueryDefinition query ) { // fetch GraphQL data } }
The annotation supports:
-
value: query name. -
name: query name, used beforevaluewhen present. -
group: optional query group name.
If a GraphQL data fetcher does not implement
getQueryName(), EzyHTTP reads the query name from @GraphQLQuery. If neither is available, the fetcher cannot provide a query name.
Description
@Description is a general metadata annotation. It can be placed on classes, methods, fields, parameters, and constructors.@Description("Request body used to create a book") public class CreateBookRequest { @Description("Book title") private String title; }
It supports:
-
value: description text. -
contentType: optional content type for the description.
This annotation is most useful for tooling and generated documentation.
Quick usage guide
Use the annotations by responsibility:
- Routing:
@Controller,@DoGet,@DoPost,@DoPut,@DoDelete. - URI metadata:
@Api,@Authenticated,@Authenticatable. - Parameters and body:
@PathVariable,@RequestParam,@RequestHeader,@RequestCookie,@RequestBody,@RequestArgument. - Conversion:
@BodyConvert,@StringConvert. - Application context:
@ComponentsScan,@ComponentClasses,@PropertiesSources,@Service. - Runtime hooks:
@ApplicationBootstrap,@EzyConfigurationAfterApplicationReady,@Interceptor,@Async. - Errors:
@ExceptionHandler,@TryCatch. - Optional GraphQL module:
@GraphQLQuery. - Documentation metadata:
@Description.
Next Step
You can also read: