为了避免分散注意力,在本书中,我们不再浪费时间深入探究Spring Data REST所创建的每个端点和可选项。但是,我们需要知道,它还支持端点的POST、PUT和DELETE方法。也就是说,你可以发送POST请求至“/ingredients”来创建新的配料,也可以发送DELETE请求到“/ingredients/FLTO”,以便于从菜单中删除玉米面薄饼。
我们想做的另外一件事可能就是为API设置一个基础路径,这样它们会有不同的端点,避免与我们所编写的控制器产生冲突(实际上,如果我们不删除前面自己创建的IngredientsController,就将会干扰Spring Data REST提供的“/ingredients”端点)。为了调整API的基础路径,我们可以设置spring.data.rest.base-path属性:
1 2 3 4
spring: data: rest: base-path: /api
这项配置会将Spring Data REST端点的基础路径设置为“/api”。现在,配料端点将会变成“/api/ingredients”。我们通过请求taco列表来验证一下这个新的基础路径:
哦,天啊!它并没有按照预期那样运行。我们有了Ingredient实体和IngredientRepository接口之后,Spring Data REST就会暴露“/api/ingredients”。我们也有Taco实体和TacoRepository接口,为什么SpringData REST没有为我们生成“/api/tacos”端点呢?
6.3.1 调整资源路径和关系名称
实际上,Spring Data确实为我们提供了处理taco的端点。虽然Spring Data REST非常聪明,但是在暴露taco端点的时候出现了一点问题。
当为Spring Data repository创建端点的时候,Spring Data REST会尝试使用相关实体类的复数形式。对于Ingredient实体来说,端点将会是“/ingredients”,对于Order和User实体,端点将会是“/orders”和“/users”。到目前为止,一切运行良好。但有些场景下,比如遇到“taco”的时候,它获取到这个单词,为其生成的复数形式就不太正确了。实际上,Spring Data REST将“taco”的复数形式计算成了“tacoes”,所以,为了向taco发送请求,我们必须将错就错,请求“/api/tacoes”地址:
Spring Data REST能够很好地为执行CRUD操作的Spring Data repository创建端点。但有时候,我们需要脱离默认的CRUD API,创建处理核心问题的端点。
我们当然可以在带有@RestController注解的bean中实现任意的端点,以此来补充Spring Data REST端点的不足。实际上,我们可以重新启用本章前面提到的DesignTacoController,它依然可以与Spring Data REST提供的端点一起运行。
但是在编写自己的API控制器时,它们的端点在有些方面会与Spring Data REST的端点脱节:
我们自己的控制器端点没有映射到Spring Data REST的基础路径下。虽然我们可以强制要求它们映射到任意前缀作为基础路径,包括使用Spring DataREST的基础路径,但是如果基础路径发生变化的话,我们还需要修改控制器的映射,以便于与其匹配。
在自己控制器所定义的端点中,返回资源时并不会自动包含超链接,与Spring Data REST所返回的资源是不同的。这意味着,客户端无法通过关系名发现自定义的端点。
我们首先来解决基础路径的问题。Spring Data REST提供了一个新的注解@RepositoryRestController,这个注解可以用到控制器类上,这样控制器类所有映射的基础路径就会与Spring Data REST端点配置的基础路径相同。简而言之,在使用@RepositoryRestController注解的控制器中,所有映射将会具有和spring.data.rest.base-path属性值一样的前缀(我们之前将这个属性的值配置成了“/api”)。
在这里我们会创建一个只包含recentTacos()方法的新控制器,而不是修改已有的DesignTacoController,因为这个旧的控制器包含了多个我们不再需要的方法。程序清单6.7中的RecentTacosController带有@RepositoryRestController注解,表明它会将Spring Data REST的基础路径用到自己的请求映射上。
虽然@GetMapping映射到了“/tacos/recent”路径,但是类级别的@Repository RestController注解会确保这个路径添加Spring Data REST的基础路径作为前缀。按照我们的配置,recentTacos()方法将会处理针对“/api/tacos/recent”的GET请求。
通过声明资源处理器(resource processor)bean,我们可以为Spring DataREST自动包含的链接列表继续添加链接。Spring Data HATEOAS提供了一个ResourceProcessor接口,能够在资源通过API返回之前对其进行操作。对于我们的场景来说,需要一个ResourceProcessor实现,为PagedResources<Resource<Taco>>类型的资源(也就是“/api/tacos”端点所返回的类型)添加recents链接。程序清单6.8展现了定义这种ResourceProcessor的bean声明方法。
超媒体作为应用状态引擎(Hypermedia as the Engine of Application State,HATEOAS)是一种创建自描述API的方式。API所返回的资源中会包含相关资源的链接,客户端只需要了解最少的API URL信息就能导航整个API。这种方式能够掌握API所提供的资源之间的关系,客户端能够基于API的URL中所发现的关系对它们进行遍历。
Spring HATEOAS提供了两个主要的类型来表示超链接资源:Resource和Resources。Resource代表一个资源,而Resources代表资源的集合。这两种类型都能携带到其他资源的链接。当从Spring MVC REST控制器返回时,它们所携带的链接将会包含到客户端所接收到的JSON(或XML)中。
这并不一本关于Angular的书,所以本章中的代码将会主要关注后端的Spring代码。我只会给出适当的Angular代码,以便于让你了解客户端是如何运行的。但是,请放心,完整的代码集会包括Angular前端,它们都是本书配套代码的一部分。如果你有兴趣,可以阅读Jeremy Wilken编写的Angular in Action(Manning,2018)以及Yakov Fain和Anton Moiseev编写的AngularDevelopment with TypeScript, Second Edition(Manning,2018)。
@GetMapping public String ordersForUser( @AuthenticationPrincipal User user, Model model) { model.addAttribute("orders", orderRepo.findByUserOrderByPlacedAtDesc(user)); return"orderList"; }
然后,选择“Create Metadata for …”选项来为属性添加元数据(会在additional-spring- configuration-metadata.json文件中进行添加),如果文件还不存在,将会自动创建该文件。
对于taco.orders.pageSize属性来说,我们可以通过如下的JSON为其添加元数据:
1 2 3 4 5 6 7 8 9 10
{ "properties":[ { "name":"taco.orders.page-size", "type":"java.lang.String", "description": "Sets the maximum number of orders to display in a list." } ] }