简体   繁体   English

REST 如何实现多态 POST 端点:抽象类型需要映射到具体类型

[英]REST How to implement polymorphic POST end point: abstract types need to be mapped to concrete types

I work on a SpringBoot application.我在 SpringBoot 应用程序上工作。

I have the following class hierarchy:我有以下类层次结构:

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

public class ContractUpdateMessage extends DlqMessage {}

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

So, the classes ContractUpdateMessage and SingleContractUpdateMessage only differ by a couple of fields.因此,类ContractUpdateMessageSingleContractUpdateMessage仅在几个字段上有所不同。

I Have a REST controller that uses POST to create and save a new entity in the DB:我有一个 REST 控制器,它使用 POST 在数据库中创建和保存一个新实体:

@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());

Then I have a test helper class that uses RestTemplate to send a POST request to the controller:然后我有一个测试助手类,它使用 RestTemplate 向控制器发送 POST 请求:

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

And it looks like my request isn't even being sent by the RestTemplate.看起来我的请求甚至没有被 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.然而,这看起来很丑陋,并不是一个“正确”的解决方案。

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

The reason you can't use the abstract type DlqMessage is because you may receive a message like:不能使用抽象类型 DlqMessage 的原因是因为您可能会收到如下消息:

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

Jackson can't determine which is type of the concrete object that this message is intending to map. Jackson 无法确定此消息打算映射的具体对象的类型。 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 { ... }

This way, the next time you make an API call you must also include this type hint in your JSON object:这样,下次您进行 API 调用时,您还必须在 JSON 对象中包含此类型提示:

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

This way, Jackson's default object mapper will use the field "messageType" to guess which type of DlqMessage your API is receiving.这样,Jackson 的默认对象映射器将使用字段“messageType”来猜测您的 API 正在接收哪种类型的 DlqMessage。

Edit: You can find further information here:编辑:您可以在此处找到更多信息:

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

The issue is that spring is trying to create an instance of DlqMessage directly from the input payload.问题是 spring 试图直接从输入负载创建 DlqMessage 的实例。 Assuming your API accepts JSON input, you'll need to instruct your json parser on how to distinguish between the subtypes based on payload contents.假设您的 API 接受 JSON 输入,您需要指示您的 json 解析器如何根据有效负载内容区分子类型。

Here's a link to a similar question that shows an example of how to do so with jackson json.这是一个类似问题的链接,该链接显示了如何使用 jackson json 执行此操作的示例。 Looks like you'll have to annotate the abstract class with knowledge of the subtypes via @JsonSubTypes as well as provide the distinguishing fields via @JsonTypeInfo :像你看上去就必须通过注释与亚型知识的抽象类@JsonSubTypes以及提供通过区分领域@JsonTypeInfo

https://stackoverflow.com/a/27183383/1563240 https://stackoverflow.com/a/27183383/1563240

you can you jackson mapping to map the classes.你可以用jackson mapping来映射类。 Add a type property in the class.在类中添加类型属性。

@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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM