Free Online Courses for Software Developers - MrBool
× Please, log in to give us a feedback. Click here to login
×

You must be logged to download. Click here to login

×

MrBool is totally free and you can help us to help the Developers Community around the world

Yes, I'd like to help the MrBool and the Developers Community before download

No, I'd like to download without make the donation

×

MrBool is totally free and you can help us to help the Developers Community around the world

Yes, I'd like to help the MrBool and the Developers Community before download

No, I'd like to download without make the donation

Use Case: Documenting a JAX-RS API with Swagger

In this article, we will see how to use Swagger to document a REST API for both machines and humans.

We will use the Swagger to document a API REST for both machines and for humans

In IT world, it is common to think first in the details of the API we develop before going to its real implementation. This is known as Contract-First or API-First. Another approach, maybe more common, is to get started by the API implementation and just then, document it.

In this article, we will document a single API: a simplified API of payments, using JAX-RS and Swagger.

Among the model classes, we have Transaction and Payment (Listing 1).

Listing 1. Main classes of our API.

public class Transaction {
            private String number;
            private String owner;
            private LocalDate date;
            private BigDecimal value;
			
            // getters e setters...
}

public class Payment {
            private Integer id;
            private String status;
            private BigDecimal value;
 
            // getters e setters...
}

In our REST API, a POST in /payments creates a new payment from a transaction. You can then confirm the payment with a PUT or cancel it with a DELETE.

If you want to know more about Swagger, please refer our article of how to use it with Node.js.

State machine of our API

An implementation of this API using JAX-RS would be something like code in Listing 2.

Listing 2. Implementation of the API.

 @Path("/payments")
public class PaymentResource {
  //...
  @POST
  @Consumes(MediaType.APPLICATION_JSON)
  public Response createPayment(Transaction transaction) throws URISyntaxException {
    // here we have the code that creates a payment from the transaction
    return Response.created(new URI("/payments/" + payments.getId())).entity(payment)
        .type(MediaType.APPLICATION_JSON).build();
  }
 
  @PUT
  @Path("/{id}")
  @Produces(MediaType.APPLICATION_JSON)
  public Response confirmPayment(@PathParam("id") Integer id) {
    // here, the code that confirms a payment...
    return Response.ok().entity(payment).build();
  }
 
  @DELETE
  @Path("/{id}")
  @Produces(MediaType.APPLICATION_JSON)
  public Response cancelPayment(@PathParam("id") Integer id) {
    //here, the code that cancels a payment...
    return Response.ok().entity(payment).build();
  }
}

In order to see the serialization from/to JSON working for the java.time.LocalDate of the Transaction class in a Java EE 7 server, we need some configurations. We do this in JacksonJavaTimeConfiguration class.

We cannot forget to enable the JAX-RS in our application:

@ApplicationPath("/v1")
public class PaymentService extends Application {
}

Configuring Swagger in our API

To use Swagger to document our API, we need its jars. The best way is to use some dependencies management tool. To set the dependency with Maven we use the code in Listing 3.

Listing 3. Setting Swagger dependency with Maven.

<dependency>
	<groupId>io.swagger</groupId>
	<artifactId>swagger-jaxrs</artifactId>
	<version>1.5.7</version>
</dependency>

Note: You may want to exclude certain transitive dependencies of swagger-jaxrs.

Through BeanConfig class, from io.swagger.jaxrs.config package, we can make some basic configurations such as title, description and version of the API, server address and application context, if it is used HTTP or HTTPS and packets whose REST resources must be scanned .

We create an object of that class in PaymentService’s constructor and when invoking setScan method, the settings are performed (Listing 4).

Listing 4. Invoking the API.

@ApplicationPath("/v1")
public class PaymentService extends Application {
  public PaymentService() {
    BeanConfig conf = new BeanConfig();
    conf.setTitle("Payfast API");
    conf.setDescription("Payfast ");
    conf.setVersion("1.0.0");
    conf.setHost("localhost:8080");
    conf.setBasePath("/fj36-payfast/v1");
    conf.setSchemes(new String[] { "http" });
    conf.setResourcePackage("br.com.caelum.payfast");
    conf.setScan(true);
  }
}

In addition, we need to load the ApiListingResource and SwaggerSerializers of Swagger classes. For this, we are overriding the method getClasses of the Application class, in PaymentService. One annoying thing is that when we do this, we need to manually add the JacksonJavaTimeConfiguration class and the PaymentResource feature (Listing 5).

Listing 5. Adding manually the configurations.

@ApplicationPath("/v1")
public class PaymentService extends Application {
  public PaymentService() {
    // code omitted
  }
  @Override
  public Set<Class<?>> getClasses() {
      Set<Class<?>> resources = new HashSet<>();
      resources.add(JacksonJavaTimeConfiguration.class);
      resources.add(PaymentResource.class);
       
      // classes of the Swagger...
      resources.add(ApiListingResource.class);
      resources.add(SwaggerSerializers.class);
      return resources;
   }
}

In order to get Swagger managing the documentation for our resource, we must annotate the PaymentResource class with @Api:

@Api
@Path("/payments")
public class PaymentResource {
  //remaining code...
}

Getting the documentation generated by Swagger

Then we can get the JSON and YAML describing our API in the following URLs, respectively:

http://localhost:8080/ourapi/v1/swagger.json and http://localhost:8080/ourapi/v1/swagger.yaml.

The JSON and YAML generated by Swagger from our PaymentResource resource uses OpenAPI language to detail URIs, Content-Types, supported HTTP methods, response codes and data models. The generated YAML would look like code in Listing 6.

Listing 6. Docs generated by Swagger.

---
swagger: "2.0"
info:
  description: "Payfast"
  version: "1.0.0"
  title: "Payfast API"
host: "localhost:8080"
basePath: "/fj36-payfast/v1"
schemes:
- "http"
paths:
  /payments:
    post:
      operationId: "createPayment"
      parameters:
      - in: "body"
        name: "body"
        required: false
        schema:
          $ref: "#/definitions/Transaction"
      responses:
        default:
          description: "successful operation"
  /payments/{id}:
    put:
      operationId: "confirmPayment"
      parameters:
      - name: "id"
        in: "path"
        required: true
        type: "integer"
        format: "int32"
      responses:
        default:
          description: "successful operation"
    delete:
      operationId: "cancelPayment"
      parameters:
      - name: "id"
        in: "path"
        required: true
        type: "integer"
        format: "int32"
      responses:
        default:
          description: "successful operation"
definitions:
  Transaction:
    type: "object"
    properties:
      number:
        type: "string"
      owner:
        type: "string"
      date:
        type: "string"
        format: "date"
      value:
        type: "number"
  Payment:
    type: "object"
    properties:
      id:
        type: "integer"
        format: "int32"
      status:
        type: "string"
      value:
        type: "number"

Correcting some details

There are some differences between the modeled YAML for our API and the YAML generated by Swagger from PaymentResource.

For the POST in /payments, the most significant differences are:

  • There is no description (summary);
  • The received and sent content-types are gone;
  • The response was with a generic description, without the Location header or the status 201;
  • The parameter is not required and with a bad name (body).

To set a description and content-types to the POST in /payments, we use the @ApiOperation annotation. For the status and the header in the response, we use the @ApiResponses, @ApiResponse and @ResponseHeader annotations. To modify the requirement and parameter name, we use @ApiParam (Listing 7).

Listing 7. Setting up the needed annotations.

@ApiOperation(
  value = "Create new payment", 
  consumes = MediaType.APPLICATION_JSON, 
  produces = MediaType.APPLICATION_JSON)
@ApiResponses(
  @ApiResponse(
    code=201,
    message="New payment create",
    response = Payment.class, 
    responseHeaders=
      @ResponseHeader(
        name="Location", 
        description="uri of the new payment",
        response=String.class)))
@POST
@Consumes(MediaType.APPLICATION_JSON)
public Response createPayment(
  @ApiParam(
    value="Transaction", 
    name="transaction", 
    required=true)
  Transaction transaction) throws URISyntaxException {
    //code omitted...
}

After these settings, the POST stretch in YAML would be generated like this code in Listing 8.

Listing 8. New generated code.

 /payments:
  post:
    summary: "Create new payment"
    description: ""
    operationId: "createPayment"
    consumes:
    - "application/json"
    produces:
    - "application/json"
    parameters:
    - in: "body"
      name: "transaction"
      description: "Transaction"
      required: true
      schema:
        $ref: "#/definitions/Transaction"
    responses:
      201:
        description: "New payment created"
        schema:
          $ref: "#/definitions/Payment"
        headers:
          Location:
            type: "string"
            description: "the new payment uri"

Very similar to what we had before!

The most important differences for the PUT and DELETE are as follows:

  • No status 200 in response;
  • The path parameter is duplicated

We can use the annotation @ApiResponse to improve the response information. Unfortunately, there is no avoiding duplication of the parameter id.

Listing 9. Making the fixes.

@ApiResponses(
  @ApiResponse(
    code=200,
    message="Payment confirmed",
    response = Payment.class))
@PUT
@Path("/{id}")
@Produces(MediaType.APPLICATION_JSON)
public Response confirmPayment(@PathParam("id") Integer paymentId) {
    //code omitted...
}
 
@ApiResponses(
  @ApiResponse(
    code=200,
    message="Payment canceled",
    response = Payment.class))
@DELETE
@Path("/{id}")
@Produces(MediaType.APPLICATION_JSON)
public Response cancelPayment(@PathParam("id") Integer paymentId) {
    //code omitted...
}

The corresponding section of the YAML would be like in Listing 10.

Listing 10. New generated code.

/payments/{id}:
  put:
    operationId: "confirmPayment"
    parameters:
    - name: "id"
      in: "path"
      required: true
      type: "integer"
      format: "int32"
    responses:
      200:
        description: "Payment confirmed"
        schema:
          $ref: "#/definitions/Payment"
  delete:
    operationId: "cancelPayment"
    parameters:
    - name: "id"
      in: "path"
      required: true
      type: "integer"
      format: "int32"
    responses:
      200:
        description: "Payment canceled"
        schema:
          $ref: "#/definitions/Payment"

Documentation for humans

We have the JSON and YAML, but these formats are not readable for people. They are almost as difficult to read as a WSDL.

In Web Services SOAP style, is quite common to have available PDFs that focus on describing the API for humans.

Do we need to write a PDF manually? It would be interesting any way to turn that JSON or YAML into an HTML page with good usability.

To achieve this, there is the project Swagger UI! In the page of the tool, you can download a zip with the latest release. After extracting, copying the contents of the dist folder to a doc folder in the document root folder (WebContent or webapp) of our project.

Then, the page of documentation would be available on: http://localhost:8080/ourapi/doc/. The strange thing is that the documentation would not be displayed on our API, but in such a PetStore. It is an example that is configured on Swagger UI.

We need to modify the following JavaScript snippet of the index.html page, correcting the URL.

 url = "http://petstore.swagger.io/v2/swagger.json";

We must change to:

 url = "../v1/swagger.json";

To re-access the page, we have a reasonably readable documentation.

The Swagger IU, through a JavaScript code, transforms the swagger.json documentation. You can trigger test requests with different content-type. You can use authorization, oauth, among many other features.



Julio is a System analyst and enthusiast of Information Technology. He is currently a developer at iFactory Solutions company, working in the development of strategic systems, is also a JAVA instructor. He has knowledge and experi...

What did you think of this post?
Services
[Close]
To have full access to this post (or download the associated files) you must have MrBool Credits.

  See the prices for this post in Mr.Bool Credits System below:

Individually – in this case the price for this post is US$ 0,00 (Buy it now)
in this case you will buy only this video by paying the full price with no discount.

Package of 10 credits - in this case the price for this post is US$ 0,00
This subscription is ideal if you want to download few videos. In this plan you will receive a discount of 50% in each video. Subscribe for this package!

Package of 50 credits – in this case the price for this post is US$ 0,00
This subscription is ideal if you want to download several videos. In this plan you will receive a discount of 83% in each video. Subscribe for this package!


> More info about MrBool Credits
[Close]
You must be logged to download.

Click here to login