# 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 的基本路径进行其请求映射。

{% code title="程序清单 6.7 为控制器应用 Spring Data REST 基础路径" %}

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

{% endcode %}

尽管 @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` 时，它仍然不会出现在超链接列表中。让我们解决这个问题。
