Spring RestTemplate,在解析到 Json 之前拦截响应

Spring RestTemplate, intercepting response before parsing to Json

我有一个 REST api 响应正文内容中的一些额外的非 JSON 数据。这打破了 RestTemplate 和 jackson 的使用。我可以在解析之前拦截 http 响应主体吗?

我正在使用 RestTemplate.getForObject。

我查看了 RestTemplate,但没有找到合适的方法。

您可以尝试从您的 Controller return ResponseEntity 并手动操作实体对象

您可以尝试实现ClientHttpRequestInterceptor并将其分配给restTemplate。实施 intercept 方法:

@Override
public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes,
        ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {

         ClientHttpResponse  response=clientHttpRequestExecution.execute(httpRequest, bytes);
         //...do magic with response body from getBody method
         return response;
}

您可能需要使用自己的实现扩展 AbstractClientHttpResponse 才能做到这一点。

另一种选择是将来自 REST API 的响应视为字符串,然后根据需要格式化字符串并使用 ObjectMapper 将其显式映射到对象。

那么在你的 restTemplate 中你会:

 String result = restTemplate.getForObject(url, String.class, host);
 //..trim the extra stuff
 MyClass object=objectMapper.readValue(result, MyClass.class);

另一种选择是实现您自己的 HttpMessageConverter,它扩展了 AbstractJackson2HttpMessageConverter 并将其注册到 restTemplate。在我看来,从 Spring 的角度来看,这将是最干净的

如果您不需要这些额外的属性,您可以添加:

    @JsonIgnoreProperties(ignoreUnknown = true)

到您的映射 class。

来自docs

Property that defines whether it is ok to just ignore any unrecognized properties during deserialization. If true, all properties that are unrecognized -- that is, there are no setters or creators that accept them -- are ignored without warnings (although handlers for unknown properties, if any, will still be called) without exception. Does not have any effect on serialization.

另一种方法是通过实现 ClientHttpRequestInterceptor 和 ClientHttpResponse 来解包响应。

        @Component
        public class MyInterceptor implements ClientHttpRequestInterceptor {

        @Autowired
        Function<ClientHttpResponse, MyResponseWrapper> responseWrapperBeanFactory;

        @Autowired
        MyRequestAdvice requestAdvice;

        @Override
          public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes, ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
            byte[] wrappedBody = requestAdvice.wrapRequest(bytes);
            ClientHttpResponse res = clientHttpRequestExecution.execute(httpRequest, wrappedBody);
            return responseWrapperBeanFactory.apply(res);
        }    

       }

这是 MyResponseWrapper 的 bean 配置:

@Bean
Function<ClientHttpResponse, MyResponseWrapper> responseWrapperBeanFactory() {
    return this::getMyResponseWrapper;
}

@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public MyResponseWrapper getMyResponseWrapper(ClientHttpResponse originalResponse) {
    return new MyResponseWrapper(originalResponse);
}

@Bean
public RestTemplate restTemplate(@Autowired MyInterceptor interceptor) {
    RestTemplate t = new RestTemplate();
    t.setInterceptors(Arrays.asList(interceptor));
    // other setup code

    return t;
}

这是 ClientHttpResponse 的实现:

public class MyResponseWrapper implements ClientHttpResponse {

    private byte[] filteredContent;
    private ByteArrayInputStream responseInputStream;


    private ClientHttpResponse originalResponse;

    public MyResponseWrapper(ClientHttpResponse originalResponse) {
        this.originalResponse = originalResponse;

        try {
            filteredContent = MyContentUnwrapper.unwrapResponse(originalResponse.getBody().readAllBytes());
        } catch (Exception e) {
            throw new RuntimeException("There was a problem reading/decoding the response coming from the service ", e);
        }
    }

    @Override
    public HttpStatus getStatusCode() throws IOException {
        return originalResponse.getStatusCode();
    }

    @Override
    public int getRawStatusCode() throws IOException {
        return originalResponse.getRawStatusCode();
    }

    @Override
    public String getStatusText() throws IOException {
        return originalResponse.getStatusText();
    }

    @Override
    public void close() {
        if (responseInputStream != null) {
            try {
                responseInputStream.close();
            } catch (IOException e) { /* so long */}
        }
    }

    @Override
    public InputStream getBody() throws IOException {
        if (responseInputStream == null) {
            responseInputStream = new ByteArrayInputStream(filteredContent);
        }
        return responseInputStream;
    }

    @Override
    public HttpHeaders getHeaders() {
        return originalResponse.getHeaders();
    }
}