EzyHTTP Response Types
Updated at 1783351550000EzyHTTP lets a controller return several kinds of values. At runtime, the framework inspects the returned value and converts it to an HTTP response: status code, headers, cookies, body, redirect, or rendered HTML.
The most common response types are:
| Response Type | Best for | What EzyHTTP does |
|---|---|---|
ResponseEntity | REST API responses that need explicit status, headers, content type, or body | Uses the status and headers from the entity, then serializes the body when it is not null |
| Plain object | Simple successful REST API responses | Sets status 200 OK and serializes the object as the response body |
View | Server-side rendered HTML pages | Adds headers and cookies, sets the view content type, then renders the template through the configured view engine |
Redirect | Post/Redirect/Get flows, login flows, form submissions, and page transitions | Adds headers, cookies, optional redirect attributes, query parameters, then calls HTTP redirect |
ResponseAsync | Handlers that write the response asynchronously | Marks the response as already being handled by async code, so the normal synchronous response conversion is skipped |
How EzyHTTP Chooses the Response Behavior
After a controller method finishes, EzyHTTP checks the returned value in this order:
flowchart TD
A[Controller method returns a value] --> B{Return value is null?}
B -- Yes --> C[Set status 200 OK]
B -- No --> D{ResponseEntity?}
D -- Yes --> E[Apply status and headers, serialize body if present]
D -- No --> F{Redirect?}
F -- Yes --> G[Add cookies, headers, attributes, query parameters, then send redirect]
F -- No --> H{View?}
H -- Yes --> I[Add cookies and headers, set content type, render template]
H -- No --> J[Set status 200 OK, serialize object body]
This means a plain object is the simplest successful response, while
ResponseEntity, View, and Redirect are more explicit response instructions.ResponseEntity
Use
ResponseEntity when the controller must control HTTP-level details directly.It can carry:
| Part | Meaning |
|---|---|
status | The HTTP status code, for example 200, 201, 204, 400, 404, or 409 |
headers | One or more response headers. The builder supports adding multiple values for the same header name. |
body | The response payload. If the body is null, EzyHTTP only writes status and headers. |
Example: return data when found, otherwise return
404 Not Found.@DoGet("/books/{bookId}") public ResponseEntity getBook(@PathVariable Long bookId) { BookData book = bookService.getBook(bookId); if (book == null) { return ResponseEntity.notFound( Collections.singletonMap("book", "notFound") ); } return ResponseEntity.ok(book); }
Example: return
409 Conflict for duplicate data and 204 No Content after a successful command.@DoPost("/books") public ResponseEntity addBook(@RequestBody AddBookRequest request) { bookValidator.validate(request); BookData currentBook = bookService.getBook(request.getBookName()); if (currentBook != null) { return ResponseEntity .status(StatusCodes.CONFLICT) .body(Collections.singletonMap("book", "existed")); } bookService.addBook(requestToDataConverter.toData(request)); return ResponseEntity.noContent(); }
Example: return plain text instead of the default JSON-style API body.
@DoGet("/health") public ResponseEntity health() { return ResponseEntity .status(StatusCodes.OK) .textPlain("OK") .build(); }
You can also add custom headers:
@DoGet("/reports/{reportId}") public ResponseEntity getReport(@PathVariable long reportId) { ReportData report = reportService.getReport(reportId); return ResponseEntity .status(StatusCodes.OK) .header("X-Report-Id", String.valueOf(reportId)) .body(report) .build(); }
Plain Object
If a controller returns a regular object, EzyHTTP treats it as a successful response body.
@DoGet("/books/{bookId}") public BookResponse getBook(@PathVariable Long bookId) { BookData book = bookService.getBook(bookId); return dataToResponseConverter.toResponse(book); }
This is equivalent to saying:
status = 200 OK body = returned object
The body is serialized by the response body serializer selected from the response content type. In a typical REST API controller, this is JSON. This style is clean when the endpoint normally succeeds and exceptional cases are handled by a global exception handler.
For example, instead of returning
ResponseEntity.notFound(...) everywhere, a service can throw an exception and a global handler can convert it to a consistent API error response.@DoGet("/books/{bookId}") public BookResponse getBook(@PathVariable Long bookId) { BookData book = bookService.getBookOrThrow(bookId); return dataToResponseConverter.toResponse(book); }
ResponseEntity vs Plain Object
Both styles are useful, but they express different intent.
| Use case | Recommended return type | Reason |
|---|---|---|
| Simple successful API response | Plain object | Less code, clear business return value |
| Need non-200 status | ResponseEntity | The status code is part of the endpoint contract |
| Need custom headers | ResponseEntity | Headers are explicit on the response object |
| Command endpoint with no response body | ResponseEntity.noContent() | Returns 204 No Content intentionally |
| Centralized error handling | Plain object plus exception handler | Keeps controller code focused on the happy path |
View
Use
View when a controller returns a server-side rendered page.@DoGet("/user/update") public View userUpdateGet(@UserId long userId) { User user = userService.getUserById(userId); return View.builder() .template("user-update") .addVariable("user", user) .addVariable("accountType", user.getAccountType().toString()) .build(); }
A
View contains:| Part | Meaning |
|---|---|
template | The template name to render. It is required. |
variables | Values exposed to the template engine. |
locale | The locale used while rendering. The default is English. |
contentType | The response content type. The default is text/html; charset=utf-8. |
headers and cookies | Extra response headers and cookies added before the template is rendered. |
Example with locale, header, and cookie:
@DoGet("/profile") public View profile(@UserId long userId) { ProfileData profile = profileService.getProfile(userId); return View.builder() .template("profile") .locale("vi") .addVariable("profile", profile) .addHeader("X-Page", "profile") .addCookie("lastPage", "profile", "/") .build(); }
To return a
View, the application must have a view context configured. If you use Thymeleaf, add the Thymeleaf integration dependency or provide your own view context implementation.Redirect
Use
Redirect when the response should instruct the browser to navigate to another URI.The short form is:
@DoPost("/logout") public Redirect logout() { return Redirect.to("/login"); }
A redirect can also carry cookies, headers, query parameters, and redirect attributes.
@DoPost("/login") public Redirect login(@RequestBody LoginRequest request) { User user = authenticationService.authenticate( request.getEmail(), request.getPassword() ); return Redirect.builder() .uri("/home") .addCookie("accessToken", user.getAccessToken(), "/") .addParameter("from", "login") .addAttribute("username", user.getUsername()) .build(); }
Query parameters are appended to the redirect URI:
/home?from=login
Redirect attributes are different from query parameters. They are stored temporarily and can be read in the next request through
RequestArguments.@DoGet("/home") public View home(RequestArguments arguments) { String username = arguments.getRedirectionAttribute("username"); return View.builder() .template("home") .addVariable("username", username) .build(); }
This is useful for short-lived values such as flash messages, form status, or the name of the user who just logged in.
View vs Redirect
For page controllers, a practical rule is:
| After this action | Return | Why |
|---|---|---|
Display a page with GET | View | The browser should render the template for the current URL. |
Handle a successful POST, PUT, or DELETE | Redirect | A redirect avoids duplicate form submission when the user refreshes the page. |
| Handle validation error and show the same form again | View or Redirect | Use View for immediate rendering, or Redirect with attributes for a Post/Redirect/Get flow. |
ResponseAsync
ResponseAsync is a special marker for asynchronous handling.You normally do not use it for ordinary REST endpoints. It is useful when the request handler starts async processing and writes to the
HttpServletResponse later. In that case, the normal synchronous conversion step should not try to serialize another body.EzyHTTP exposes this marker through:
ResponseEntity.ASYNC
The idea is:
sequenceDiagram
participant C as Client
participant S as EzyHTTP Servlet
participant H as Handler
participant A as Async work
C->>S: HTTP request
S->>H: Invoke handler
H->>S: Return ResponseEntity.ASYNC
H->>A: Continue work asynchronously
A->>C: Write response later
In normal application controllers, prefer
ResponseEntity, a plain object, View, or Redirect. Reach for async only when you are intentionally managing the response lifecycle yourself.Error Responses
Response types also apply to exception handlers.
An exception handler can return a plain object,
ResponseEntity, View, or another supported response value. For API errors, ResponseEntity is usually the clearest choice because it can carry the error status and body together.@ExceptionHandler(BookNotFoundException.class) public ResponseEntity handleBookNotFound(BookNotFoundException e) { return ResponseEntity .status(StatusCodes.NOT_FOUND) .body(Collections.singletonMap("book", "notFound")); }
If an exception handler returns a plain object, the response status depends on the response handling around that exception. When the status matters, prefer returning
ResponseEntity explicitly.Content Type and Serialization
For body responses, EzyHTTP chooses a body serializer from the current response content type.
That content type can come from:
| Source | Example |
|---|---|
| Controller response metadata | An API endpoint configured to return JSON |
ResponseEntity header | .contentType(ContentTypes.TEXT_PLAIN) |
View content type | Default text/html; charset=utf-8 |
If you return a plain object from an API endpoint, make sure the endpoint has the expected response content type so the correct serializer is used.
Choosing the Right Return Type
flowchart TD
A[What should the endpoint do?] --> B{Render HTML?}
B -- Yes --> C[Return View]
B -- No --> D{Navigate browser to another URL?}
D -- Yes --> E[Return Redirect]
D -- No --> F{Need custom status or headers?}
F -- Yes --> G[Return ResponseEntity]
F -- No --> H{Managing response asynchronously?}
H -- Yes --> I[Return ResponseEntity.ASYNC]
H -- No --> J[Return a plain object]
In short:
| Return type | Use it when... |
|---|---|
ResponseEntity | You want precise control over status, headers, content type, and body. |
| Plain object | You want a simple 200 OK API response. |
View | You want to render a template. |
Redirect | You want the browser to request another URL. |
ResponseEntity.ASYNC | You intentionally handle the servlet response asynchronously. |
Next
You can also read EzyHTTP request arguments to see how request data is mapped into controller method parameters.