kgoedert
Posted on June 6, 2020
Sometimes when you are building a REST API, you don't want the user to be able to set some specific property. If he decides to send it anyway, you want to discard it. Or, not even receive it. Jackson can help with that.
But it would be even nicer if the developer that is using your API, has site where he can test your API, and besides that have some documentation showing what is required and what is not. Here, swagger ui. But say the developer using your api doesn't like GUIs much, and prefers the command line. There is a solution for that too.
To show all this tools working together I am going to use quarkus and build upon on one of their examples.
The full code is available on github.
The code
The example has enpoint where the user can post some fruits, and the class that represent the fruits.
@Path("/fruits")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class FruitResource {
@POST
@Operation(summary = "POSTs a new fruit to the list")
@APIResponse(responseCode = "200", description = "Fruit registration successful")
@APIResponse(responseCode = "500", description = "Server unavailable")
public Response save(Fruit fruit) {
if (fruit.getCreated() == null) {
fruit.addCreationDate();
}
if (fruit.getUuid() == null) {
fruit.addUUID();
}
return Response.ok(fruit).build();
}
}
@Schema(name = "Fruit", description = "Represents a fruit")
@JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE)
public class Fruit {
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
@Schema(required = false, readOnly = true)
private String uuid;
@Schema(required = true, example = "The name of the fruit you want to save")
private String name;
@Schema(required = false, example = "Some description for the fruit")
private String description;
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
@JsonFormat(shape = Shape.STRING, pattern = "MM/dd/yyyy HH:mm:ss")
@Schema(required = false, readOnly = true, pattern = "MM/dd/yyyy HH:mm:ss")
private LocalDateTime created;
public void addCreationDate() {
this.created = LocalDateTime.now();
}
public void addUUID() {
this.uuid = UUID.randomUUID().toString();
}
public LocalDateTime getCreated() {
return created;
}
public String getUuid() {
return uuid;
}
}
Here, you can see the use of swagger open api annotations, @Operation
and @APIResponse
. Their purpose is to document the endpoint. It will be shown in swagger-ui and when you access the open api URL of the application.
In the fruit class, the @Schema
annotations serve the purpose of documenting and marking if the property is required, its pattern, as in the case of the date field, and setting it as readonly. This annotation is used by swagger and open api. It means that when the user receives the result of the post, the result will have this field present, but when trying to post a fruit, this field will be ignored.
The @JsonAutoDetect
, @JsonProperty
and @JsonFormat
do the same for jackson, that is responsible for parsing the results. If the property is marking as readonly, it will be ignored when something is posted, but it will be available on the response.
There is also the writeonly version of the property, that you can use for example to send something when you post, like a password, the you do not want it shown, when the user makes a get request.
You can see the swagger UI on http://localhost:8080/swagger-ui
and it will show you something like this
And in the open api url you should see something like this
curl http://localhost:8080/openapi
---
openapi: 3.0.1
info:
title: Generated API
version: "1.0"
paths:
/fruits:
post:
summary: POSTs a new fruit to the list
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Fruit'
responses:
"500":
description: Server unavailable
"200":
description: Fruit registration successful
components:
schemas:
Fruit:
description: Represents a fruit
required:
- name
type: object
properties:
created:
allOf:
- $ref: '#/components/schemas/LocalDateTime'
- pattern: MM/dd/yyyy HH:mm:ss
readOnly: true
description:
type: string
example: Some description for the fruit
name:
type: string
example: The name of the fruit you want to save
uuid:
type: string
readOnly: true
LocalDateTime:
format: date-time
type: string
This makes it very easy to document your APIs.
Posted on June 6, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.