6.3.3 添加用户端点

Spring Data REST 非常擅长创建针对 Spring Data 存储库执行 CRUD 操作的端点。但是有时需要脱离默认的 CRUD API,并创建一个能够解决核心问题的端点。

没有任何东西可以阻止你在 @RestController 注解的 bean 中实现任何想要的端点,来补充 Spring Data REST 自动生成的内容。实际上,可以重新使用本章前面的 DesignTacoController,它仍然可以与 Spring Data REST 提供的端点一起工作。

但是,当你编写自己的 API 控制器时,它们的端点似乎以以下两种方式与 Spring Data REST 端点分离:

  • 自己的控制器端点没有映射到 Spring Data REST 的基本路径下。可以强制它们的映射以任何想要的基本路径作为前缀,包括 Spring Data REST 基本路径,但是如果基本路径要更改,需要编辑控制器的映射来匹配。

  • 在自己的控制器中定义的任何端点都不会自动作为超链接包含在 Spring Data REST 端点返回的资源中。这意味着客户端将无法发现具有关系名称的自定义端点。

让我们首先解决关于基本路径的问题。Spring Data REST 包括 @RepositoryRestController,这是一个用于控制器类的新注解,其映射应该采用与为 Spring Data REST 端点配置的基本路径相同的基本路径。简单地说,@RepositoryRestController 注解的控制器中的所有映射的路径都将以 spring.data.rest.base-path 的值为前缀(已配置为 /api)。

将创建一个只包含 recentTacos() 方法的新控制器,而不是重新启用 DesignTacoController,它有几个不需要的处理程序方法。下一个程序清单中的 RecentTacosController 使用 @RepositoryRestController 进行注解,以采用 Spring Data REST 的基本路径进行其请求映射。

程序清单 6.7 为控制器应用 Spring Data REST 基础路径
package tacos.web.api;

import static org.springframework.hateoas.mvc.ControllerLinkBuilder.*;

import java.util.List;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.rest.webmvc.RepositoryRestController;
import org.springframework.hateoas.Resources;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;

import tacos.Taco;
import tacos.data.TacoRepository;

@RepositoryRestController
public class RecentTacosController {
    
    private TacoRepository tacoRepo;
    
    public RecentTacosController(TacoRepository tacoRepo) {
        this.tacoRepo = tacoRepo;
    }
    
    @GetMapping(path="/tacos/recent", produces="application/hal+json")
    public ResponseEntity<Resources<TacoResource>> recentTacos() {
        PageRequest page = PageRequest.of(
            0, 12, Sort.by("createdAt").descending());
        List<Taco> tacos = tacoRepo.findAll(page).getContent();
    
        List<TacoResource> tacoResources =
            new TacoResourceAssembler().toResources(tacos);
        Resources<TacoResource> recentResources =
            new Resources<TacoResource>(tacoResources);
        
        recentResources.add(
            linkTo(methodOn(RecentTacosController.class).recentTacos())
            .withRel("recents"));
        
        return new ResponseEntity<>(recentResources, HttpStatus.OK);
    }
}

尽管 @GetMapping 映射到路径 /tacos/recent,但是类级别的 @RepositoryRestController 注解将确保它以 Spring Data REST 的基本路径作为前缀。正如所配置的,recentTacos() 方法将处理 /api/tacos/recent 的 GET 请求。

需要注意的一件重要事情是,尽管 @RepositoryRestController 的名称与 @RestController 类似,但它的语义与 @RestController 不同。具体来说,它不确保从处理程序方法返回的值被自动写入响应体。因此,需要使用 @ResponseBody 对方法进行注解,或者返回一个包装响应数据的 ResponseEntity。

使用 RecentTacosController,对 /api/tacos/recent 的请求将返回最多 15 个最近创建的 tacos,而不需要在 URL 中对参数进行分页和排序。但是,当请求 /api/tacos 时,它仍然不会出现在超链接列表中。让我们解决这个问题。

最后更新于