Files
Zakaria El OrcheandClaude Opus 5 60dd4eab6a fix(ext-openapi): an answer's media type is the handler's, not the body's
@Consumes says what a route reads. It was also deciding what the document said
a route answers, through a JSON default nothing could override: a handler that
takes a JSON body is not thereby a handler that answers JSON.

@Produces now says that, beside @Consumes and as descriptive as it is — on the
handler, or once on a base class. Every response takes its media type from it,
JSON when nothing declares one, and the error object stays JSON because that is
what Flash answers a failure with whatever the route produces.

Content loses contentType with it. One handler answers in one format and a
status code does not change that, so the media type was in the wrong place; a
response with no schema and no return type to infer one from is a response
with no body, which is what a 204 was using it to say.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 14:41:56 +00:00
..

flash-ext-jackson-json

JSON bodies and JSON responses.

Install

JsonExtension json = new JsonExtension();

FlashApp.create(8080)
    .install(json)
    .use(json.auto())
    .scan("com.acme.handlers")
    .startAndBlock();

The default mapper discovers the modules on the classpath (Java Time among them) and writes dates as ISO strings; new JsonExtension(mapper) takes one of your own. auto() serializes whatever a handler returns, leaving alone what is already a response: null, a Response, a byte[] or a CharSequence.

A handler with a body

The body type is the handler's type argument, and that is the whole declaration — it is also what the OpenAPI document describes and what the constraints are read from:

@POST("/users")
public final class CreateUser extends JsonHandler<NewUser> {
    @Inject private UserService users;

    @Override protected Object handle(Request req, Response res, NewUser body) {
        return users.create(body);
    }
}

A malformed body never reaches it (400), nor does one that breaks a constraint (422).

A handler that reads it itself

public final class Import extends RequestHandler {
    @Inject private Json json;

    @Override public Object handle(Request req, Response res) throws Exception {
        return archive.store(json.body(req, Manifest.class));
    }
}

Json is the Codec for application/json: body, write, writeView, mapper.

Notes

  • One mapper per application (or per scope), shared by every handler; ObjectMapper is thread-safe once configured.
  • Install flash-ext-jackson-xml beside this one when an application speaks both: a route picks its format by the handler it extends, not by negotiation.