REST 如何实现多态 POST 端点:抽象类型需要映射到具体类型 [英] REST How to implement polymorphic POST end point: abstract types need to be mapped to concrete types

查看:57
本文介绍了REST 如何实现多态 POST 端点:抽象类型需要映射到具体类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一个 SpringBoot 应用程序.

I work on a SpringBoot application.

我有以下类层次结构:

public abstract class DlqMessage {
    Long id;
    UUID employeeId;
    EventReason reason;
}

public class ContractUpdateMessage extends DlqMessage {}

public class SingleContractUpdateMessage extends DlqMessage {
    UUID benefitId;
    UUID employerId;
}

因此,ContractUpdateMessageSingleContractUpdateMessage 类仅在几个字段上有所不同.

So, the classes ContractUpdateMessage and SingleContractUpdateMessage only differ by a couple of fields.

我有一个 REST 控制器,它使用 POST 在数据库中创建和保存一个新实体:

I Have a REST controller that uses POST to create and save a new entity in the DB:

@PostMapping("/messages")
public ResponseEntity<DlqMessage> create(@RequestBody DlqMessage contractUpdateMessage) {
    DlqMesssage dlqEntry = dlqEntityService.save(contractUpdateMessage);
    return ResponseEntity.ok(dlqEntry);
}

现在,我有一个测试,随机生成一个或另一个类的实例:

Now, I have a test that randomly generates on instance of the one or the other class:

 DlqMessage message = randomOneOf(contractUpdateMessageBuilder().build(), singleContractUpdateMessageBuilder().build());

然后我有一个测试助手类,它使用 RestTemplate 向控制器发送 POST 请求:

Then I have a test helper class that uses RestTemplate to send a POST request to the controller:

ResponseEntity<DlqMesssage> response =
                crudRestClient.post("/messages/contract", message, DlqMesssage.class, null);
        return response.getBody();

并调用整个事情我最终有以下例外:

And invoking the whole things I end up with the following exception:

Failed to read HTTP message: org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Can not construct instance of com.orbitbenefits.benefitsselection.server.errorrecovery.entity.DlqMessage: abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of com.orbitbenefits.benefitsselection.server.errorrecovery.entity.DlqMessage: abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information

而且看起来我的请求甚至没有被 RestTemplate 发送.

And it looks like my request isn't even being sent by the RestTemplate.

顺便说一句,当我为每个单独的子类型拆分端点时,这一切都有效:

BTW, it all works when I split endpoints for each individual subtype:

@PostMapping("/messages/contract")
public ResponseEntity<DlqMessage> create(@RequestBody ContractUpdateMessage contractUpdateMessage) {
    ContractUpdateMessage dlqEntry = (ContractUpdateMessage) dlqEntityService.save(contractUpdateMessage);
    return ResponseEntity.ok(dlqEntry);
}

@PostMapping("/messages/single")
public ResponseEntity<DlqMessage> create(@RequestBody SingleContractUpdateMessage contractUpdateMessage) {
    SingleContractUpdateMessage dlqEntry = (SingleContractUpdateMessage) dlqEntityService.save(contractUpdateMessage);
    return ResponseEntity.ok(dlqEntry);
}

然而,这看起来很丑陋,并不是一个正确"的解决方案.

However that looks ugly and is not a "correct" solution.

基本上,我想知道是否有可能以及如何实现将多态实例作为参数的 REST 端点以及如何调用这样的端点?

Basically, I would like to know if it's possible and how to implement a REST end point that takes polymorphic instance as a parameter and how to invoke such an end point?

推荐答案

不能使用抽象类型 DlqMessage 的原因是因为你可能会收到这样的消息:

The reason you can't use the abstract type DlqMessage is because you may receive a message like:

{
  "id":300, 
  "employeeId":"d6a00fb2-058c-4bf7-9a0a-7cc538cd85f5"
}

Jackson 无法确定此消息打算映射的具体对象的类型.处理这个最简单的方法是定义一个类型提示,比如:

Jackson can't determine which is type of the concrete object that this message is intending to map. The simplest way to handle this is defining a type hint, like:

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "messageType")
@JsonSubTypes({
    @JsonSubTypes.Type(value=ContractUpdateMessage.class, name = "ContractUpdateMessage"),
    @JsonSubTypes.Type(value=SingleContractUpdateMessage.class, name = "SingleContractUpdateMessage")
})
public abstract class DlqMessage { ... }

这样,下次您进行 API 调用时,您还必须在 JSON 对象中包含此类型提示:

This way, the next time you make an API call you must also include this type hint in your JSON object:

{
  "id":300, 
  "employeeId":"d6a00fb2-058c-4bf7-9a0a-7cc538cd85f5",
  "messageType":"ContractUpdateMessage"
}

这样,Jackson 的默认对象映射器将使用字段messageType"来猜测您的 API 正在接收哪种类型的 DlqMessage.

This way, Jackson's default object mapper will use the field "messageType" to guess which type of DlqMessage your API is receiving.

您可以在此处找到更多信息:

You can find further information here:

https://www.baeldung.com/jackson-annotations

这篇关于REST 如何实现多态 POST 端点:抽象类型需要映射到具体类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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