RestController example with Spring Boot and Swagger

In this article, I’ll explain how to set up a basic example of RestController in a Spring Boot application, using both @GetMapping and @PostMapping annotations. Besides, this application includes Springfox Swagger so the REST API endpoints are documented and accessible through this tool.

Table of Contents

Spring’s Controller annotations

Many people know the advantages of using Spring to create a REST API in a very efficient way. Since version 4 (4.3), Spring has some annotations that make even easier to write REST-enabled Controllers:

  • @RestController: It's a convenience annotation that combines @Controller and @ResponseBody. This last one, when set at class level, makes all the methods annotated with @RequestMapping being considered as annotated with @ResponseBody as well. And, in case you don't know, you need @ResponseBody annotation when returning data from your controller methods in order to build a proper REST interface (as you may guess, to return data such as JSON in your response's body).
  • @GetMapping and @PostMapping are shortcut annotations for @RequestMapping(method = RequestMethod.GET) and @RequestMapping(method = RequestMethod.GET).  When combined with the above-mentioned @RestController, annotated methods will be treated as if they were annotated with @ResponseBody.

Wow! Too many annotations to digest? We’ll get to a more practical explanation about this later when having a look at the Controller class.

Code

All the code included in this post is available on GitHub: Spring Boot RestController. If you find it useful, please give it a star!

Time to see the code! Let’s start with the pom.xml so you see the configuration and dependencies included:

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>es.macero.dev</groupId>
    <artifactId>spring-boot-restcontroller-example</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>spring-boot-restcontroller-example</name>
    <description>Example project using RestController with Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.6.1</version>
        </dependency>

        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.6.1</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>
Get the book Practical Software Architecture

The highlighted lines are the dependencies needed to include Springfox Swagger in our project. We’ll see the advantages of this soon. Now, in order to have some code to play with, I’ve created a SpanishGreeting class with just a message in it:

package es.macero.dev.restexample;

class SpanishGreeting {

    private String message;

    // Required for JSON deserialization
    SpanishGreeting() {
    }

    public SpanishGreeting(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }
}

That’s a very simple class. The only strange part is that empty constructor there. If you’re familiar with working with JSON you’ll know that this constructor is needed by the Jackson deserializer to transform an incoming JSON message to the corresponding Java class (in our case when calling the POST method below). Usually, you don’t need this constructor for anything else, so it shouldn’t be public.

And, finally, the main part of our application: the controller class.

@RestController
@RequestMapping("/spanish-greetings")
public class SpanishGreetingController {

    private List<SpanishGreeting> spanishGreetings;

    public SpanishGreetingController() {
        spanishGreetings = new ArrayList<>();
        spanishGreetings.add(new SpanishGreeting("Hola!"));
        spanishGreetings.add(new SpanishGreeting("Qué tal?!"));
        spanishGreetings.add(new SpanishGreeting("Buenas!"));
    }

    @GetMapping("/{id}")
    public SpanishGreeting getSpanishGreetingById(@PathVariable("id") final int id) {
        return spanishGreetings.get(id - 1); // list index starts with 0 but we prefer to start on 1
    }

    @GetMapping("/random")
    public SpanishGreeting getRandom() {
        return spanishGreetings.get(new Random().nextInt(spanishGreetings.size()));
    }

    @PostMapping
    @ResponseStatus(HttpStatus.OK)
    public void createSpanishGreeting(@RequestBody SpanishGreeting spanishGreeting) {
        spanishGreetings.add(spanishGreeting);
    }
}

Let’s see what we have here:

  • The class is annotated with @RestController. As explained above, if we use just @Controller instead, then we need some extra annotations @ResponseBody at method level for getSpanishGreetingById and getRandom.  Or, since Spring version 4, we could also annotate the class with @ResponseBody and have the same effect. So, @RestController annotation is just a shortcut of two annotations.
  • The class is also annotated with @RequestMapping, that's a convenient way for setting the same context url to all methods defined inside the class. In our case, the resulting endpoints will be:
    • GET /spanish-greetings/{id}
    • GET /spanish-greetings/random
    • POST /spanish-greetings
  • Methods annotated with @GetMapping are just same as if they were annotated with @RequestMapping(method = RequestMethod.GET). Again, we don't need @ResponseBody in these methods. The @PathVariable annotation indicates Spring from where to pick that variable value, in this case from the path. We could use also @RequestParam if it were a parameter instead (also changing the mapping pattern).
  • @PostMapping, as you may guess, is actually same as @RequestMapping(method = RequestMethod.POST). We don't use here any extra mapping value because we want to post our data to the collection mapping, following best REST API conventions.
  • The @ResponseStatus(HttpStatus.OK) is not needed here, it's just there to see explicitly that we want to return that status code, but it's actually the default one.

And now some curiosities:

  • As you might have imagined, there are some other shortcut annotations for DELETE, PUT, etc. Check out this link if you're curious.
  • In Spring, if you don't use the method for a @RequestMapping, you're accepting all of them. That means that if you for example change the @GetMapping("/random") for @RequestMapping("/random"), you can do a DELETE /spanish-greetings/random and you will still receive a nice Spanish greeting. Always specify the method when defining your API with Spring or you will easily fall in bad practices. You can try this in the code and check the changes in the Swagger UI (you'll see a lot more of endpoints).
  • As many other smart features of Spring MVC, it is able to convert our id path variable to int magically, using default type converter.
  • You might be asking yourself why the @ResponseBody annotation was magically embedded inside @RestController but we still need to write the @RequestBody annotation for our POST method. Why not doing the same? You can find a good reasoning in this JIRA ticket for the Spring project. Summary: because it would be going too far with assumptions, depending on the client library you use there could be some useful query parameters.

Trying out the API with Swagger UI

Thanks to the dependencies added to the project and this configuration added to our Spring Boot application,

@Bean
public Docket api() {
    return new Docket(DocumentationType.SWAGGER_2)
            .select()
            .apis(RequestHandlerSelectors.any())
            .paths(PathSelectors.regex("/spanish-greeting.*"))
            .build();
}

we can navigate to http://localhost:8080/swagger-ui.html (after we start our application) and play with the REST API:

swagger screenshot
swagger screenshot

In this case, we’re using Swagger in the most basic way, and mainly to use the UI, but it’s a powerful tool to add documentation to your REST API just by adding annotations to your code.

You can try the API in the following ways:

  • Do some GETs to /random, you should see the predefined Spanish Greetings being retrieved.
  • GET some of them by id, the default ones are 1, 2, 3.
  • POST a new greeting, it will be included in the list and you can access it by id or randomly. Do you want suggestions? Just try "Buenos días!" or "Buenas tardes!"...
If you're interested in Spring Boot and creating real (not one-class) microservices with Restful APIs and integration between them, get my book today and get real experience on Microservice Architecture.
Moisés Macero's Picture

About Moisés Macero

Software Developer, Architect, and Author.
Check my workshops

Amsterdam, The Netherlands https://thepracticaldeveloper.com

Comments