使用Spring的RestClient优雅地处理404错误

dnph8jn4  于 2024-01-05  发布在  Spring
关注(0)|答案(1)|浏览(131)

我目前正在使用Sping Boot 3.2/Spring Framework 5.1中的新RestClient,我遇到了处理404错误的挑战。我的目标是优雅地处理这些错误,而不会导致代码中的后续步骤失败,特别是在转换响应体时。
范例:

  1. class CmsClient {
  2. pubic Policy getPolicy() {
  3. return restClient.get()
  4. .uri("some/url")
  5. .accept(MediaType.APPLICATION_JSON)
  6. .retrieve()
  7. .onStatus(ignore404())
  8. .body(Policy.class);
  9. }
  10. public static ResponseErrorHandler ignore404() {
  11. return new ResponseErrorHandler() {
  12. @Override
  13. public boolean hasError(final ClientHttpResponse response) throws IOException {
  14. return response.getStatusCode() == HttpStatus.NOT_FOUND;
  15. }
  16. @Override
  17. public void handleError(final ClientHttpResponse response) {
  18. //Do nothing
  19. }
  20. };
  21. }
  22. }

字符串
这是我得到的例外:

  1. org.springframework.web.client.RestClientException: Error while extracting response for type [package.Policy] and content type [application/json]
  2. at org.springframework.web.client.DefaultRestClient.readWithMessageConverters(DefaultRestClient.java:216)
  3. at org.springframework.web.client.DefaultRestClient$DefaultResponseSpec.readBody(DefaultRestClient.java:641)
  4. at org.springframework.web.client.DefaultRestClient$DefaultResponseSpec.body(DefaultRestClient.java:589)
  5. at package.CmsClient.getPolicy(CmsClient.java:27)
  6. at package.CmsClientTest.getPolicy_404_ThrowException_Test(CmsClientTest.java:61)
  7. at java.base/java.lang.reflect.Method.invoke(Method.java:580)
  8. at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
  9. at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
  10. Caused by: org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: No content to map due to end-of-input
  11. at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:406)
  12. at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.java:354)
  13. at org.springframework.web.client.DefaultRestClient.readWithMessageConverters(DefaultRestClient.java:200)
  14. ... 7 more
  15. Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: No content to map due to end-of-input
  16. at [Source: (org.springframework.util.StreamUtils$NonClosingInputStream); line: 1, column: 0]
  17. at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:59)
  18. at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1752)
  19. at com.fasterxml.jackson.databind.ObjectReader._initForReading(ObjectReader.java:360)
  20. at com.fasterxml.jackson.databind.ObjectReader._bindAndClose(ObjectReader.java:2095)
  21. at com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:1481)
  22. at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:395)
  23. ... 9 more


我预计方法.body(Policy.class);应该返回null,而不是由于MismatchedInputException而触发异常。这种行为与之前在WebClient中观察到的行为一致,我希望在此上下文中有类似的方法。
当前解决方案:作为临时措施,可以在body方法调用中指定String.class。然而,这种方法会损害代码的流动性和易用性,需要使用ObjectMapper和类似工具进行额外处理。

nszi6y05

nszi6y051#

我通过注册一个自定义消息转换器来解决它:

  1. public RestClient create(String baseUrl, Duration timeout) {
  2. return RestClient.builder()
  3. .baseUrl("...")
  4. //addFirst is important
  5. .messageConverters(converterList -> converterList.addFirst(new JsonMessageConverter()))
  6. .build();
  7. }

字符串
这是我的自定义消息转换器:

  1. import org.jetbrains.annotations.NotNull;
  2. import org.springframework.http.HttpHeaders;
  3. import org.springframework.http.HttpInputMessage;
  4. import org.springframework.http.converter.HttpMessageNotReadableException;
  5. import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
  6. import java.io.ByteArrayInputStream;
  7. import java.io.IOException;
  8. import java.io.InputStream;
  9. import java.lang.reflect.Type;
  10. class JsonMessageConverter extends MappingJackson2HttpMessageConverter {
  11. @Override
  12. public Object read(@NotNull Type type, Class<?> contextClass, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
  13. var bytes = inputMessage.getBody().readAllBytes();
  14. if (bytes.length == 0) {
  15. return null;
  16. }
  17. return super.read(type, contextClass, new HttpInputMessageWrapper(new ByteArrayInputStream(bytes), inputMessage.getHeaders()));
  18. }
  19. private static class HttpInputMessageWrapper implements HttpInputMessage {
  20. private final InputStream body;
  21. private final HttpHeaders headers;
  22. private HttpInputMessageWrapper(InputStream body, HttpHeaders headers) {
  23. this.body = body;
  24. this.headers = headers;
  25. }
  26. @Override
  27. public @NotNull InputStream getBody() {
  28. return this.body;
  29. }
  30. @Override
  31. public @NotNull HttpHeaders getHeaders() {
  32. return this.headers;
  33. }
  34. }
  35. }


然后,我添加了我在问题中发布的错误处理程序来优雅地处理404:

  1. Optional<Policy> fetchDataRemotly() {
  2. var policy = restClient.get()
  3. .uri("...")
  4. .accept(MediaType.APPLICATION_JSON)
  5. .retrieve()
  6. .onStatus(RestClientFactory.ignore404())
  7. .body(Policy.class);
  8. /*
  9. * Variable 'policy' will be null if we get 404.
  10. */
  11. return Optional.ofNullable(policy);
  12. }

展开查看全部

相关问题