在 Spring Boot 中为自定义控制器方法启用 HAL 序列化 [英] Enable HAL serialization in Spring Boot for custom controller method

查看:31
本文介绍了在 Spring Boot 中为自定义控制器方法启用 HAL 序列化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用 spring-boot-starter-data-rest 使用 Spring Boot 构建 RESTful API.有一些实体:帐户、交易、类别和用户 - 只是常见的东西.

I'm trying to build a RESTful API with Spring Boot using spring-boot-starter-data-rest. There are some entities: accounts, transactions, categories and users - just the usual stuff.

当我通过已生成的 API 在 http://localhost:8080/transactions 检索对象时默认情况下,一切顺利,我得到一个列表,其中包含所有事务作为 JSON 对象,如下所示:

When I retrieve the objects at http://localhost:8080/transactions via the API that has been generated by default, all is going well an I get a list with all transactions as JSON objects like that one:

{
  "amount": -4.81,
  "date": "2014-06-17T21:18:00.000+0000",
  "description": "Pizza",
  "_links": {
    "self": {
      "href": "http://localhost:8080/transactions/5"
    },
    "category": {
      "href": "http://localhost:8080/transactions/5/category"
    },
    "account": {
      "href": "http://localhost:8080/transactions/5/account"
    }
  }
}

但现在的目标是仅检索该 URL 下的最新事务,因为我不想序列化整个数据库表.所以我写了一个控制器:

But now the goal is to retrieve only the latest transactions under that URL since I don't want to serialize the whole database table. So I wrote a Controller:

@Controller
public class TransactionController {
    private final TransactionRepository transactionRepository;

    @Autowired
    public TransactionController(TransactionRepository transactionRepository) {
        this.transactionRepository = transactionRepository;
    }

    // return the 5 latest transactions
    @RequestMapping(value = "/transactions", method = RequestMethod.GET)
    public @ResponseBody List<Transaction> getLastTransactions() {
        return  transactionRepository.findAll(new PageRequest(0, 5, new Sort(new Sort.Order(Sort.Direction.DESC, "date")))).getContent();
    }
}

当我现在尝试访问 http://localhost:8080/transactions 时,有一个

When I now try to access http://localhost:8080/transactions there's a

java.lang.IllegalStateException: Cannot call sendError() after the response has been committed

因为用户和帐户之间的循环引用.当我通过向 User 中的帐户列表添加 @JsonBackReference 注释来解决此问题时,我可以检索交易列表,但只能使用这种经典"格式:

because of the circular reference between users and accounts. When I solve this by adding a @JsonBackReference annotation to the account list in User, I can retrieve the transaction list but only with this "classic" format:

{
  "id": 5,
  "amount": -4.5,
  "date": "2014-06-17T21:18:00.000+0000",
  "description": "Pizza",
  "account": {
    "id": 2,
    "name": "Account Tilman",
    "owner": {
      "id": 1,
      "name": "Tilman"
    },
    "categories": [
      {
        "id": 1,
        "name": "Groceries"
      },
      {
        "id": 2,
        "name": "Restaurant"
      }
    ],
    "users": [
      {
        "id": 1,
        "name": "Tilman"
      }
    ]
  },
  "category": {
    "id": 2,
    "name": "Restaurant"
  }
}

不再有 HAL 链接,一切都由 jackson 直接序列化.我尝试添加

No HAL links anymore, everything is getting serialized directly by jackson. I tried adding

@EnableHypermediaSupport(type = HypermediaType.HAL)

到实体类,但这并没有让我到任何地方.我只希望我的控制器返回与生成的 API 相同的对象,使用 HAL _links 而不是序列化每个引用.有什么想法吗?

to the entity classes but that didn't get me anywhere. I just want my controller to return the same objects that the generated API does, with HAL _links instead of every reference being serialized. Any thoughts?

好吧,经过深思熟虑后,我意识到必须将 @EnableHypermediaSupport 注释添加到 configuration 中,当然.这解决了循环引用的问题,我可以从用户中删除@JsonBackReference.但是只有对象本身的属性被序列化,没有_links部分:

OK, after thinking twice I realized that the @EnableHypermediaSupport annotation has to be added to the configuration, of course. This solves the problem of the circular references and I can remove the @JsonBackReference from User. But only the attributes of the object itself are being serialized, there is no _links section:

{
    "amount": -4.81,
    "date": "2014-06-17T21:18:00.000+0000",
    "description": "Pizza"
}

我知道我可以为我的所有实体编写扩展 ResourceSupport 的包装类,但这似乎毫无意义.由于 spring-hateoas 能够使用自动创建的 REST 接口的 _link 部分神奇地生成表示,因此应该有一种方法可以从自定义控制器返回相同的表示,对吗?

I know that I could write wrapper classes extending ResourceSupport for all my entities but this seems rather pointless. As spring-hateoas is able to magically generate the representations with the _link section for the REST interface that is created automatically there should be a way to return the same representations from a custom controller, right?

推荐答案

这里有很多方面:

  1. 我怀疑 /transactions 中的集合资源是否真的像您描述的那样返回单个事务.为项目资源返回这些表示.

  1. I doubt that the collection resource at /transactions really returns an individual transaction as you described. Those representations are returned for item resources.

如果 TransactionRepository 已经是 PageableAndSortingRepository,则可以通过扩展 API 根中公开的 URI 模板来调整名为 transactions 的链接的集合资源.默认情况下,这是一个 pagesizesort 参数.这意味着客户可以请求您已经公开的内容.

If TransactionRepository already is a PageableAndSortingRepository the collection resource can be tweaked by expanding the URI template exposed in the API root for the link named transactions. By default that's a page, size and sort parameter. That means clients can request what you want to expose already.

如果你想默认分页和排序选项,实现一个控制器是正确的方法.但是,要实现像 Spring Data REST 公开这样的表示,您至少需要返回 ResourceSupport 因为这是 HAL 映射注册的类型.

If you want to default the paging and sorting options, implementing a controller is the correct way. However, to achieve a representation like Spring Data REST exposes you need to return at least instances of ResourceSupport as this is the type the HAL mapping is registered for.

仔细想想,这里并没有什么神奇之处.普通实体没有任何链接,ResourcesSupport 和诸如 Resource 之类的类型允许您包装实体并在您认为合适的时候用链接丰富它.Spring Data REST 基本上为您使用了大量有关隐式可用的域和存储库结构的知识.您可以重复使用很多,如下所示.

There's nothing magically here if you think about it. A plain entity does not have any links, the ResourcesSupport and types like Resource<T> allow you to wrap the entity and enrich it with links as you see fit. Spring Data REST basically does that for you using a lot of the knowledge about the domain and repository structure that's available implicitly. You can reuse a lot of as shown below.

您需要注意以下几个助手:

There are a few helper you need to be aware of here:

  • PersistentEntityResourceAssembler - 通常注入控制器方法.它以 Spring Data REST 方式呈现单个实体,这意味着指向托管类型的关联将呈现为链接等.
  • PagedResourcesAssembler - 通常注入控制器实例.负责准备页面中包含的项目,可选择使用专用的 ResourceAssembler.
  • PersistentEntityResourceAssembler - which is usually injected into the controller method. It renders a single entity in a Spring Data REST way, which means that associations pointing to managed types will be rendered as links etc.
  • PagedResourcesAssembler - usually injected into the controller instance. Takes care of preparing the items contained in the page, optionally by using a dedicated ResourceAssembler.

Spring Data REST 对页面的基本作用如下:

What Spring Data REST basically does for pages is the following:

PersistentEntityResourceAssembler entityAssembler = …;
Resources<?> … = pagedResourcesAssembler.toResources(page, entityAssembler);

这基本上是使用 PagedResourcesAssemblerPersistentEntityResourceAssembler 来呈现项目.

That's basically using the PagedResourcesAssembler with the PersistentEntityResourceAssembler to render the items.

返回那个 Resources 实例应该会给你你期望的表示设计.

Returning that Resources instance should give you the representation design you expected.

这篇关于在 Spring Boot 中为自定义控制器方法启用 HAL 序列化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆