The following annotations allow for defining part schemas based on the OpenAPI standard.
- Request annotations:
- {@link oaj.http.annotation.Request}
- {@link oaj.http.annotation.Request#on() on}
- {@link oaj.http.annotation.Request#onClass() onClass}
- {@link oaj.http.annotation.Request#parser() parser}
- {@link oaj.http.annotation.Header}
- {@link oaj.http.annotation.Header#name() name}
- {@link oaj.http.annotation.Header#parser() parser}
- {@link oaj.http.annotation.Header#schema() schema}
- {@link oaj.http.annotation.Header#value() value}
- {@link oaj.http.annotation.Query}
- {@link oaj.http.annotation.Query#name() name}
- {@link oaj.http.annotation.Query#parser() parser}
- {@link oaj.http.annotation.Query#schema() schema}
- {@link oaj.http.annotation.Query#value() value}
- {@link oaj.http.annotation.FormData}
- {@link oaj.http.annotation.FormData#name() name}
- {@link oaj.http.annotation.FormData#parser() parser}
- {@link oaj.http.annotation.FormData#schema() schema}
- {@link oaj.http.annotation.FormData#value() value}
- {@link oaj.http.annotation.Path}
- {@link oaj.http.annotation.Path#name() name}
- {@link oaj.http.annotation.Path#parser() parser}
- {@link oaj.http.annotation.Path#schema() schema}
- {@link oaj.http.annotation.Path#value() value}
- {@link oaj.http.annotation.Content}
- {@link oaj.http.annotation.Content#schema() schema}
- Response annotations:
- {@link oaj.http.annotation.Response}
- {@link oaj.http.annotation.Response#examples() examples}
- {@link oaj.http.annotation.Response#headers() headers}
- {@link oaj.http.annotation.Response#on() on}
- {@link oaj.http.annotation.Response#onClass() onClass}
- {@link oaj.http.annotation.Response#schema() schema}
- {@link oaj.http.annotation.Response#serializer() serializer}
- {@link oaj.http.annotation.Header}
- {@link oaj.http.annotation.Header#name() name}
- {@link oaj.http.annotation.Header#schema() schema}
- {@link oaj.http.annotation.Header#serializer() serializer}
- {@link oaj.http.annotation.Header#value() value}
- {@link oaj.http.annotation.Content}
- {@link oaj.http.annotation.Content#schema() schema}
- {@link oaj.http.annotation.StatusCode}
- {@link oaj.http.annotation.StatusCode#value() value}
- Common schema annotation:
- {@link oaj.annotation.Schema}
- {@link oaj.annotation.Schema#_default() _default}
- {@link oaj.annotation.Schema#_enum() _enum}
- {@link oaj.annotation.Schema#$ref() $ref}
- {@link oaj.annotation.Schema#additionalProperties() additionalProperties}
- {@link oaj.annotation.Schema#aev() aev}
- {@link oaj.annotation.Schema#allOf() allOf}
- {@link oaj.annotation.Schema#allowEmptyValue() allowEmptyValue}
- {@link oaj.annotation.Schema#cf() cf}
- {@link oaj.annotation.Schema#collectionFormat() collectionFormat}
- {@link oaj.annotation.Schema#d() d}
- {@link oaj.annotation.Schema#description() description}
- {@link oaj.annotation.Schema#df() df}
- {@link oaj.annotation.Schema#discriminator() discriminator}
- {@link oaj.annotation.Schema#e() e}
- {@link oaj.annotation.Schema#emax() emax}
- {@link oaj.annotation.Schema#emin() emin}
- {@link oaj.annotation.Schema#exclusiveMaximum() exclusiveMaximum}
- {@link oaj.annotation.Schema#exclusiveMinimum() exclusiveMinimum}
- {@link oaj.annotation.Schema#externalDocs() externalDocs}
- {@link oaj.annotation.Schema#f() f}
- {@link oaj.annotation.Schema#format() format}
- {@link oaj.annotation.Schema#ignore() ignore}
- {@link oaj.annotation.Schema#items() items}
- {@link oaj.annotation.Schema#max() max}
- {@link oaj.annotation.Schema#maxi() maxi}
- {@link oaj.annotation.Schema#maximum() maximum}
- {@link oaj.annotation.Schema#maxItems() maxItems}
- {@link oaj.annotation.Schema#maxl() maxl}
- {@link oaj.annotation.Schema#maxLength() maxLength}
- {@link oaj.annotation.Schema#maxp() maxp}
- {@link oaj.annotation.Schema#maxProperties() maxProperties}
- {@link oaj.annotation.Schema#min() min}
- {@link oaj.annotation.Schema#mini() mini}
- {@link oaj.annotation.Schema#minimum() minimum}
- {@link oaj.annotation.Schema#minItems() minItems}
- {@link oaj.annotation.Schema#minl() minl}
- {@link oaj.annotation.Schema#minLength() minLength}
- {@link oaj.annotation.Schema#minp() minp}
- {@link oaj.annotation.Schema#minProperties() minProperties}
- {@link oaj.annotation.Schema#mo() mo}
- {@link oaj.annotation.Schema#multipleOf() multipleOf}
- {@link oaj.annotation.Schema#on() on}
- {@link oaj.annotation.Schema#onClass() onClass}
- {@link oaj.annotation.Schema#p() p}
- {@link oaj.annotation.Schema#pattern() pattern}
- {@link oaj.annotation.Schema#properties() properties}
- {@link oaj.annotation.Schema#r() r}
- {@link oaj.annotation.Schema#readOnly() readOnly}
- {@link oaj.annotation.Schema#required() required}
- {@link oaj.annotation.Schema#ro() ro}
- {@link oaj.annotation.Schema#sie() sie}
- {@link oaj.annotation.Schema#skipIfEmpty() v}
- {@link oaj.annotation.Schema#t() t}
- {@link oaj.annotation.Schema#title() title}
- {@link oaj.annotation.Schema#type() type}
- {@link oaj.annotation.Schema#ui() ui}
- {@link oaj.annotation.Schema#uniqueItems() uniqueItems}
- {@link oaj.annotation.Schema#xml() xml}
The {@link oaj.http.annotation.Header @Header}/{@link oaj.http.annotation.Query @Query}/
{@link oaj.http.annotation.FormData @FormData}/{@link oaj.http.annotation.Path @Path} annotations
can be used on parameters of {@link oajr.annotation.RestOp @RestOp}-annotated methods to
get access to request headers, query parameters, form-data parameters, and path parts.
The most typical scenario is to simply use the value field to define parameter names:
| @RestGet
| public void doGet(
| @Query("p1") int p1,
| @Query("p2") String p2,
| @Query("p3") UUID p3) {...}
This is functionally equivalent to the following code:
| @RestGet
| public void doGet(RestRequest req) {
| RequestQueryParams query = req.getQueryParams();
| int p1 = query.get("p1").asInteger().orElse(0);
| String p2 = query.get("p2").orElse(null);
| UUID p3 = query.get("p3").as(UUID.class).orElse(null);
| }
The special name "*" (or blank) can be used to represent all values.
When used, the data type must be a Map or bean.
| // Multiple values passed as a map.
| @RestGet
| public void doGet(@Query("*") Map<String,Object> map) {...}
| // Same but name "*" is inferred.
| @RestGet
| public void doGet(@Query Map<String,Object> map) {...}
| // Multiple values passed as a bean.
| @RestGet
| public void doGet(@Query MyQueryBean bean) {...}
The {@link oaj.http.annotation.Content @Content} annotation is used to identify POJOs to be used as the body of an HTTP request.
| // Defined on parameter
| @RestPost
| public void addPet(@Content Pet pet) {...}
| // Defined on POJO class
| @RestPost
| public void addPet(Pet pet) {...}
|
| @Content
| public class Pet {...}
This is functionally equivalent to the following code:
| @RestPost
| public void addPet(RestRequest req) {
| Pet pet = req.getContent().as(Pet.class);
| ...
| }
In addition to {@link oaj.http.annotation.Content @Content}-annotated parameters/types, the body of an HTTP request
can be retrieved by passing in parameters of the following types (matched in the specified order):
-
{@link java.io.Reader}
@Content annotation is optional.
Content-Type is ignored.
-
{@link java.io.InputStream}
@Content annotation is optional.
Content-Type is ignored.
-
Any Parseable POJO type.
Content-Type is required to identify correct parser.
-
Objects convertible from {@link java.io.Reader} by having one of the following non-deprecated methods:
- public T(Reader in) {...}
- public static T create(Reader in) {...}
- public static T fromReader(Reader in) {...}
Content-Type must not be present or match an existing parser so that it's not parsed as a POJO.
-
Objects convertible from {@link java.io.InputStream} by having one of the following non-deprecated methods:
- public T(InputStream in) {...}
- public static T create(InputStream in) {...}
- public static T fromInputStream(InputStream in) {...}
Content-Type must not be present or match an existing parser so that it's not parsed as a POJO.
-
Objects convertible from {@link java.lang.String} by having one of the following non-deprecated methods:
- public T(String in) {...}
- public static T create(String in) {...}
- public static T fromString(String in) {...}
- public static T parse(String in) {...}
- public static T parseString(String in) {...}
- public static T forName(String in) {...}
- public static T forString(String in) {...}
Note that this also includes all enums.
-
Any {@link java.util.Optional} of anything on this list.
When used in combination with the mutable {@link oaj.Value} object, the {@link oaj.http.annotation.StatusCode @StatusCode} and {@link oaj.http.annotation.Header @Header} annotations
can be used on parameters {@link oajr.annotation.RestOp @RestOp}-annotated methods to
to define to response codes and headers.
| @RestGet("/user/login")
| public void login(
| @FormData("username") String username,
| @FormData("password") String password,
| @StatusCode Value<Integer> status,
| @Header("My-Response-Header") Value<String> myResponseHeader
| ) {
| if (isValid(username, password)) {
| status.set(200);
| myResponseHeader.set("Welcome " + username + "!");
| } else {
| status.set(401);
| }
| }
This is functionally equivalent to the following code:
| @RestGet("/user/login")
| public void doGet(RestRequest req, RestResponse res) {
| RequestFormParams form = req.getFormParams();
| String username = form.get("username").orElse(null);
| String password = form.get("password").orElse(null);
| if (isValid(username, password) {
| res.setStatus(200);
| res.setHeader("My-Response-Header", "Welcome " + username + "!");
| } else {
| res.setStatus(401);
| }
| }
The default registered part marshallers, {@link oaj.oapi.OpenApiSerializer} and {@link oaj.oapi.OpenApiParser}, are used to
marshall POJOs using schemas defined via the {@link oaj.annotation.Schema @Schema} annotation.
For example, the following shows how a pipe-delimited list of comma-delimited numbers (e.g. "1,2,3|4,5,6|7,8,9") can be converted to a 2-dimensional array of Longs:
| @RestGet("/testQuery1")
| public void testQuery1(
| @Query("queryParamName")
| @Schema(
| collectionFormat="pipes",
| items=@Items(
| collectionFormat="csv",
| type="integer",
| format="int64",
| minimum="0",
| maximum="100"
| minLength=1,
| maxLength=10
| ),
| minLength=1,
| maxLength=10
| )
| Long[][] queryParameter
| ) {...}
|
| // Same but using condensed notation.
| @RestGet("/testQuery2")
| public void testQuery2(
| @Query("queryParamName")
| @Schema(
| cf="pipes", minl=1, maxl=10,
| i=@Items(cf="csv", t="integer", f="int64", min="0", max="100", minl=1, maxl=10)
| )
| Long[][] queryParameter
| ) {...}
Schema-based marshalling works for both request and response parts.
Input will be converted based on the types and formats defined in the schema definition.
Input validations such as minLength/maxLength that don't match the input will result in automatic 400 Bad Request responses.
The part and schema annotations are also used for supplying swagger information about the HTTP part.
This information is used to populate the auto-generated Swagger documentation and UI.
| @Query("name")
| @Schema(
| description="Pet name",
| required=true
| )
SVL Variables (e.g. "$L{my.localized.variable}") are supported on annotation fields as well.
Among other things, this allow for annotation values to be defined externally and the ability to produce localized swagger documents
based on the Accept-Language on a request.
| @Schema(
| description="$L{PetNameDescription}"
| )
The {@link oaj.http.annotation.Content @Content} annotation can also be used to parse HTTP request bodies using OpenAPI schemas
when the body content type matches the {@link oaj.oapi.OpenApiParser} parser via the header Content-Type: text/openapi.
The following shows the same for a request body:
| @RestPost("/testContent")
| public void testContent(
| @Content
| @Schema(
| items=@Items(
| collectionFormat="pipes",
| items=@SubItems(
| collectionFormat="csv",
| type="integer",
| format="int64",
| minimum="0",
| maximum="100"
| minLength=1,
| maxLength=10
| )
| ),
| minLength=1,
| maxLength=10
| )
| Long[][] content
| ) {...}
The list of valid POJO types for parameters depends on type and format of the value or items/entries of the value.
For example, instead of Longs in the example above, we could also define a 2-dimensional array of POJOs convertible from Longs:
| // Content is a 2-dimensional array of POJOs convertible from Longs:
| @RestPost("/example1")
| public void testContent(@Content(...) MyPojo1[][] content) {...}
|
| public class MyPojo1 {
| public MyPojo1(Long input) {...}
| }
| // Content is a POJO that takes in a Long array:
| @RestPost("/example2")
| public void testContent(@Content(...) MyPojo2[] content) {...}
|
| public class MyPojo2 {
| public MyPojo2(Long[] input) {...}
| }
| // Content is a POJO that takes in the whole 2-dimensional array:
| @RestPost("/example3")
| public voidtestContent@Content(...) MyPojo3 content) {...}
|
| public class MyPojo3 {
| public MyPojo3(Long[][] input) {...}
| }
As you can see, the complexity of possible input types expands significantly.
For more information about valid parameter types, see OpenAPI Parsers.