如何在客户端防止JacksonInvalidTypeIdExceptions?

wljmcqd8  于 2022-11-08  发布在  其他
关注(0)|答案(1)|浏览(194)

假设我们有一个基类Pet,其子类Dog、Cat和Fish定义为我们的服务响应:

openapi: '3.0.3'
paths:
  '/pets':
    get:
      responses:
        200:
          description: Ok
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Pet'
components:
  schemas:
    Pet:
      type: object
      description: Base class for all types of pets
      properties:
        kind:
          type: string
      required:
        - kind
      discriminator:
        propertyName: kind
        mapping:
          Dog: '#/components/schemas/Dog'
          Cat: '#/components/schemas/Cat'
          Fish: '#/components/schemas/Fish'

我们有几个客户端使用这个OpenApi定义来使用我们的服务。
现在,我们想要添加第四个宠物类型Snakes,所以我们将其添加到Pet模式的“mapping”部分。但是,如果我们将这样的更改部署到服务生产系统,则每当返回Snake时,客户端将无法解析服务响应。
我们可以提前发布我们新改进的API,给予客户一些时间进行升级,并增加对这种新类型的支持,但我们不能无限期地推迟升级。因此,我们希望能够部署我们的新服务版本,并在其响应中发送蛇,而不破坏我们客户端的东西。
有没有办法以“开放”的方式定义这个多态类型,这样客户端就可以解析snake和其他未知的类型,而不会得到InvalidTypeIdException?我不需要能够得到snake字段(我知道涉及到的安全问题),我很乐意只拥有一个“Pet”示例,将种类设置为“Snake”,而不需要其他信息。
如果这不能在服务器端完成,是否有任何方法可以生成客户端代码,使其对这些错误具有弹性?
如果在定义或客户端代码生成时不存在解决方案,是否可以使用Jackson注解或配置在Java中实现(在客户端)这样的特性?

xt0899hw

xt0899hw1#

到目前为止,我还没有找到从服务器端或通过OpenApi规范获取所需内容的任何方法。但是,我找到了使用Jackson从客户端获取所需内容的两种可能方法。
第一种方法是在ObjectMapper中禁用FAIL_ON_INVALID_SUBTYPE

var mapper = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE);

这将使未知的子类型示例被反序列化为空值。对于Pet列表来说,考虑到这一点很重要,因为它们将来可能会有空值项。
另一种方法是创建一个DeserializationProblemHandler子类,强制将未知示例反序列化为根类型的示例:

public class UnknownPetTypeHandler extends DeserializationProblemHandler {
  @Override
  public JavaType handleUnknownTypeId(DeserializationContext ctx,
      JavaType baseType, String subTypeId,
      TypeIdResolver idResolver, String failureMsg) {
    return Pet.class.equals(baseType.getRawClass()) ? baseType : null;
  }
}

并将其添加到用于反序列化的ObjectMapper中:

var mapper = new ObjectMapper().addHandler(new UnknownPetTypeHandler());

该第二选项避免了空值,并且以额外的复杂性为代价提供了用于检测和通知接收到的未知类型的机制。

相关问题