简体   繁体   English

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

[英]Spring RestTemplate, intercepting response before parsing to Json

I have a REST api that responds with some additional non JSON data in the body content.我有一个 REST api,它响应正文内容中的一些额外的非 JSON 数据。 This breaks the use of RestTemplate and jackson.这打破了 RestTemplate 和 jackson 的使用。 Can I intercept the http response body prior to the parsing?我可以在解析之前拦截 http 响应正文吗?

I am using RestTemplate.getForObject.我正在使用 RestTemplate.getForObject。

I've taken a look at the RestTemplate and couldn't see an appropriate method.我查看了 RestTemplate 并看不到合适的方法。

You can try to implement ClientHttpRequestInterceptor and assign it to restTemplate .您可以尝试实现ClientHttpRequestInterceptor并将其分配给restTemplate Implement intercept method:实现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;
}

You might have to extend AbstractClientHttpResponse with your own implementation to do that.您可能必须使用自己的实现扩展AbstractClientHttpResponse才能做到这一点。

Another option could be to treat the response from the REST API as String, then format the string as needed and explicitly map it to object using ObjectMapper.另一种选择可能是将来自 REST API 的响应视为字符串,然后根据需要格式化字符串并使用 ObjectMapper 将其显式映射到对象。

Then in your restTemplate you would have:然后在您的restTemplate中,您将拥有:

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

Yet another option would be to implement your own HttpMessageConverter which extends AbstractJackson2HttpMessageConverter and register it with restTemplate .另一种选择是实现您自己的HttpMessageConverter扩展AbstractJackson2HttpMessageConverter并将其注册到restTemplate In my opinion that would be the cleaneast from the Spring point of view在我看来,从春天的角度来看,这将是最干净的

Another way would be to unwrap the response by implementing a ClientHttpRequestInterceptor along with a ClientHttpResponse .另一种方法是通过实现 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);
    }    
}

Here's the bean config for the MyResponseWrapper :这是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;
}

And here's the ClientHttpResponse implementation:这是 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();
    }
}

从您的控制器中,您可以尝试返回一个 ResponseEntity 并手动操作实体对象

If you don't need these extra properties you may add:如果您不需要这些额外的属性,您可以添加:

    @JsonIgnoreProperties(ignoreUnknown = true)

to your mapping class.到您的映射类。

From 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.如果为 true,则所有无法识别的属性——即没有接受它们的设置者或创建者——将被忽略而不会发出警告(尽管仍将调用未知属性的处理程序,如果有的话),无一例外。 Does not have any effect on serialization.对序列化没有任何影响。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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