Home How to override only 1 Http Method (POST) in a @RestController, but let Spring handle all the other Http methods with a standard HATEOAS response
Reply: 1

How to override only 1 Http Method (POST) in a @RestController, but let Spring handle all the other Http methods with a standard HATEOAS response

Sebastiaan van den Broek
1#
Sebastiaan van den Broek Published in 2018-02-14 12:14:56Z

I want to expose some REST resources in Spring, by default I can just let Spring Boot deal with this and this generates a nice HATEOAS response where I get everything 'free'.

Now I need to perform some logic when a certain resource is being POSTED, so I implemented my own @RestController class and override the POST method as follows:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;

import java.net.URI;
import java.security.Principal;

@RestController
@RequestMapping(Constants.PROJECTSPATH)
public class ProjectController {
    @Autowired private ProjectRepository projectRepo;
    @Autowired private ProjectRoleRepository projectRoleRepo;
    @Autowired private AccountRepository accountRepo;

    @RequestMapping(method = RequestMethod.POST)
    public ResponseEntity<?> addProject(@RequestBody Project projectFromRequest, Principal principal) {
        Project project = projectRepo.save(projectFromRequest);
        Account currentAccount = accountRepo.findByUsername(principal.getName());
        ProjectRole ownerRole = new ProjectRole(project, currentAccount, ProjectRoleEnum.OWNER);
        projectRoleRepo.save(ownerRole);

        final URI uri =
                MvcUriComponentsBuilder.fromController(getClass())
                        .path("/{id}")
                        .buildAndExpand(project.getId())
                        .toUri();
        return ResponseEntity.created(uri).body(new ProjectResource(project));
    }
}

I use a class inheriting from ResourceSupport and in there I generate the required links for a HATEOAS response. So far so good, that isn't too much custom work.

The problem is that if I now just try to GET all projects, I get this message:

{"timestamp":1518610270403,"status":405,"error":"Method Not Allowed","exception":"org.springframework.web.HttpRequestMethodNotSupportedException","message":"Request method 'GET' not supported","path":"/projects"}

I really prefer not to implement the other methods too, the default done by Spring Boot is great. But am I now forced to implement these as well and keep my entire HATEOAS response up to date with my domain object changes every time? That is functionality that I love 'out of the box'.

Marc Tarin
2#
Marc Tarin Reply to 2018-02-14 16:05:16Z

How to override only 1 Http Method (POST) in a @RestController, but let Spring handle all the other Http methods with a standard HATEOAS response

You can't do that with a @RestController. As stated in the Spring Data REST reference:

Sometimes you may want to write a custom handler for a specific resource. To take advantage of Spring Data REST’s settings, message converters, exception handling, and more, use the @RepositoryRestController annotation instead of a standard Spring MVC @Controller or @RestController

It is not explicitly mentionned, but annotating your controller with @RepositoryRestController allows you to define a custom behavior for one endpoint while keeping all the other endpoints that Spring automatically generates. One important thing though, is to use the @RequestMapping annotation at the method level only.

Your example becomes:

@RepositoryRestController
public class ProjectController {

    @Autowired private ProjectRepository projectRepo;
    @Autowired private ProjectRoleRepository projectRoleRepo;
    @Autowired private AccountRepository accountRepo;

    @PostMapping(Constants.PROJECTSPATH) // @PostMapping is a shortcut for @RequestMapping(method = RequestMethod.POST)
    public ResponseEntity<?> addProject(@RequestBody Project projectFromRequest, Principal principal) {
        Project project = projectRepo.save(projectFromRequest);
        Account currentAccount = accountRepo.findByUsername(principal.getName());
        ProjectRole ownerRole = new ProjectRole(project, currentAccount, ProjectRoleEnum.OWNER);
        projectRoleRepo.save(ownerRole);

        final URI uri =
                MvcUriComponentsBuilder.fromController(getClass())
                        .path("/{id}")
                        .buildAndExpand(project.getId())
                        .toUri();
        return ResponseEntity.created(uri).body(new ProjectResource(project));
    }
}
You need to login account before you can post.

About| Privacy statement| Terms of Service| Advertising| Contact us| Help| Sitemap|
Processed in 0.333375 second(s) , Gzip On .

© 2016 Powered by mzan.com design MATCHINFO