spring/jackson-databind 小升级后 HttpMessageConverter 不再工作

thu*_*rmc 6 spring json spring-mvc jackson jackson-databind

我必须升级几个包才能通过 whitesource 安全扫描,现在升级了依赖项HttpMessageConverter,以前拦截和构建响应的自定义不再有效。相关的依赖升级如下所示。

Tomcat 嵌入核心 8.5.50 -> 9.0.30

Spring Cloud 合约发布 2.0.1.RELEASE-> 2.0.6.RELEASE

Spring Boot 版本 2.0.4.RELEASE -> 2.0.6.RELEASE

杰克逊数据绑定 2.9.6 -> 2.10.0.pr1

杰克逊核心:2.10.1 -> 2.10.0.pr1

这是以前工作的自定义 HttpMessageConverter:

    private class JsonApiHttpMessageConverter extends AbstractHttpMessageConverter<Object> {
    JsonApiHttpMessageConverter() {
        super(MediaType.valueOf(ResponseType.MEDIA_TYPE_JSON_API));
    }

    @Override
    protected boolean supports(final Class<?> clazz) {
        return clazz == HttpErrorResponse.class;
    }

    @Override
    protected Object readInternal(final Class<?> clazz, final HttpInputMessage inputMessage)
        throws IOException, HttpMessageNotReadableException {
        return null;
    }

    @Override
    protected void writeInternal(final Object o, final HttpOutputMessage outputMessage)
        throws IOException, HttpMessageNotWritableException {
        try (OutputStreamWriter outputStream =
            new OutputStreamWriter(outputMessage.getBody(), Charset.defaultCharset())) {
            JsonAPIDocument document;
            if (o instanceof HttpErrorResponse) {
                // Build Error Document
                final HttpErrorResponse errorResponse = (HttpErrorResponse) o;
                final JsonApiErrorDTO errorDTO = new JsonApiErrorDTO(Integer.toString(errorResponse.getStatus()),
                    Integer.toString(errorResponse.getCode()), errorResponse.getMessage());
                document = new JsonAPIDocument();
                document.addError(errorDTO);
            } else { // Build JSON API Response Document
                final JsonApiDocumentBuilder documentBuilder = new JsonApiDocumentBuilder();
                documentBuilder.data(o);
                document = documentBuilder.build();
            }
            outputStream.write(new Gson().toJson(document));
            outputStream.flush();
        } catch (final InvalidJsonApiObjectException ijaoe) {
            LOG.error("Error in converting Object to JsonAPIDocument", ijaoe);
            throw new ServiceException(ijaoe);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

它是在一个类中定义的,该类注册了如下所示的消息转换器

@Configuration
public class ServiceConfiguration implements WebMvcConfigurer {

...

    @Override
public void configureMessageConverters(final List<HttpMessageConverter<?>> converters) {
    converters.add(new JsonApiHttpMessageConverter());
    converters.add(new ByteArrayHttpMessageConverter());
    converters.add(new StringHttpMessageConverter());
    converters.add(new ResourceHttpMessageConverter());
    converters.add(new SourceHttpMessageConverter<>());
    converters.add(new AllEncompassingFormHttpMessageConverter());
    converters.add(new MappingJackson2HttpMessageConverter());
    converters.add(new MappingJackson2CborHttpMessageConverter());
    converters.add(new Jaxb2RootElementHttpMessageConverter());
}

@Override
public void configureContentNegotiation(final ContentNegotiationConfigurer configurer) {
    configurer.defaultContentType(MediaType.valueOf(ResponseType.MEDIA_TYPE_JSON_API));
}

}
Run Code Online (Sandbox Code Playgroud)

这是错误响应代码:

@XmlRootElement(name = "error")
@XmlAccessorType(XmlAccessType.FIELD)
@JsonApiType(type = "error")
public class HttpErrorResponse implements Serializable {
    private static final long serialVersionUID = 1321088631120274988L;

    @XmlTransient
    private int status;

    @XmlElement(name = "code", required = true)
    private int code;

    @XmlElement(name = "message", required = true)
    @JsonApiAttribute(name = "detail")
    private String message;

    @JsonApiIgnore
    @XmlElement(name = "uuid", required = true)
    private String uuid;

    public HttpErrorResponse() {
        this.uuid = UUID.randomUUID().toString();
    }

    public HttpErrorResponse(final int status, final int code, final String message) {
        this();
        this.status = status;
        this.code = code;
        this.message = message;
    }

    public int getStatus() {
        return status;
    }

    public void setStatus(final int status) {
        this.status = status;
    }

    public int getCode() {
        return code;
    }

    public void setCode(final int code) {
        this.code = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(final String message) {
        this.message = message;
    }

    public String getUuid() {
        return uuid;
    }

    public void setUuid(final String uuid) {
        this.uuid = uuid;
    }

}
Run Code Online (Sandbox Code Playgroud)

并且该对象似乎确实是返回的对象,但消息转换器并未对其进行转换。转换器工作的旧响应格式是:

"{\"included\":[],\"errors\":[{\"status\":\"400\",\"code\":\"990002\",\"detail\":\"Invalid subscription id: XYZ\"}]}",
Run Code Online (Sandbox Code Playgroud)

目前的回应是:

"responseBody": "{\"status\":400,\"code\":990002,\"message\":\"Invalid subscription id: XYZ\",\"uuid\":\"018fe1e3-3936-4c53-8612-61ef778fd811\"}",
Run Code Online (Sandbox Code Playgroud)

如果我的问题不清楚,我很抱歉,如果有什么我遗漏了,请告诉我。我很困惑为什么转换器不再拦截响应并更新格式。这里也是转换器应该构建的格式:

@JsonPropertyOrder({ "data", "included", "errors" })
@JsonInclude(JsonInclude.Include.NON_NULL)
public class JsonAPIDocument extends AbstractJsonAPIDocument {

    @JsonInclude(JsonInclude.Include.NON_NULL)
    private IJsonAPIDTO data;

    @JsonInclude(JsonInclude.Include.NON_NULL)
    public IJsonAPIDTO getData() {
        return data;
    }

    public void setData(IJsonAPIDTO data) {
        this.data = data;
    }
}
Run Code Online (Sandbox Code Playgroud)

以下是响应头:

"responseHeaders": {
    "X-Origin-Ref": "microservice",
    "X-Transaction-Ref": "b2e3d807-0d6f-40e7-90ad-43d5ecb455c2",
    "Content-Type": "application/vnd.api+json"
}
Run Code Online (Sandbox Code Playgroud)

和 ResponseType.MEDIA_TYPE_JSON_API 是

public static final String MEDIA_TYPE_JSON_API = "application/vnd.api+json"
Run Code Online (Sandbox Code Playgroud)

作为绝望的尝试,我将 JsonApiHttpMessageConverter 类的支持方法更改为始终返回 true 并且转换器仍然没有接收响应