在Spring Boot中启用HAL序列化以获取自定义控制器方法

Til*_*man 11 spring spring-mvc spring-data-rest spring-hateoas spring-boot

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

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

{
  "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"
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

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

@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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

java.lang.IllegalStateException: Cannot call sendError() after the response has been committed
Run Code Online (Sandbox Code Playgroud)

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

{
  "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"
  }
}
Run Code Online (Sandbox Code Playgroud)

没有HAL链接,杰克逊直接将所有内容序列化.我尝试添加

@EnableHypermediaSupport(type = HypermediaType.HAL)
Run Code Online (Sandbox Code Playgroud)

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

编辑: 好的,在考虑了两次后,我意识到必须将@EnableHypermediaSupport注释添加到配置中.这解决了循环引用的问题,我可以从User中删除@JsonBackReference.但只有对象本身的属性被序列化,没有_links部分:

{
    "amount": -4.81,
    "date": "2014-06-17T21:18:00.000+0000",
    "description": "Pizza"
}
Run Code Online (Sandbox Code Playgroud)

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

Oli*_*ohm 24

这里有很多方面:

  1. 我怀疑收集资源/transactions真的如你所描述的那样返回一个单独的事务.为项目资源返回这些表示.

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

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

    如果你想一想,这里没有什么神奇的东西.普通实体没有任何链接,ResourcesSupport类型和类型Resource<T>允许您包装实体并使用您认为合适的链接丰富它.Spring Data REST基本上是为您使用隐含的可用域和存储库结构的大量知识.你可以重复使用很多,如下所示.

    你需要注意一些帮手:

    • PersistentEntityResourceAssembler- 通常将其注入控制器方法.它以Spring Data REST方式呈现单个实体,这意味着指向托管类型的关联将呈现为链接等.
    • PagedResourcesAssembler- 通常注入控制器实例.负责准备页面中包含的项目,可选择使用专用项目ResourceAssembler.

    Spring Data REST基本上对页面执行的操作如下:

    PersistentEntityResourceAssembler entityAssembler = …;
    Resources<?> … = pagedResourcesAssembler.toResources(page, entityAssembler);
    
    Run Code Online (Sandbox Code Playgroud)

    这基本上PagedResourcesAssemblerPersistentEntityResourceAssembler用于渲染项目.

    返回该Resources实例应该为您提供您期望的表示设计.