[英]REST How to implement polymorphic POST end point: abstract types need to be mapped to concrete types
我在 SpringBoot 应用程序上工作。
我有以下类层次结构:
public abstract class DlqMessage {
Long id;
UUID employeeId;
EventReason reason;
}
public class ContractUpdateMessage extends DlqMessage {}
public class SingleContractUpdateMessage extends DlqMessage {
UUID benefitId;
UUID employerId;
}
因此,类ContractUpdateMessage
和SingleContractUpdateMessage
仅在几个字段上有所不同。
我有一个 REST 控制器,它使用 POST 在数据库中创建和保存一个新实体:
@PostMapping("/messages")
public ResponseEntity<DlqMessage> create(@RequestBody DlqMessage contractUpdateMessage) {
DlqMesssage dlqEntry = dlqEntityService.save(contractUpdateMessage);
return ResponseEntity.ok(dlqEntry);
}
现在,我有一个在一个或另一个类的实例上随机生成的测试:
DlqMessage message = randomOneOf(contractUpdateMessageBuilder().build(), singleContractUpdateMessageBuilder().build());
然后我有一个测试助手类,它使用 RestTemplate 向控制器发送 POST 请求:
ResponseEntity<DlqMesssage> response =
crudRestClient.post("/messages/contract", message, DlqMesssage.class, null);
return response.getBody();
并调用整个事情我最终有以下例外:
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 发送。
顺便说一句,当我为每个单独的子类型拆分端点时,这一切都有效:
@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);
}
然而,这看起来很丑陋,并不是一个“正确”的解决方案。
基本上,我想知道是否有可能以及如何实现将多态实例作为参数的 REST 端点以及如何调用这样的端点?
不能使用抽象类型 DlqMessage 的原因是因为您可能会收到如下消息:
{
"id":300,
"employeeId":"d6a00fb2-058c-4bf7-9a0a-7cc538cd85f5"
}
Jackson 无法确定此消息打算映射的具体对象的类型。 处理此问题的最简单方法是定义类型提示,例如:
@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 对象中包含此类型提示:
{
"id":300,
"employeeId":"d6a00fb2-058c-4bf7-9a0a-7cc538cd85f5",
"messageType":"ContractUpdateMessage"
}
这样,Jackson 的默认对象映射器将使用字段“messageType”来猜测您的 API 正在接收哪种类型的 DlqMessage。
编辑:您可以在此处找到更多信息:
问题是 spring 试图直接从输入负载创建 DlqMessage 的实例。 假设您的 API 接受 JSON 输入,您需要指示您的 json 解析器如何根据有效负载内容区分子类型。
这是一个类似问题的链接,该链接显示了如何使用 jackson json 执行此操作的示例。 像你看上去就必须通过注释与亚型知识的抽象类@JsonSubTypes
以及提供通过区分领域@JsonTypeInfo
:
你可以用jackson mapping来映射类。 在类中添加类型属性。
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, visible = true, property = "type")
@JsonSubTypes({@JsonSubTypes.Type(value = ContractUpdateMessage.class, name = "ContractUpdateMessage"),
@JsonSubTypes.Type(value = SingleContractUpdateMessage.class, name = "SingleContractUpdateMessage")})
public abstract class DlqMessage {
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.