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.0.2
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 currently a global mapping switch.
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: v2
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 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.