【发布时间】:2016-02-02 13:58:21
【问题描述】:
我有一个 REST api,它在正文内容中响应一些额外的非 JSON 数据。这打破了 RestTemplate 和 jackson 的使用。可以在解析前截取http响应体吗?
我正在使用 RestTemplate.getForObject。
我查看了 RestTemplate,但找不到合适的方法。
【问题讨论】:
标签: java json spring jackson resttemplate
我有一个 REST api,它在正文内容中响应一些额外的非 JSON 数据。这打破了 RestTemplate 和 jackson 的使用。可以在解析前截取http响应体吗?
我正在使用 RestTemplate.getForObject。
我查看了 RestTemplate,但找不到合适的方法。
【问题讨论】:
标签: java json spring jackson resttemplate
您可以尝试从您的控制器返回一个 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)
到你的映射类。
来自docs:
定义是否可以忽略任何无法识别的属性 反序列化期间的属性。如果为真,则所有属性 unrecognized - 也就是说,没有接受它们的设置者或创建者 -- 在没有警告的情况下被忽略(尽管未知属性的处理程序, 如果有的话,仍然会被调用),无一例外。 对序列化没有任何影响。
【讨论】:
另一种方法是通过实现 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();
}
}
【讨论】: