---
title: Spring Boot Rest Tutorial
url: https://blog.octoperf.com/spring-boot-rest-tutorial/
date: '2018-02-22'
authors:
- jloisel
categories:
- Development
tags:
- Java
- Spring
- REST
- HTTP
- Maven
description: Get started quickly with your first Rest API. Learn how to build it with Spring Boot, Spring MVC, Thymeleaf and Jackson Json.
---

# Spring Boot Rest Tutorial

I'm sure you're looking for a complete **Spring Rest Tutorial** which covers the most important topics related to [Spring Boot](https://projects.spring.io/spring-boot/). You're in the right place!

You want to build a web application or a [REST API](https://fr.wikipedia.org/wiki/Representational_state_transfer) using Spring Boot (and other popular technologies like Thymeleaf), but you don't know where to start... Let me help you get things done. This tutorial explains how to create a simple Rest Api **exposing data as Json**.

Don't worry, Spring isn't that difficult! **In under 5 minutes**, you will build your first web app using Spring Boot.

**NOTE**: Updated with [Spring Boot 2](https://spring.io/blog/2018/03/01/spring-boot-2-0-goes-ga) and Spring 5!

> Full source code is available at [Spring Boot 2 Demo](https://github.com/jloisel/spring-boot2-demo) on Github.


## Spring Initializr

To get started quickly, please generate a **Sample Web** project using [Spring Initializr](https://start.spring.io/). Some of the sections below review part of the code being generated by Spring Initializr.

Even if you could not use Spring Initialzr, you should be able to follow this tutorial.

## Maven Module Pom

First, we need to create a Maven Project with the following `pom.xml`:

```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>com.octoperf</groupId>
  <artifactId>demo</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>demo</name>
  <description>Demo project for Spring Boot</description>

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.1.RELEASE</version>
    <relativePath/>
  </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.projectlombok</groupId>
      <artifactId>lombok</artifactId>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

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

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


</project>


```

Let's understand what's being configured here:

- **Parent**: `spring-boot-starter-parent` is a convenient Maven Parent POM configured with everything needed to run a *Spring Boot* application. By defining it as a parent, most dependencies like `lombok` or `jackson` are already managed (no need to specify a version),
- **Dependencies**:
 
    - [lombok](https://projectlombok.org/): provides annotations to generate most of the boiler plate code (like constructors, equals and hashcode methods etc.),
    - `jackson-core`: [Jackson](https://github.com/FasterXML/jackson) is a popular Java Json serialization framework,
    - [spring-boot-starter-web](https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web) is Spring dependencies which imports everything needed to build a web application using Spring Boot.

By adding Jackson as a dependency, Spring Boot automatically configures The Rest endpoints with **Jackson** serializer.

Please make sure to enable **Annotation Processing** within the IDE. It can be done in **Build &gt; Compiler &gt; Annotation Processors** in Intellij (`Enable Annotation Processing`, and `Obtain processors from project classpath`).

Now, we need to create a main application which will bootstrap Spring Boot.

## Spring-Boot Bootstrap

If you're already so far, you have a working maven project in your favorite IDE (like [Intellij](https://www.jetbrains.com/idea/)). It's time to create the main application:

```java

package com.octoperf;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {

  public static void main(String[] args) {
    SpringApplication.run(DemoApplication.class, args);
  }
}


```

I've created the class `DemoApplication` in the `com.octoperf.demo` package. When the Java application will be run, the execution will be directly delegated to the `SpringApplication` class. 

`SpringApplication` bootstraps and launches a Spring application from a Java main
 method. By default it will perform the following steps to bootstrap your
 application:
 
 - Create an appropriate *ApplicationContext* instance (depending on your classpath),
 - Register a *CommandLinePropertySource* to expose command line arguments as Spring properties,
 - Refresh the application context, loading all singleton beans,
 - And Trigger any *CommandLineRunner* beans.

 What is an `ApplicationContext`?

 Central interface to provide configuration for an application. This is read-only while the application is running, but may be reloaded if the implementation supports this.

 What is a `CommandLinePropertySource` ?

 Abstract base class for {@link PropertySource} implementations backed by command line arguments. The parameterized type **T** represents the underlying source of command line options.

 What is a `CommandLineRunner`?

 Interface used to indicate that a bean should **run** when it is contained within a `SpringApplication`.

 If we take a look at what the `@SpringBootApplication` does:

```java

 @Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
    @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
    @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
  ...
}

```

It's an annotation which enables various features like [Spring Boot Auto-Configuration](https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-auto-configuration.html) and [component scanning](https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-structuring-your-code.html). 

Spring will automatically discover any Spring annotated class (like `@Component` or `@Service`) under the `com.octoperf.demo` and instantiate them on application startup.

So yeah, **there is a lot of magic** behind the scene, but no worries! You have plenty of time to master Spring internals once you understand the basics.

## Person Bean

Let's now create a `Person` bean within the same package. This bean represents a Person:

```java

package com.octoperf;

import lombok.Data;

@Data
public class Person {
  String firstname, lastname;
}


```

Thanks to lombok's `@Value` annotation, the bean is pretty simple! Lombok takes care of generating the constructor, equals/hashcode methods, getters and define all fields as `private final`.

We're going to send this bean over the wire by serializing it into Json.

## Spring MVC Controller

Now let's expose the `Person` bean through a Spring MVC Controller:

```java

package com.octoperf;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/person")
class PersonController {

  @GetMapping("/hello")
  public Person hello() {
    final Person person = new Person();
    person.setFirstname("John");
    person.setLastname("Smith");
    return person;
  }
}


```

This endpoint exposes the path `/person/hello`, which returns a `Person` instance with *John* as firstname, and *Smith* as lastname.

Let's edit the application.properties to configure the `server.port` property which controls the port on which the Spring Boot server will run:

```yml

server.port=8081

```

Spring Boot embeds an [Apache Tomcat](https://tomcat.apache.org/) application server by default.

## Starting the Demo App

It's time to run the application! Open the `DemoApplication` class and right-click on it. Then select, `Run DemoApplication.main()`. It should start the web application:

```bash

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.0.0.RELEASE)

2018-04-03 16:28:16.378  INFO 186339 --- [           main] com.octoperf.DemoApplication             : Starting DemoApplication on desktop with PID 186339 (/home/ubuntu/git/demo/target/classes started by ubuntu in /home/ubuntu/git/demo)
2018-04-03 16:28:16.381  INFO 186339 --- [           main] com.octoperf.DemoApplication             : No active profile set, falling back to default profiles: default
2018-04-03 16:28:16.427  INFO 186339 --- [           main] ConfigServletWebServerApplicationContext : Refreshing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@3c130745: startup date [Tue Apr 03 16:28:16 CEST 2018]; root of context hierarchy
2018-04-03 16:28:17.427  INFO 186339 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8081 (http)
2018-04-03 16:28:17.452  INFO 186339 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2018-04-03 16:28:17.453  INFO 186339 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/8.5.28
2018-04-03 16:28:17.467  INFO 186339 --- [ost-startStop-1] o.a.catalina.core.AprLifecycleListener   : The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: [/usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib]
2018-04-03 16:28:17.550  INFO 186339 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2018-04-03 16:28:17.550  INFO 186339 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1126 ms
2018-04-03 16:28:17.638  INFO 186339 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Servlet dispatcherServlet mapped to [/]
2018-04-03 16:28:17.641  INFO 186339 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'characterEncodingFilter' to: [/*]
2018-04-03 16:28:17.642  INFO 186339 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2018-04-03 16:28:17.642  INFO 186339 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'httpPutFormContentFilter' to: [/*]
2018-04-03 16:28:17.642  INFO 186339 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'requestContextFilter' to: [/*]
2018-04-03 16:28:17.918  INFO 186339 --- [           main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@3c130745: startup date [Tue Apr 03 16:28:16 CEST 2018]; root of context hierarchy
2018-04-03 16:28:17.997  INFO 186339 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/person/hello],methods=[GET]}" onto public com.octoperf.Person com.octoperf.PersonController.hello()
2018-04-03 16:28:18.001  INFO 186339 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2018-04-03 16:28:18.002  INFO 186339 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2018-04-03 16:28:18.034  INFO 186339 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-04-03 16:28:18.035  INFO 186339 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-04-03 16:28:18.076  INFO 186339 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-04-03 16:28:18.243  INFO 186339 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2018-04-03 16:28:18.281  INFO 186339 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8081 (http) with context path ''
2018-04-03 16:28:18.284  INFO 186339 --- [           main] com.octoperf.DemoApplication             : Started DemoApplication in 2.267 seconds (JVM running for 2.65)



```

The output above shows the application command-line output. It states the application is running on port `8081`. The application has started in `2.181 seconds` on my computer.

> 2018-01-16 14:49:59.373  INFO 35582 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/person/hello],methods=[GET]}" onto public com.octoperf.demo.Person com.octoperf.demo.PersonController.hello()

The line above means the `PersonController` has been successfully detected and mapped.

Now let's run a `curl` command-line to check if the endpoint is working properly: `curl http://localhost:8081/person/hello`.

The output should be:

```json
{"firstname":"John","lastname":"Smith"}
```

Congratulations, you've just built **your first Rest Api using Spring Boot**!

## Post Rest Endpoint

Let's go further by adding a new endpoint to our existing `PersonController`:

```java

@PostMapping("/hello")
public String postHello(@RequestBody final Person person) {
  return "Hello " + person.getFirstname() + " " + person.getLastname() + "!";
}

```

The server should respond:

```bash
$ curl -XPOST -H 'Content-type: application/json' -d '{"firstname": "John","lastname":"Smith"}' http://localhost:8081/person/hello
Hello John Smith!
```

Nice! We were able to send an object in Json format and Spring converted it back to the corresponding Java Object using Jackson.

## Securing the Endpoints

Now, let's secure our endpoints to prevent unauthorized access. We're going to enable [Basic Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication). How does it work?

The client has to send an `Authorization` [HTTP Header](https://en.wikipedia.org/wiki/List_of_HTTP_header_fields) within the request like the following:

```http
Authorization: Basic QWxhZGRpbjpPcGVuU2VzYW1l
```

The header value starts with the `Basic` keyword followed by the `username:password` encoded in Base64.

First, we need to add the following Maven dependency to the `pom.xml`:

```xml
<dependency> 
    <groupId>org.springframework.boot</groupId> 
    <artifactId>spring-boot-starter-security</artifactId> 
</dependency>
```

First, let's add the  following config to our `application.yml`:

```yaml

spring:
  security:
    user:
      name: admin
      password: passw0rd
      roles: USER

```

Then, we must create a Security `@Configuration` annotated class:

```java
package com.octoperf;


import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

  @Override
  protected void configure(final HttpSecurity http) throws Exception {
    http
      .csrf()
      .disable()
      .httpBasic()
      .and()
      .authorizeRequests()
      .anyRequest()
      .authenticated();
  }
}

```
This configuration tells **Spring Boot 2** to enable basic authentication.
Note the `{noop}` prefix tells Spring Security to ignore password encoding in this case.

For more information, see [Spring Security 5 Password Encoder](https://spring.io/blog/2017/11/01/spring-security-5-0-0-rc1-released) for more information. This is new to **Spring 5**.

Now it's time to restart the web application:

```bash

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.0.0.RELEASE)

2018-04-03 16:32:59.059  INFO 188101 --- [           main] com.octoperf.DemoApplication             : Starting DemoApplication on desktop with PID 188101 (/home/ubuntu/git/demo/target/classes started by ubuntu in /home/ubuntu/git/demo)
2018-04-03 16:32:59.062  INFO 188101 --- [           main] com.octoperf.DemoApplication             : No active profile set, falling back to default profiles: default
2018-04-03 16:32:59.111  INFO 188101 --- [           main] ConfigServletWebServerApplicationContext : Refreshing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@35ef1869: startup date [Tue Apr 03 16:32:59 CEST 2018]; root of context hierarchy
2018-04-03 16:33:00.185  INFO 188101 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8081 (http)
2018-04-03 16:33:00.210  INFO 188101 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2018-04-03 16:33:00.210  INFO 188101 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/8.5.28
2018-04-03 16:33:00.220  INFO 188101 --- [ost-startStop-1] o.a.catalina.core.AprLifecycleListener   : The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: [/usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib]
2018-04-03 16:33:00.302  INFO 188101 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2018-04-03 16:33:00.302  INFO 188101 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1195 ms
2018-04-03 16:33:00.437  INFO 188101 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'characterEncodingFilter' to: [/*]
2018-04-03 16:33:00.438  INFO 188101 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2018-04-03 16:33:00.438  INFO 188101 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'httpPutFormContentFilter' to: [/*]
2018-04-03 16:33:00.438  INFO 188101 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'requestContextFilter' to: [/*]
2018-04-03 16:33:00.438  INFO 188101 --- [ost-startStop-1] .s.DelegatingFilterProxyRegistrationBean : Mapping filter: 'springSecurityFilterChain' to: [/*]
2018-04-03 16:33:00.439  INFO 188101 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Servlet dispatcherServlet mapped to [/]
2018-04-03 16:33:00.712  INFO 188101 --- [           main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@35ef1869: startup date [Tue Apr 03 16:32:59 CEST 2018]; root of context hierarchy
2018-04-03 16:33:00.790  INFO 188101 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/person/hello],methods=[GET]}" onto public com.octoperf.Person com.octoperf.PersonController.hello()
2018-04-03 16:33:00.791  INFO 188101 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/person/hello],methods=[POST]}" onto public java.lang.String com.octoperf.PersonController.postHello(com.octoperf.Person)
2018-04-03 16:33:00.795  INFO 188101 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2018-04-03 16:33:00.796  INFO 188101 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2018-04-03 16:33:00.829  INFO 188101 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-04-03 16:33:00.829  INFO 188101 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-04-03 16:33:00.867  INFO 188101 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-04-03 16:33:01.349  INFO 188101 --- [           main] o.s.s.web.DefaultSecurityFilterChain     : Creating filter chain: org.springframework.security.web.util.matcher.AnyRequestMatcher@1, [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@7f02251, org.springframework.security.web.context.SecurityContextPersistenceFilter@73877e19, org.springframework.security.web.header.HeaderWriterFilter@30404dba, org.springframework.security.web.csrf.CsrfFilter@53093491, org.springframework.security.web.authentication.logout.LogoutFilter@75b3673, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@7dd00705, org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter@2b0b4d53, org.springframework.security.web.authentication.www.BasicAuthenticationFilter@6d4a65c6, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@5bfc257, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@4443ef6f, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@dffa30b, org.springframework.security.web.session.SessionManagementFilter@4c0884e8, org.springframework.security.web.access.ExceptionTranslationFilter@23ee75c5, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@267517e4]
2018-04-03 16:33:01.436  INFO 188101 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2018-04-03 16:33:01.475  INFO 188101 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8081 (http) with context path ''
2018-04-03 16:33:01.479  INFO 188101 --- [           main] com.octoperf.DemoApplication             : Started DemoApplication in 2.774 seconds (JVM running for 3.16)
2018-04-03 16:33:15.467  INFO 188101 --- [nio-8081-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring FrameworkServlet 'dispatcherServlet'
2018-04-03 16:33:15.467  INFO 188101 --- [nio-8081-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization started
2018-04-03 16:33:15.490  INFO 188101 --- [nio-8081-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization completed in 23 ms


```

As you see, new log lines related to `org.springframework.security.web` have appeared, meaning [Spring Security](https://projects.spring.io/spring-security/) has been enabled. For those who want to understand how the auto-configuration works, see [SpringBootWebSecurityConfiguration javadoc](https://docs.spring.io/spring-boot/docs/2.0.6.RELEASE/api/org/springframework/boot/autoconfigure/security/servlet/SpringBootWebSecurityConfiguration.html).

Now, let's run the *curl* command again:

```bash

curl -XPOST -H 'Content-type: application/json' -d '{"firstname": "John","lastname":"Smith"}' http://localhost:8081/person/hello
{"timestamp":1516112340374,"status":401,"error":"Unauthorized","message":"Full authentication is required to access this resource","path":"/person/hello"}

```

The server responds by stating **the endpoint requires an authentication**. Let's try again by specifying the username and password:

```bash

curl -XPOST -H 'Content-type: application/json' -d '{"firstname": "John","lastname":"Smith"}' http://admin:passw0rd@localhost:8081/person/hello
Hello John Smith!

```

Great! The endpoint is now secured by a general Basic Authentication. Of course, when building a real-world web application, you probably want to secure your web-application with a specific access per user. We'll cover this point in a future article.

## Business Logic moved to a service

The thing is, it's pretty ugly to have the application logic written right inside the controller. Sure, this demo application is simple enough and it does not really matter here. But, when building a fully-fledged web-application using Spring, **you have high-level services which do the job for you**.

Let's create a simple `PersonService` which does the actual job in a sub-package called `com.octoperf.demo.service`:

```java

package com.octoperf;

import com.octoperf.demo.Person;

public interface PersonService {

  Person johnSmith();

  String hello(Person person);
}


```

The implementation is the following:

```java

package com.octoperf.demo.service;

import com.octoperf.demo.Person;
import org.springframework.stereotype.Service;

@Service
final class DemoPersonService implements PersonService {
  @Override
  public Person johnSmith() {
    final Person person = new Person();
    person.setFirstname("John");
    person.setLastname("Smith");
    return person;
  }

  @Override
  public String hello(final Person person) {
    return "Hello " + person.getFirstname() + " " + person.getLastname() + "!";
  }
}


```

In the code above:

- `DemoPersonService` implements `PersonService`,
- `DemoPersonService` is annotated Spring's `@Service` annotation: tells Spring this is a service that needs to be instantiated. A Service is a long running instance which lives as long as the web application is running,
- The service is `package protected`: the class is not accessible outside the package,
- Only the `PersonService` interface is public.

Now, modify the `PersonController` to delegate the work to the service defined above:

```java

package com.octoperf.demo;

import com.octoperf.demo.service.PersonService;
import lombok.AllArgsConstructor;
import lombok.NonNull;
import lombok.experimental.FieldDefaults;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import static lombok.AccessLevel.PACKAGE;
import static lombok.AccessLevel.PRIVATE;

@RestController
@RequestMapping("/person")
@AllArgsConstructor(access = PACKAGE)
@FieldDefaults(level = PRIVATE, makeFinal = true)
class PersonController {
  @NonNull
  PersonService persons;

  @GetMapping("/johnsmith")
  public Person hello() {
    return persons.johnSmith();
  }

  @PostMapping("/hello")
  public String postHello(@RequestBody final Person person) {
    return persons.hello(person);
  }
}
```

A few things have changed:

- `PersonController` is annotated with `@AllArgsConstructor` and `@FieldDefaults` lombok annotations: tells lombok to create a constructor for required params, and mark all fields as `private final` (making them all required). The controller is immutable,
- `@NonNull PersonService persons;`: the service is defined as a field of `PersonController`, and should not be null (nullcheck code written by lombok).

The generated code looks like:

```java

@RestController
@RequestMapping({"/person"})
class PersonController {
  @NonNull
  private final PersonService persons;

  @GetMapping({"/johnsmith"})
  public Person hello() {
    return this.persons.johnSmith();
  }

  @PostMapping({"/hello"})
  public String postHello(@RequestBody Person person) {
    return this.persons.hello(person);
  }

  @ConstructorProperties({"persons"})
  PersonController(@NonNull PersonService persons) {
    if (persons == null) {
      throw new NullPointerException("persons");
    } else {
      this.persons = persons;
    }
  }
}

```

You see how powerful `Lombok` is! Your application logic is inside the `PersonService` implementation. Spring automatically:

- Instantiated the `DemoPersonService`,
- Instantiated the `PersonController` by providing an instance of `PersonService` to its constructor.

Once you understand all those simple concepts, designing even really big web applications does not differ that much from the model above. It's all about services being exposed through Rest or Web Endpoints. And those services themselves can delegate to sub-services.

## RestAssured Unit Test

Now, it's time to write a unit test to check our Rest Endpoint. Testing is a critical point to avoid regressions when the code evolves.

First, add the [RestAssured](https://github.com/rest-assured/rest-assured) test dependency:

```xml

<dependency>
  <groupId>io.rest-assured</groupId>
  <artifactId>rest-assured</artifactId>
  <version>3.0.7</version>
  <scope>test</scope>
</dependency>

```

Then, let's write a JUnit which performs a Rest call using **RestAssured**:

```java

package com.octoperf;

import io.restassured.RestAssured;
import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import static io.restassured.RestAssured.get;
import static io.restassured.RestAssured.preemptive;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = { DemoApplication.class }, webEnvironment = RANDOM_PORT)
public class PersonControllerTest {

  @Value("${local.server.port}")
  private int port;

  @Before
  public void setUp() {
    RestAssured.authentication = preemptive().basic("admin", "passw0rd");
  }

  @Test
  public void shouldSayHello() {
    get("http://localhost:" + port + "/person/johnsmith")
      .then()
      .assertThat()
      .statusCode(200)
      .body("firstname", Matchers.equalTo("John"))
      .and()
      .body("lastname", Matchers.equalTo("Smith"));
  }
}

```

The Junit above does several things:

- Runs with `SpringRunner`: the unit test should be embedded into a Spring application,
- `@SpringBootTest`: specifies the bootstrap application to use, and setup a web context on a randomly available port,
- `@Value("${local.server.port}")`: autowires the random web application port, so we can reuse it when performing the Rest call through **RestAssured**,
- `RestAssured.authentication = preemptive().basic("admin", "passw0rd");`: configures *RestAssured* to use **Basic Authentication** as our endpoints have been secured previously.

Now you have a unit-test which automatically spins up an embedded web server with your controller and your services inside. The unit test then performs a real HTTP request to the controller endpoint `/person/johnsmith`, and checks the content of the response.

I suggest you deep dive into the [RestAssured documentation](https://github.com/rest-assured/rest-assured/wiki/GettingStarted) to further explore testing Rest Endpoints.

## Retrofit Unit Test

Alternatively to **RestAssured**, you can use [Retrofit](https://lysine.dev/retrofit/). Retrofit is a type-safe HTTP client for Java. In fact, you could use any Java rest-client that suits your needs.

First, add the test dependency:

```xml

<dependency>
  <groupId>com.squareup.retrofit2</groupId>
  <artifactId>retrofit</artifactId>
  <version>2.3.0</version>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>com.squareup.retrofit2</groupId>
  <artifactId>converter-jackson</artifactId>
  <version>2.3.0</version>
  <scope>test</scope>
</dependency>

```

Please make sure to use latest version at the time you are reading this. The code may vary depending on the future evolutions of the library.

Let's now write the API Interface using Retrofit:

```java

package com.octoperf.demo;

import retrofit2.Call;
import retrofit2.http.GET;

public interface PersonApi {

  @GET("/person/johnsmith")
  Call<Person> johnSmith();
}


```

Then, we need a Basic Authentication request interceptor:

```java

package com.octoperf.demo;

import okhttp3.Credentials;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;

import java.io.IOException;

final class BasicAuthInterceptor implements Interceptor {

  private final String credentials;

  BasicAuthInterceptor(final String user, final String password) {
    this.credentials = Credentials.basic(user, password);
  }

  @Override
  public Response intercept(Chain chain) throws IOException {
    final Request request = chain.request();
    final Request authenticatedRequest = request.newBuilder()
      .header("Authorization", credentials).build();
    return chain.proceed(authenticatedRequest);
  }

}

```

And finally, use this interface within the unit test:

```java
package com.octoperf.demo;

import com.fasterxml.jackson.databind.ObjectMapper;
import okhttp3.Credentials;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import retrofit2.Retrofit;
import retrofit2.converter.jackson.JacksonConverterFactory;

import java.io.IOException;

import static org.junit.Assert.assertEquals;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = { DemoApplication.class }, webEnvironment = RANDOM_PORT)
public class PersonControllerRetrofitTest {

  @Value("${local.server.port}")
  private int port;

  private Retrofit retrofit;

  @Before
  public void setUp() {
    final OkHttpClient client = new OkHttpClient.Builder()
      .addInterceptor(new BasicAuthInterceptor("admin", "passw0rd"))
      .build();

    retrofit = new Retrofit.Builder()
      .baseUrl("http://localhost:"+port)
      .client(client)
      .addConverterFactory(JacksonConverterFactory.create(new ObjectMapper()))
      .build();
  }

  @Test
  public void shouldSayHello() throws IOException {
    final PersonApi api = retrofit.create(PersonApi.class);
    final Person person = api.johnSmith().execute().body();
    assertEquals("John", person.getFirstname());
    assertEquals("Smith", person.getLastname());
  }
}

```

It's up to you to choose the Rest client you're the more comfortable with. There are many other clients available (Feign, Resteasy, Spring RestTemplate, UniRest and more).

## Exception Handler

Spring offers a simple centralized error handling mechanism using `@ControllerAdvice` annotation. Here is an example.

First, let's create a `DemoException` in package `com.octoperf.demo.exception`:

```java

package com.octoperf.demo.exception;

public class DemoException extends Exception {

}

```

Now, we're going to create the `DemoExceptionHandler`:

```java

package com.octoperf.demo.exception;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

@ControllerAdvice
class DemoExceptionHandler extends ResponseEntityExceptionHandler {

  @ExceptionHandler({ DemoException.class })
  protected ResponseEntity<Object> handleNotFound(
    Exception ex, WebRequest request) {
    return handleExceptionInternal(ex, "Demo Exception Encountered",
      new HttpHeaders(), HttpStatus.NOT_FOUND, request);
  }
}


```

The handler basically sends an Http 404 not found when the `DemoException` is thrown by any Spring MVC Controller.

Finally, enrich our `PersonController` by adding a endpoint to simulate this exception:

```java

@GetMapping("/exception")
public void exception() throws DemoException {
  throw new DemoException();
}

```

Here is the output when executing an HTTP request to this endpoint with `curl`:

```bash

ubuntu@desktop:~$ curl -v http://admin:passw0rd@localhost:8081/person/exception
*   Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 8081 (#0)
* Server auth using Basic with user 'admin'
> GET /person/exception HTTP/1.1
> Host: localhost:8081
> Authorization: Basic YWRtaW46cGFzc3cwcmQ=
> User-Agent: curl/7.47.0
> Accept: */*
> 
< HTTP/1.1 404 
< Set-Cookie: JSESSIONID=B1AE72512170F07C4D440BF87167C014; Path=/; HttpOnly
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
< Content-Type: text/plain;charset=UTF-8
< Content-Length: 26
< Date: Tue, 03 Apr 2018 15:12:14 GMT
< 
* Connection #0 to host localhost left intact
Demo Exception Encountered

```

The server properly catches the exception and returns the HTTP 404 as stated in the `DemoExceptionHandler`. This way, you can control how the application behaves for each exception being thrown by any Rest Controller.

Even multiple `@ControllerAdvice` annotated classes can be defined. Each can be responsible for handling exceptions thrown by a particular part of your web application.

## Final Words

We have only scratched the surface of what's possible to do with **Spring Boot** in this tutorial. **Spring** is a powerful aggregate of dozen of libraries which make developing web applications as easy as it can be. Make sure to explore them each time you have a need, maybe there is a library to do it for you!

> Full source code is available at [Spring Boot 2 Demo](https://github.com/jloisel/spring-boot2-demo) on Github.

*Feel free to share your own code examples if you feel like something is missing in this article!*
