Spring MVC 3:以 JSON 形式返回 Spring-Data 页面

我有一个使用 Spring-Data 制作的数据访问层.我现在正在它之上创建一个 Web 应用程序.这个控制器方法应该返回一个 Spring-Data Page 格式为 JSON.

这样的页面是一个列表,其中包含额外的分页信息,例如记录总数等.

这可能吗?如果可以,怎么做?

与此直接相关,我可以定义属性名称的映射吗?例如.这意味着我需要定义如何在 JSON 中命名分页信息属性(不同于在页面中).这可能吗?如何实现?

解决方案

在 Spring HATEOAS 和 Spring Data Commons 中支持这样的场景.Spring HATEOAS 带有一个 PageMetadata 对象,该对象本质上包含与 Page 相同的数据,但执行方式较少,因此可以更轻松地编组和解组.p>

我们结合 Spring HATEOAS 和 Spring Data commons 实现此功能的另一个原因是,简单地编组页面、内容和元数据没有什么价值,但还希望生成可能存在的下一页或上一页的链接,因此客户端不必自己构造 URI 来遍历这些页面.

一个例子

假设一个域类Person:

类人{长身份证;字符串名、姓;}

以及它对应的仓库:

接口 PersonRepository 扩展 PagingAndSortingRepository{ }

您现在可以按如下方式公开 Spring MVC 控制器:

@Controller类人控制器 {@Autowired PersonRepository 存储库;@RequestMapping(value = "/persons", method = RequestMethod.GET)HttpEntity<PagedResources<Person>>人(可分页可分页,PagedResourcesAssembler 汇编器){页面<人物>人 = repository.findAll(pageable);return new ResponseEntity<>(assembler.toResources(persons), HttpStatus.OK);}}

这里可能有很多需要解释的地方.让我们一步一步来:

  1. 我们有一个 Spring MVC 控制器,将存储库连接到其中.这需要设置 Spring Data(通过 @Enable(Jpa|Mongo|Neo4j|Gemfire)Repositories 或 XML 等效项).控制器方法映射到 /persons,这意味着它将接受对该方法的所有 GET 请求.
  2. 方法返回的核心类型是一个PagedResources - 来自 Spring HATEOAS 的一种类型,它表示用 LinksPageMetadata 丰富的一些内容.
  3. 当方法被调用时,Spring MVC 必须为 PageablePagedResourcesAssembler 创建实例.要使其工作,您需要通过 @EnableSpringDataWebSupport 注释启用 Spring Data Web 支持,即将在 Spring Data Commons 的即将到来的里程碑中引入,或者通过独立的 bean 定义(记录在 这里).

    Pageable 将填充来自请求的信息.默认配置会将 ?page=0&size=10 变成一个 Pageable 请求第一个页面,页面大小为 10.

    PageableResourcesAssembler 允许您轻松地将 Page 转换为 PagedResources 实例.它不仅会将页面元数据添加到响应中,还会根据您访问的页面以及 Pageable 分辨率的配置方式,将适当的链接添加到表示中.

为 JPA 启用此功能的示例 JavaConfig 配置如下所示:

@Configuration@EnableWebMvc@EnableSpringDataWebSupport@EnableJpaRepositories类应用程序配置 {//在此处声明 EntityManagerFactory 等基础设施组件}

请求和响应示例

假设我们在数据库中有 30 个 Persons.您现在可以触发请求 GET http://localhost:8080/persons,您会看到类似以下内容:

{ "链接" : [{ rel":下一个",href":http://localhost:8080/persons?page=1&size=20 }],内容" : […//这里渲染了 20 个 Person 实例],页面元数据":{大小":20,总元素":30,总页数":2,数字":0}}

请注意,汇编器生成了正确的 URI,并且还选择了默认配置,以将参数解析为 Pageable 以用于即将到来的请求.这意味着,如果您更改该配置,链接将自动遵循更改.默认情况下,汇编器指向调用它的控制器方法,但可以通过提交自定义 Link 来自定义该方法,以用作构建指向 PagedResourcesAssembler 重载的分页链接的基础.toResource(…) 方法.

展望

PagedResourcesAssembler 位将在即将发布的 Spring Data Babbage 里程碑版本中提供 发布火车.它已在当前快照中可用.您可以在我的 Spring RESTBucks 示例应用程序中看到一个工作示例.只需克隆它,运行 mvn jetty:run 和 curl http://localhost:8080/pages.

I have a data access layer made with Spring-Data. I'm now creating a web application on top of it. This one controller method should return a Spring-Data Page formatted as JSON.

Such a Page is a List with additional Paging info like total amount of records and so forth.

Is that possible and if yes how?

And directly related to that can I define the mapping of property names? Eg. meaning I would need define how the paging info properties are named in JSON (differently than in page). Is this possible and how?

解决方案

There's support for a scenario like this upcoming in Spring HATEOAS and Spring Data Commons. Spring HATEOAS comes with a PageMetadata object that essentially contains the same data as a Page but in a less enforcing manner, so that it can be more easily marshaled and unmarshaled.

Another aspect of the reason we implement this in combination with Spring HATEOAS and Spring Data commons is that there's little value in simply marshaling the page, it's content and the metadata but also want to generate the links to maybe existing next or previous pages, so that the client doesn't have to construct URIs to traverse these pages itself.

An example

Assume a domain class Person:

class Person {

  Long id;
  String firstname, lastname;
}

as well as it's corresponding repository:

interface PersonRepository extends PagingAndSortingRepository<Person, Long> { }

You can now expose a Spring MVC controller as follows:

@Controller
class PersonController {

  @Autowired PersonRepository repository;

  @RequestMapping(value = "/persons", method = RequestMethod.GET)
  HttpEntity<PagedResources<Person>> persons(Pageable pageable, 
    PagedResourcesAssembler assembler) {

    Page<Person> persons = repository.findAll(pageable);
    return new ResponseEntity<>(assembler.toResources(persons), HttpStatus.OK);
  }
}

There's probably quite a bit to explain here. Let's take it step by step:

  1. We have a Spring MVC controller getting the repository wired into it. This requires Spring Data being set up (either through @Enable(Jpa|Mongo|Neo4j|Gemfire)Repositories or the XML equivalents). The controller method is mapped to /persons, which means it will accept all GET requests to that method.
  2. The core type returned from the method is a PagedResources - a type from Spring HATEOAS that represents some content enriched with Links plus a PageMetadata.
  3. When the method is invoked, Spring MVC will have to create instances for Pageable and PagedResourcesAssembler. To get this working you need to enable the Spring Data web support either through the @EnableSpringDataWebSupport annotation about to be introduced in the upcoming milestone of Spring Data Commons or via standalone bean definitions (documented here).

    The Pageable will be populated with information from the request. The default configuration will turn ?page=0&size=10 into a Pageable requesting the first page by a page size of 10.

    The PageableResourcesAssembler allows you to easily turn a Page into a PagedResources instances. It will not only add the page metadata to the response but also add the appropriate links to the representation based on what page you access and how your Pageable resolution is configured.

A sample JavaConfig configuration to enable this for JPA would look like this:

@Configuration
@EnableWebMvc
@EnableSpringDataWebSupport
@EnableJpaRepositories
class ApplicationConfig {

  // declare infrastructure components like EntityManagerFactory etc. here
}

A sample request and response

Assume we have 30 Persons in the database. You can now trigger a request GET http://localhost:8080/persons and you'll see something similar to this:

{ "links" : [
    { "rel" : "next", "href" : "http://localhost:8080/persons?page=1&size=20 }
  ],
  "content" : [
    … // 20 Person instances rendered here
  ],
  "pageMetadata" : {
    "size" : 20,
    "totalElements" : 30,
    "totalPages" : 2,
    "number" : 0
  }
}

Note that the assembler produced the correct URI and also picks up the default configuration present to resolve the parameters into a Pageable for an upcoming request. This means, if you change that configuration, the links will automatically adhere to the change. By default the assembler points to the controller method it was invoked in but that can be customized by handing in a custom Link to be used as base to build the pagination links to overloads of the PagedResourcesAssembler.toResource(…) method.

Outlook

The PagedResourcesAssembler bits will be available in the upcoming milestone release of the Spring Data Babbage release train. It's already available in the current snapshots. You can see a working example of this in my Spring RESTBucks sample application. Simply clone it, run mvn jetty:run and curl http://localhost:8080/pages.

相关文章