Spring MVC and Swagger. Generating documentation for your RESTful web services - PART 2

Automating your RESTful web services documentation Using Spring MVC with Swagger for generating it


Part 2 - Implementing some business logic and configuring Swagger


1.- Configuring swagger and swagger-ui

1.1.- What is swagger ?

Swagger is a specification and complete framework implementation for describing, producing, consuming, and visualizing RESTful web services as stated by their official website

Personally I find Swagger to be a pretty nice way to generate my web services documentation instead of having to rewrite everything again (I already comment my code so it should be enough!)

However at the moment swagger does not support Spring MVC natively but there is an implementation over at github here

As stated by the developer not all the Swagger annotations are currently supported but the implementation works mainly with the Spring MVC annotations(@Controller, @RequestMapping , etc.) which I just what I needed.

1.2.- Integrating swagger into spring

First thing to do is to create a configuration file needed by the swagger-spring-mvc implementation. So go ahead and create a swagger.properties files under src/main/resources/config


#this is the base url for your applications (basically the root url for all your webservices)
documentation.services.basePath=http://localhost:9090/spring-mvc-swagger-tutorial
documentation.services.version=1.0

head up to your spring-web.xml configuration file and add the following lines :


 


    
    
1.3.- Swagger UI

In order to display the web services documentation swaggers it's necessary to download and add the swagger-ui libraries to your project

To my knowledge the Swagger UI dependency cannot be found in the maven repository, so you need to downloaded manually from here :
swagger-ui download

Once you've downloaded it unzip it in your maven webbap folder. you should end up with a tree similar to this one :


Note : It's equally possible to clone the GIT repository instead of downloading the files if you prefer

You will now need to edit the index.html file and change the discoveryUrl parameter to reflect your application

From :
   "discoveryUrl": "http://petstore.swagger.wordnik.com/api/api-docs.json",  
To your application base url (eg : http://localhost:9090/spring-mvc-swagger-tutorial/api-docs.json) :
   "discoveryUrl":"http://localhost:9090/spring-mvc-swagger-tutorial/api-docs.json", 

2.- Writing the business logic

The future controller will be using a simple DAO implementation to recover data from the database

Below is the DAO interface that will be used by the controller. To save some space I will not be posting the DAO implementation here but you can get it from GitHub here

package com.ufasoli.tutorial.swagger.springmvc.core.dao;

import com.ufasoli.tutorial.swagger.springmvc.core.status.OperationResult;
import java.util.List;

public interface DAO<T, U> {

    public OperationResult create(T object);  
    public OperationResult<T> update(U id, T object);  
    public OperationResult delete(U id);  
    public T findOne(U id);  
    public List<T> findAll();
}

I realize the interface and the implementation are far from perfect there is no proper exception handling but since that is not the main goal of this tutorial please bear with me :)

3.- Writing the Spring controllers and swagger "magic"

Finally we are almost there one last thing before we can begin writing some spring controllers and adding some swagger "magic"

There are a few swagger annotations allowing you to customize what it will be printed out by the swagger-ui interface. We'll be using the following :

  • @ApiOperation : describes an API operation or method
  • @ApiParam : describes an api method parameter and it's eventual constraints (ie : required)
  • @ApiError : describes a possible error (HTTP Status) and the error's cause

Bellow is a sample spring controller annotated with the appropriate swagger annotations

package com.ufasoli.tutorial.swagger.springmvc.web.services;
import com.ufasoli.tutorial.swagger.springmvc.core.dao.DAO;
import com.ufasoli.tutorial.swagger.springmvc.core.model.Book;
import com.ufasoli.tutorial.swagger.springmvc.core.status.OperationResult;
import com.wordnik.swagger.annotations.ApiOperation;
import com.wordnik.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.List;


@Controller
@RequestMapping(value = "/books", produces = MediaType.APPLICATION_JSON_VALUE)
public class BookService {

    @Autowired
    private DAO<Book, String> bookDAO;


    @ApiOperation(value = "Creates a Book")
    @RequestMapping(method = RequestMethod.POST)
    public  @ResponseBody void create(@ApiParam(required = true, name = "book", value = "The book object that needs to be created")@RequestBody Book book){

        return bookDAO.create(book);
    }

    
    @ApiOperation(value = "Method to update a book")
    @RequestMapping(method = RequestMethod.PUT, value = "/{bookId}")
    public  @ResponseBody OperationResult<Book> update(
            @ApiParam(required = true, value = "The id of the book that should be updated", name = "bookId")@PathVariable("bookId") String bookId,
                    @ApiParam(required = true, name = "book", value = "The book object that needs to be updated")@RequestBody Book book){

        return bookDAO.update(bookId, book);
    }

    
    @RequestMapping(method = RequestMethod.GET)
    @ApiOperation(value = "Lists all the books in the database")
    public  @ResponseBody List<Book> list(){
        return bookDAO.findAll();
    }

    
    @ApiOperation(value = "Retrieves a book based on their id")
    @ApiErrors(value = {@ApiError(code=404, reason = "No book corresponding to the id was found")})
    @RequestMapping(method = RequestMethod.GET, value = "/{bookId}")
    public  @ResponseBody Book view(@ApiParam(name = "bookId" , required = true, value = "The id of the book that needs to be retrieved")@PathVariable("bookId") String bookId){
         
      Book book =  bookDAO.findOne(bookId);

        if(book == null){

            response.setStatus(404);
        }
        return book;
    }

    
    @ApiOperation(value = "Deletes a book based on their id")
    @RequestMapping(method = RequestMethod.DELETE, value = "/{bookId}")
    public  @ResponseBody OperationResult delete(@ApiParam(name = "bookId", value = "The id of the book to be deleted", required = true)@PathVariable("bookId") String bookId){
      return bookDAO.delete(bookId);

    }

}

4.- Time to check-out the generated documentation

Once again fire up your Jetty server by running the maven goal

  mvn jetty:run

Once your server is started head up to the following URL using your inseparable favorite browser :

http://localhost:9090/spring-mvc-swagger-tutorial/

If Murphy's law has not applied here we should see swagger-ui's homepage

Go ahead and click on the show/hide link right next to the books element. You should see a pretty nice ui show you the different api operations available along with the required params and HTTP methods :

What's even better swagger-ui offers you a light REST client allowing you to directly test your API operations by simply selecting an operation and filling the required parameters.

You can play with the different annotations and options and observe the different results that they produce

5.- Wrapping things up

So that's it, I hope this tutorial was useful to you, even though you will no longer have any excuse to not having nice up-to-date documentation for your RESTful applications.

You can find this sample application over at github :

Swagger - Spring MVC tutorial sample application code

Popular posts from this blog

Multi-hop ssh tunnel - howto : Creating a SSH tunnel with port forwarding between multiple hosts

Spring boot and spring data jpa tutorial - A sample application using spring 4.0 spring boot and JPA (part 2/2)

Maven build number, versioning your projects builds automatically