Endpoint content types
A simple path of the OpenAPI description will usually produce a single endpoint method in the target interface as described in the introduction.
OpenAPI allows us to define more complex apis that behave differently based on the request header. For example the following api definition can return its response in different formats. As json or as plain text:
openapi: 3.1.0
info:
title: multiple response content types
version: 1.0.0
paths:
/foo:
get:
responses:
'200':
description: json or plain text result
content:
application/json:
schema:
$ref: '#/components/schemas/Foo'
text/plain:
schema:
type: string
default:
description: error
content:
application/xml:
schema:
$ref: '#/components/schemas/Error'
components:
schemas:
Foo:
type: object
properties:
bar:
type: string
Error:
type: object
properties:
error:
type: string
A client request uses the request Accept
header to tell the api which result content types it can handle.
Using a single endpoint method it has to decide which response to create.This leads to some boring if/else
code. To avoid this the processor creates one endpoint method for each possible response.
multiple content types
For the example above (ignoring the default
xml response) it creates the following interface:
package generated.api;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
public interface Api {
@GetMapping( path = "/foo", produces = {"application/json"})
Foo getFooApplicationJson();
@GetMapping( path = "/foo", produces = {"text/plain"})
String getFooTextPlain();
}
The apis normal response (status '200') can return the result as json or as plain text which leads to two methods for the same endpoint but with a different produces
list in the mapping annotation.
One method when json gets requested and one when plain text gets requested. Spring will take care of selecting the correct endpoint.
multiple content types & default content type
In the (contrived) example our api does also define another content type for all other result status codes (usually the errors): xml.
If an endpoint returns multiple types, a success response (typically 200 ok) and at least one error response, the processor has to pick a return type for the endpoint methods.
With versions before 2021.5 the processor generates (by default) endpoint methods with an Object
return value (or if generic something like ResponseType<?>
) to handle the unrelated success and error response types.
This has the drawback that an important piece of information is missing: the success response type. With Object
as return type it not clear what the success response type is.
With version 2021.5 it is possible to generate the endpoints with the success response type even with error responses. It is ignoring the error result types.
Since it is common practice to handle errors by throwing exceptions (e.g. in combination with the Spring @ResponseStatus
annotation) the endpoint methods don’t need to handle different return types, and it is possible to simply use the type of the success response.
To switch between previous and new behavior there is a new mapping configuration to control the style of the return type named result-style
. It has two possible values: success
or all
. This is available on all mapping levels new with 2025.2.
The default is success
, i.e. the processor will automatically generate the code using the new behavior. In case the previous behavior is required, set the result-style
to all
.
openapi-processor-mapping: v12
options:
package-name: ...
map:
#result-style: success # use the success result type, this is the default
result-style: all # use an Object return type
new behavior, since 2021.5
Example of the new code, using result-style: success
. This is the default if result-style
is not set.
package generated.api;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
public interface Api {
@GetMapping( path = "/foo", produces = {"application/json", "application/xml"})
Foo getFooApplicationJson();
@GetMapping( path = "/foo", produces = {"text/plain", "application/xml"})
String getFooTextPlain();
}
previous behavior, before 2021.5
Example of the previous code, using result-style: all
. The setting is required to generate the previous code.
package generated.api;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
public interface Api {
@GetMapping( path = "/foo", produces = {"application/json", "application/xml"})
Object getFooApplicationJson();
@GetMapping( path = "/foo", produces = {"text/plain", "application/xml"})
Object getFooTextPlain();
}
multiple responses with same content type
new with 2025.2
There is a case that was not properly handled before: if an endpoint of the OpenAPI description has multiple success responses with the same content type (e.g. application/json
) but different response payloads.
The processor did honor only one response status using the last success response.
Here is an example OpenAPI:
openapi: 3.1.0
info:
title: test multiple success responses
version: 1.0.0
paths:
/foo:
get:
description: endpoint with multiple success responses
responses:
'200':
description: success
content:
application/json:
schema:
$ref: '#/components/schemas/Foo'
'202':
description: another success
content:
application/json:
schema:
$ref: '#/components/schemas/Bar'
components:
schemas:
Foo:
type: object
properties:
foo:
type: string
Bar:
type: object
properties:
bar:
type: string
The two result payloads Foo
and Bar
are handled by two possible ways now.
Either by using Object
as return type or by generating a marker interface that’s implemented by both payload and using it as the result type.
using Object
as the result type
This is the default. To return either Foo
or Bar
from the endpoint method it uses Object
as the return type.
package generated.api;
import annotation.Mapping;
import generated.support.Generated;
@Generated(value = "openapi-processor-core", version = "test")
public interface Api {
@GetMapping("/foo")
Object getFooApplicationJson();
}
using a marker interface as result type
An Object
return type is obviously not very descriptive. It is impossible to know from the interface which results are possible.
To improve on that situation the processor can generate a marker interface that is more descriptive and helps with navigation in the IDE.
Generation of the marker interface is enabled by adding the response-interface
option:
openapi-processor-mapping: v12
options:
package-name: ...
# ...
response-interface: true
The generated interface will now look like this:
package generated.api;
import annotation.Mapping;
import generated.model.Foo;
import generated.model.GetFooBarAApplicationJsonResponse;
import generated.model.GetFooBarBApplicationJsonResponse;
import generated.support.Generated;
@Generated(value = "openapi-processor-core", version = "test")
public interface Api {
@Mapping("/foo")
GetFooApplicationJsonResponse getFooApplicationJson(); (1)
}
1 | instead of returning Object it does return the marker interface type GetFooApplicationJsonResponse . |
The marker interface is an empty interface and its name is derived from http method, path and content type to create a unique name.
package generated.model;
import generated.support.Generated;
@Generated(value = "openapi-processor-core", version = "test")
public interface GetFooApplicationJsonResponse {
}
The response payload classes Foo
and Bar
will both implement this interface:
package generated.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import generated.support.Generated;
@Generated(value = "openapi-processor-core", version = "test")
public class Foo implements GetFooApplicationJsonResponse {
@JsonProperty("foo")
private String foo;
// ...
}
If the response type (Foo
in this case) is used on multiple endpoints with multiple success response status it will implement multiple marker interfaces.
In case one of the success responses is a primitive result (like String
) the processor will fall back to using Object
because it can’t hide the primitive type behind a marker interface.
multiple content types, default content type & result wrapper
In case we (globally) enable a result wrapper, e.g. ResponseEntity
in the mapping.yaml
map:
result: org.springframework.http.ResponseEntity
the created code will now look like this:
package generated.api;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
public interface Api {
@GetMapping(
path = "/foo",
produces = {"application/json", "application/xml"})
ResponseEntity<?> getFooApplicationJson();
@GetMapping(
path = "/foo",
produces = {"text/plain", "application/xml"})
ResponseEntity<?> getFooTextPlain();
}
The response wraps the type by a ResponseEntity
and to handle the multiple response types the generic parameter is the unknown type.