spring - 在 Spring Boot 中为自定义 Controller 方法启用 HAL 序列化

标签 spring spring-mvc spring-boot spring-data-rest spring-hateoas

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

当我在 http://localhost:8080/transactions 检索对象时通过默认生成的 API,一切顺利,我得到了一个列表,其中包含所有事务作为 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"
    }
  }
}

但现在的目标是只检索该 URL 下的最新事务,因为我不想序列化整个数据库表。于是我写了一个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 时有一个

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

因为用户和帐户之间的循环引用。当我通过向用户中的帐户列表添加@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"
  }
}

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

@EnableHypermediaSupport(type = HypermediaType.HAL)

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

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

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

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

最佳答案

这里有很多方面:

  1. 我怀疑 /transactions 的收藏资源如您所述,确实返回了单个交易。为项目资源返回这些表示。

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

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

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

    这里有一些你需要注意的助手:

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

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

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

    这基本上是使用 PagedResourcesAssemblerPersistentEntityResourceAssembler渲染项目。

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

关于spring - 在 Spring Boot 中为自定义 Controller 方法启用 HAL 序列化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31758862/

相关文章:

java - Spring中如何定义一个String数组的bean(使用XML配置)

java - 使用 MultipartFile 作为可选字段的多部分请求 - Spring MVC

java - Spring MVC 5.0 在开发注释驱动的 Web 应用程序时使用哪个 HandlerMapping 作为默认 HandlerMapping?

docker - Spring Boot 应用程序独立运行良好,在 docker 中出错

java - 在 Spring Boot 中存储证书的最佳做法是什么?

java - 在 Spring 实现授权码授予时出现的问题

xml - 将 activemq 代理配置拆分为多个文件

spring-boot - 如何重新连接 ReactorNettyWebSocketClient 连接?

java - 删除 commons-logging 并使用 slf4j 会导致 spring 出现错误

java - 在 Spring Boot 的 application.properties 中使用 env 变量