Spring RESTful service

Laur Spilca Youtube Tutorial series

spring docs

Spring Restful service is a popular backend application. The architecture usually is made up with 3 layers: Controller, Service, and Repository.

Configuration setup

@SpringBootApplication - define the app root

  • The start point of program
  • It’s with @ComponentScan - tell spring to create beans in which the classes have been annotated with.

Spring RESTful Service architecture

Spring RESTful service usually has this architecture.

  • Controller - Responsible for exposing the API to the external consumer. E.g., In web service, it’s the endpoint. like POST /user/create.
  • Service - A layer that conduct business logic. It’s also the layer that talks to the repository.
  • Repository - The layer that talks to database. This defines how to perform CRUD to database.

@Controller, RestController - Controller exposes API

This annotation serves as a specialization of @Component, allowing for implementation classes to be auto-detected through component scanning.


  • @RestController - it combines @Controller and @ResponseBody.
  • @Controller is a stereotype annotation
  • @ResponseBody tells spring that instead of finding the static web templates, e.g. thymeleaf or JSP, response the content in this method/class directly.
  • @PathVariable - to specify a variable url, e.g. /api/{userId}


It is typically used in combination with annotated handler methods based on the @Controller and @RequestMapping.

Furthermore, use @RestController as the shorthand of @Controller and @RequestMapping

@RequestMapping - the parent of mapping annotation

Used to annotate either the class or methods for URLs.

  • Class-level - for specific request path
  • method level - for a specific HTTP method request (“GET”/“POST”) or specific HTTP request parameters.

@RequestMapping() can use path or value to map. See Spring alias

public class WebController {

	@RequestMapping(method = RequestMethod.GET, path = "/hello")
	public String hello() {
	    return "Hello World!";

	@RequestMapping(method = RequestMethod.POST, path = "/create")
	public String create() {
		return new Object();


Class-level makes the entire mapping to /api. So that when clients want to call hello(), the url path is GET /api/hello. Method-level maps a specific path, and a particular HTTP methods.

Another example

public class WebController {
	@RequestMapping(method = RequestMethod.GET, path = "/hello")
	public String hello() {
	    return "Hello World!";

No class-level @RequestMapping, therefore the path for this method is http://hostname.com/hello

@GetMapping, @PostMapping, etc, shorthands
  • @GetMapping
  • @PostMapping
  • @PutMapping
  • @DeleteMapping
  • @PatchMapping

Boiler template codes can be shorter:

// from
@RequestMapping(method = RequestMethod.GET, path = "/hello")
// to
@GetMapping(path = "/hello")
@RequestMapping quick example with path, header, body, and response

You can access everything in the method

@PostMapping(path = "/test/{name}")
public String test(@PathVariable("name") String name,
                    @RequestHeader String a,
                    @RequestHeader String b,
                    @RequestHeader String c,
                    @RequestBody    String body,
                    HttpServletResponse reponse) {
    return a + b + c + body + name;
@PathVariable - URI templates

indicate that a method parameter should be bound to the value of a URI template variable.

Note that the parameter name should match the URI variable.

@RequestMapping(method = RequestMethod.GET, path = "/hello/{name}")
public String hello(@PathVariable("name") String name) {
    return "Hello " + name + "!";

Multiple parameters

@RequestMapping(method = RequestMethod.GET, path = "/hello/{firstname}/{familyname}")
public String hello(@PathVariable("firstName") String firstName, 
				   @PathVariable("familyName") String familyName) {
    return "Hello " + firstName + " " + familyName + "!";

Class-level GET /owners/{ownerId}/pets/{petId}

public class RelativePathUriTemplateController {

  public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) {    
    // implementation omitted

Method parameters that are decorated with the @PathVariable annotation can be of any simple type such as int, long, Date… This can be customized through WebDataBinder

@RequestParam - access the params of /api?key=value

spring request param

Extract query parameters, form parameters, and even files from the request.


  • value, alias “name” - name of param
  • defaultValue - give a default value
  • required - Method parameters annotated with @RequestParam are required by default. Set to false means otherwise.
Example 1: basic use
public String getFoos(@RequestParam String id) {
    return "ID: " + id;
GET localhost:8080/api/foos?id=123
<Response 200> ID: 123
Example 2: different param name
public String addFoo(@RequestParam(name = "id") String fooId) { 
    return "ID: " + fooId;
GET localhost:8080/api/foos?id=123
<Response 200> ID: 123
Example 3: Set optional parameters
public String getFoos(@RequestParam(required = false) String id) { 
    return "ID: " + id;

With parameter

GET localhost:8080/api/foos?id=abc
ID: abc

Without parameter

ID: null

Hence, wrap it with Optional will be better:

public String getFoos(@RequestParam Optional<String> id){
    return "ID: " + id.orElseGet(() -> "not provided");
GET http://localhost:8080/api/foos 
ID: not provided
Example 4: Use map to capture all
public String updateFoos(@RequestParam Map<String,String> allParams) {
    return "Parameters are " + allParams.entrySet();
curl -X POST -F 'name=abc' -F 'id=123' http://localhost:8080/api/foos
Parameters are {[name=abc], [id=123]}
Example 5: array like value
public String getFoos(@RequestParam List<String> id) {
    return "IDs are " + id;
GET http://localhost:8080/api/foos?id=1,2,3
IDs are [1,2,3]
### AND
IDs are [1,2]
@RequestBody - JSON unmarshal the request to object

There’s no “request body” in GET method. Only in other request methods, there’s body.

Spring @RequestBody annotation maps the HttpRequest body to a transfer or domain object, enabling automatic deserialization of the inbound HttpRequest body onto a Java object. It’s assuming an appropriate one is specified.

Commonly, developers will use the ClassForm to transit the input request.

public class PersonForm {
    private String name;
    private int age;
    // constructor, getters and setters
@PostMapping(path = "/goodbye")
public String goodbye(@RequestBody PsersonForm p) {
    return "Goodbye, " + p.getName() + "!";


POST http://localhost:8080/goodbye/

{ "name" : "Bill" }
Goodbye, Bill!
Setting the response content type

explicitly set the response content type in @RequestMapping(produce = type)

  • produces = MediaType.APPLICATION_JSON_VALUE)
  • produces = MediaType.APPLICATION_XML_VALUE)

Example, respond with XML

@PostMapping(value = "/content", produces = MediaType.APPLICATION_XML_VALUE)
public ResponseTransfer postResponseXmlContent(
  @RequestBody LoginForm loginForm) {
    return new ResponseTransfer("XML Content!");
# Request:
curl -i \
-H "Accept: application/xml" \
-H "Content-Type:application/json" \
-X POST --data
  '{"username": "johnny", "password": "password"}' \

# Response:
HTTP/1.1 200
Content-Type: application/xml
Transfer-Encoding: chunked
Date: Thu, 20 Feb 2020 19:43:19 GMT

<ResponseTransfer><text>XML Content!</text></ResponseTransfer>
@RequestHeader - access the headers

The @RequestHeader allows a method parameter to be bound to a request header.

public void getHeaderInfo(@RequestHeader("Accept-Encoding") String encoding,
                          @RequestHeader("Keep-Alive") long keepAlive)  {

Response and @ResponseBody

@ResponseBody tells spring that instead of finding the static web templates, e.g. thymeleaf or JSP, response the content in this method/class directly.

byte[], image, set, map, etc can be returned. Spring will automatically marshal it.

Return an object
@GetMapping(path = "/get")
public Person goodbye() {
    Person p = new Person("Bill");
    return p;


{ "name" : "Bill" }
Return a List
@GetMapping(path = "/getall")
public List<Person> goodbye() {
    Person p1 = new Person("Bill");
    Person p2 = new Person("John");
    return Arrays.asList(p1, p2);


        "name" : "Bill"
        "name" : "John"
Return a Map
public Map<String, String> all(@RequestHeader Map<String, String> map) {
    return map;

Set the HttpServletResponse status

We can set the status by this, Spring framework will take care of it. You can also return a body if you wish.

@GetMapping(path = "/status")
public void statusTest(HttpServletResponse reponse) {
    // set the status you preferred

@Service - stereotype annotation for business logic

Taking care of business logic. It performs the manipulation of an entity, an object, or a desired behaviour.

make use of:

  • @Transactional - in service layer, a logic might perform multiple database transaction. Use this for ACID.

Common example:

public class CarService {
    private final CarRepository repository;
    //  ...
    public Car findCarById(long carId) {
        return repository.findById(carId)
                         .orElseThrow(() -> new CarNotFoundException(carId));


@Repository - stereotype annotation for interaction with data persistence

The best way to guarantee that your Data Access Objects (DAOs) or repositories provide exception translation is to use the @Repository annotation. This annotation also allows the component scanning support to find and configure your DAOs and repositories without having to provide XML configuration entries for them.

public class SomeMovieFinder implements MovieFinder {
    // ...

One very common is to combine with Spring Data JPA:

public interface CarRepository extends JpaRepository<Car, Long> {
    // ...