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>
This commit is contained in:
Zakaria El Orche
2026-09-23 14:41:56 +00:00
co-authored by Claude Opus 5
parent 2f06ca7c1d
commit 60dd4eab6a
6 changed files with 82 additions and 19 deletions
@@ -0,0 +1,26 @@
package dev.relism.flash.routing;
import dev.relism.flash.http.ContentType;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* The media type a handler answers in.
*
* <p>Independent of {@link Consumes}: what a request carries says nothing about what the answer
* is written as, and a handler that takes JSON may well answer something else. Declare it on the
* handler, or once on a base class every handler of that kind extends.
*
* <p>Descriptive, like {@link Consumes}: tooling reads it, the router does not. What actually
* serializes a response is the middleware the route runs under.
*/
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Produces {
ContentType value();
}