6.3 启用数据为中心的服务
6.3 启用以数据为中心的服务
正如在第
要开始使用
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
信不信由你,这就是在一个已经将
要尝试
例如,可以通过向 /ingredients
接口发出
$ curl localhost:8080/ingredients
{
"_embedded" : {
"ingredients" : [
{
"name" : "Flour Tortilla",
"type" : "WRAP",
"_links" : {
"self" : {"href" : "http://localhost:8080/ingredients/FLTO"},
"ingredient" : {
"href" : "http://localhost:8080/ingredients/FLTO"
}
}
},
...
]
},
"_links" : {
"self" : {
"href" : "http://localhost:8080/ingredients"
},
"profile" : {
"href" : "http://localhost:8080/profile/ingredients"
}
}
}
哇!通过向构建中添加一个依赖项,不仅获得了
$ curl localhost:8080/ingredients/FLTO
{
"name" : "Flour Tortilla",
"type" : "WRAP",
"_links" : {
"self" : {
"href" : "http://localhost:8080/ingredients/FLTO"
},
"ingredient" : {
"href" : "http://localhost:8080/ingredients/FLTO"
}
}
}
为了避免过于分散注意力,在本书中我们不会浪费太多时间来深入研究/ingredients
接口发送/indegredient/FLTO
接口发送
可能想要做的一件事是为/ingredients
端点
spring:
data:
rest:
base-path: /api
这将设置/api
。因此,/api/ingredients
。现在,通过请求一个
$ curl http://localhost:8080/api/tacos
{
"timestamp": "2018-02-11T16:22:12.381+0000",
"status": 404,
"error": "Not Found",
"message": "No message available",
"path": "/api/tacos"
}
噢?这并没有达到预期的效果。有一个/api/ingredients
端点。因此,如果有一个/api/tacos
端点呢?
6.3.1 调整资源路径和关系名称
实际上,
在为/ingredients
。对于/orders
和 /users
。到目前为止,一切顺利。
但有时,比如 “taco”,它会在一个字母上出错,这样复数形式就不太正确了。事实证明,/api/tacoes
:
% curl localhost:8080/api/tacoes
{
"_embedded" : {
"tacoes" : [ {
"name" : "Carnivore",
"createdAt" : "2018-02-11T17:01:32.999+0000",
"_links" : {
"self" : {
"href" : "http://localhost:8080/api/tacoes/2"
},
"taco" : {
"href" : "http://localhost:8080/api/tacoes/2"
},
"ingredients" : {
"href" : "http://localhost:8080/api/tacoes/2/ingredients"
}
}
}]
},
"page" : {
"size" : 20,
"totalElements" : 3,
"totalPages" : 1,
"number" : 0
}
}
你可能想知道我怎么知道 “taco” 会被误拼成 “tacoes”。事实证明,
$ curl localhost:8080/api
{
"_links" : {
"orders" : {
"href" : "http://localhost:8080/api/orders"
},
"ingredients" : {
"href" : "http://localhost:8080/api/ingredients"
},
"tacoes" : {
"href" : "http://localhost:8080/api/tacoes{?page,size,sort}",
"templated" : true
},
"users" : {
"href" : "http://localhost:8080/api/users"
},
"profile" : {
"href" : "http://localhost:8080/api/profile"
}
}
}
可以看到,
好消息是,不必接受
@Data
@Entity
@RestResource(rel="tacos", path="tacos")
public class Taco {
...
}
"tacos": {
"href": "http://localhost:8080/api/tacos{?page,size,sort}",
"templeted": true
}
这还可以对端点的路径进行排序,这样就可以针对 /api/tacos
接口发起请求来使用
说到排序,让我们看看如何对
6.3.2 分页和排序
你可能注意到了在/api/tacos
这种集合资源请求的接口来说,将会从第一页返回每页
举个例子,要请求
$ curl "localhost:8080/api/tacos?size=5"
假设有多于
$ curl "localhost:8080/api/tacos?size5&page=1"
注意&
符号上出错,这就是为什么我在前面的
可以使用字符串操作将这些参数添加到
"_links" : {
"first" : {
"href" : "http://localhost:8080/api/tacos?page=0&size=5"
},
"self" : {
"href" : "http://localhost:8080/api/tacos"
},
"next" : {
"href" : "http://localhost:8080/api/tacos?page=1&size=5"
},
"last" : {
"href" : "http://localhost:8080/api/tacos?page=2&size=5"
},
"profile" : {
"href" : "http://localhost:8080/api/profile/tacos"
},
"recents" : {
"href" : "http://localhost:8080/api/tacos/recent"
}
}
有了这些链接,
$ curl "localhost:8080/api/tacos?sort=createAt,desc?page=0&size=12"
这里,
这正是/design/recent
端点大致相同。
不过有个小问题,需要对/design/recent
端点一样,那就更好了。
6.3.3 添加用户端点
没有任何东西可以阻止你在
但是,当你编写自己的
- 自己的控制器端点没有映射到
Spring Data REST 的基本路径下。可以强制它们的映射以任何想要的基本路径作为前缀,包括Spring Data REST 基本路径,但是如果基本路径要更改,需要编辑控制器的映射来匹配。 - 在自己的控制器中定义的任何端点都不会自动作为超链接包含在
Spring Data REST 端点返回的资源中。这意味着客户端将无法发现具有关系名称的自定义端点。
让我们首先解决关于基本路径的问题。/api
将创建一个只包含
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);
}
}
尽管/tacos/recent
,但是类级别的/api/tacos/recent
的
需要注意的一件重要事情是,尽管
使用/api/tacos/recent
的请求将返回最多/api/tacos
时,它仍然不会出现在超链接列表中。让我们解决这个问题。
6.3.4 向Spring Data 端点添加用户超链接
如果最近的/api/tacos
返回的超链接中,客户端如何知道如何获取最近的
不过,通过声明资源处理器/api/tacos
端点返回的类型
@Bean
public ResourceProcessor<PagedResources<Resource<Taco>>>
tacoProcessor(EntityLinks links) {
return new ResourceProcessor<PagedResources<Resource<Taco>>>() {
@Override
public PagedResources<Resource<Taco>> process(
PagedResources<Resource<Taco>> resource) {
resource.add(
links.linkFor(Taco.class)
.slash("recent")
.withRel("recents"));
return resource;
}
};
}
程序清单/api/tacos
请求的响应。