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();
}
}
我有一个 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();
}
}