如何在@RestController 中仅覆盖 1 个 Http 方法 (POST),但让 Spring 使用标准 HATEOAS 响应处理所有其他 Http 方法

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

我想在 Spring 中公开一些 REST 资源,默认情况下我可以让 Spring Boot 处理这个,这会生成一个很好的 HATEOAS 响应,我得到所有东西 'free' .

现在我需要在某个资源被 POSTED 时执行一些逻辑,所以我实现了自己的 @RestController class 并重写了 POST 方法,如下所示:

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));
    }
}

我使用从 ResourceSupport 继承的 class,并在其中生成 HATEOAS 响应所需的链接。到目前为止一切顺利,自定义工作量不大。

问题是,如果我现在尝试获取所有项目,我会收到此消息:

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

我真的不想也实现其他方法,Spring Boot 的默认设置很棒。但是我现在是否也被迫实施这些并使我的整个 HATEOAS 响应每次都与我的域对象更改保持同步?这是我喜欢的功能 'out of the box'。

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

你不能用 @RestController 做到这一点。如 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

没有明确提及,但使用 @RepositoryRestController 注释您的控制器允许您为一个端点定义自定义行为,同时保留 Spring 自动生成的所有其他端点。

对于旧版本,请注意您可以在 method level only 处使用 @RequestMapping 注释。问题已在 2.4 M1 (Gosling)、2.3.1 (Fowler SR1)、2.1.6 (Dijkstra SR6) 中修复。

您的示例变为:

@RepositoryRestController
@RequestMapping(/project)
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). path can be something like "/prj" (beginning with slash, because on class level, there is no suffix slash)
    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));
    }
}