java 在 Spring 中为 RestTemplate 客户端抛出 ResourceAccessException 与 HttpClientErrorException

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/45305811/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 08:37:04  来源:igfitidea点击:

Throwing ResourceAccessException vs HttpClientErrorException for RestTemplate client in Spring

javaspringrestweb

提问by Marcin

Using Spring 4.3.1 on Apache Tomcat 8.5, We have implemented a REST server that handles GET, POST & PUT requests. The POST request is handled in the form:

在 Apache Tomcat 8.5 上使用 Spring 4.3.1,我们实现了一个处理 GET、POST 和 PUT 请求的 REST 服务器。POST 请求以以下形式处理:

@POST
@Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
@Transactional
public Response postMethod(final MyDomain object) {
     domainHandler.createDomain(object);
     return Response.status(201).entity(object).build();
}

During handling POST request on the server side, the WebApplicationException is thrown, which is handled by:

在服务器端处理POST请求时,抛出WebApplicationException,由以下处理:

@Provider
public class WebExceptionMapper implements ExceptionMapper<WebApplicationException> {
     @Override
     public Response toResponse(WebApplicationException exception) {
          int httpStatus = exception.getResponse().getStatus();
          ErrorMessage em = new WebErrorMessage(exception.getMessage());
          return Response.status(httpStatus).entity(em).build();
     }
}

On the client side:

在客户端:

response = rest.exchange(requestURL, method, requestEntity, MyDomain.class);

the ResourceAccessException is thrown:

ResourceAccessException 被抛出:

2017-07-25 15:46:41,489 ERROR [com.my.code.web.presentation.ManagementController:63] (http-nio-8080-exec-7#34): Internal exception occured with cause:
org.springframework.web.client.ResourceAccessException: I/O error on POST request for "http://localhost:8080/": Server returned HTTP response code: 500 for URL: http://localhost:8080/; nested exception is java.io.IOException: Server returned HTTP response code: 500 for URL: http://localhost:8080/
     at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:633) ~[spring-web-4.3.1.RELEASE.jar:4.3.1.RELEASE]
     at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:580) ~[spring-web-4.3.1.RELEASE.jar:4.3.1.RELEASE]
     at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:498) ~[spring-web-4.3.1.RELEASE.jar:4.3.1.RELEASE]
     at com.my.code.web.integration.client.rest.FeedbackRestClient.callForApiCreateFeedback(FeedbackRestClient.java:218) ~[classes/:?]
     at com.my.code.web.integration.client.rest.FeedbackRestClient.pushFeedbackData(FeedbackRestClient.java:70) ~[classes/:?]
     at com.my.code.web.presentation.FeedbackController.feedback(FeedbackController.java:91) ~[classes/:?]
     at sun.reflect.GeneratedMethodAccessor72.invoke(Unknown Source) ~[?:?]
     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_131]
     at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_131]
     at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:221) ~[spring-web-4.3.1.RELEASE.jar:4.3.1.RELEASE]
     at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136) ~[spring-web-4.3.1.RELEASE.jar:4.3.1.RELEASE]
     at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:114) ~[spring-webmvc-4.3.1.RELEASE.jar:4.3.1.RELEASE]
     at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827) ~[spring-webmvc-4.3.1.RELEASE.jar:4.3.1.RELEASE]
     at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738) ~[spring-webmvc-4.3.1.RELEASE.jar:4.3.1.RELEASE]
     at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) ~[spring-webmvc-4.3.1.RELEASE.jar:4.3.1.RELEASE]
     at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963) [spring-webmvc-4.3.1.RELEASE.jar:4.3.1.RELEASE]
     at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897) [spring-webmvc-4.3.1.RELEASE.jar:4.3.1.RELEASE]
     at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970) [spring-webmvc-4.3.1.RELEASE.jar:4.3.1.RELEASE]
     at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861) [spring-webmvc-4.3.1.RELEASE.jar:4.3.1.RELEASE]
     at javax.servlet.http.HttpServlet.service(HttpServlet.java:635) [servlet-api.jar:?]
     at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846) [spring-webmvc-4.3.1.RELEASE.jar:4.3.1.RELEASE]
     at javax.servlet.http.HttpServlet.service(HttpServlet.java:742) [servlet-api.jar:?]
     at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) [catalina.jar:8.5.16]
     at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [catalina.jar:8.5.16]
     at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197) [spring-web-4.3.1.RELEASE.jar:4.3.1.RELEASE]
     at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.3.1.RELEASE.jar:4.3.1.RELEASE]
     at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [catalina.jar:8.5.16]
     at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [catalina.jar:8.5.16]
     at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) [tomcat-websocket.jar:8.5.16]
     at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [catalina.jar:8.5.16]
     at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [catalina.jar:8.5.16]
     at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198) [catalina.jar:8.5.16]
     at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [catalina.jar:8.5.16]
     at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:478) [catalina.jar:8.5.16]
     at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) [catalina.jar:8.5.16]
     at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:80) [catalina.jar:8.5.16]
     at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:624) [catalina.jar:8.5.16]
     at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) [catalina.jar:8.5.16]
     at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342) [catalina.jar:8.5.16]
     at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:799) [tomcat-coyote.jar:8.5.16]
     at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-coyote.jar:8.5.16]
     at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868) [tomcat-coyote.jar:8.5.16]
     at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1455) [tomcat-coyote.jar:8.5.16]
     at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-coyote.jar:8.5.16]
     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [?:1.8.0_131]
     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [?:1.8.0_131]
     at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-util.jar:8.5.16]
     at java.lang.Thread.run(Thread.java:748) [?:1.8.0_131]
Caused by: java.io.IOException: Server returned HTTP response code: 400 for URL: http://localhost:8080/
     at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1876) ~[?:1.8.0_131]
     at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1474) ~[?:1.8.0_131]
     at org.springframework.http.client.SimpleClientHttpResponse.getBody(SimpleClientHttpResponse.java:85) ~[spring-web-4.3.1.RELEASE.jar:4.3.1.RELEASE]
     at com.my.code.web.integration.client.rest.BufferingClientHttpResponseWrapper.getBody(BufferingClientHttpResponseWrapper.java:46) ~[classes/:?]
     at com.my.code.web.integration.client.rest.LoggingRequestInterceptor.traceResponse(LoggingRequestInterceptor.java:66) ~[classes/:?]
     at com.my.code.web.integration.client.rest.LoggingRequestInterceptor.intercept(LoggingRequestInterceptor.java:34) ~[classes/:?]
     at org.springframework.http.client.InterceptingClientHttpRequest$InterceptingRequestExecution.execute(InterceptingClientHttpRequest.java:85) ~[spring-web-4.3.1.RELEASE.jar:4.3.1.RELEASE]
     at org.springframework.http.client.InterceptingClientHttpRequest.executeInternal(InterceptingClientHttpRequest.java:69) ~[spring-web-4.3.1.RELEASE.jar:4.3.1.RELEASE]
     at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48) ~[spring-web-4.3.1.RELEASE.jar:4.3.1.RELEASE]
     at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:53) ~[spring-web-4.3.1.RELEASE.jar:4.3.1.RELEASE]
     at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:619) ~[spring-web-4.3.1.RELEASE.jar:4.3.1.RELEASE]
     ... 47 more

Question:

问题:

Why the exception org.springframework.web.client.ResourceAccessExceptionis thrown, but not org.springframework.web.client.HttpClientErrorException, what's happening for handling the GET request when WebApplicationExceptionoccurs?

为什么org.springframework.web.client.ResourceAccessException抛出异常,而不是org.springframework.web.client.HttpClientErrorException,发生时处理 GET 请求WebApplicationException会发生什么?

The ResourceAccessExceptionexception does not have methods that allow me to read the HTTP status, eg getRawStatusCode (), getResponseHeaders(), etc.

ResourceAccessException异常不会有让我读HTTP状态,例如使用的方法getRawStatusCode ()getResponseHeaders()等等。

回答by OrangeDog

Because it's wrapping an IOException, not looking at the actual response code.

因为它包装了一个IOException,而不是查看实际的响应代码。

The root cause appears to be because BufferingClientHttpResponseWrapperis trying to read a body without checking if there is one first.

根本原因似乎是因为BufferingClientHttpResponseWrapper试图读取正文而不先检查是否有正文。

回答by jeremysprofile

As Yasammez said in the comments, it is a known, fixed bugthat you get ResourceAccessExceptioninstead of a subclass of RestClientExceptionsuch as HttpClientErrorExceptionand has been fixed as of Spring 3.0.7.

正如 Yasammez 在评论中所说的那样,这是一个已知的、已修复的错误ResourceAccessException而不是RestClientException诸如此类的子类,HttpClientErrorException并且已从 Spring 3.0.7 开始修复。

The best I could do was catch both and only ask for the message, not the status code:

我能做的最好的事情就是同时捕获两者,只询问消息,而不是状态代码:

try {
    #method that calls restTemplate.getForObject(...) or similar
} catch (RestClientResponseException | ResourceAccessException e) {
    log.info("Failed to get remote resource because: " + e.getMessage());
}

You could also have 2 different catch statements and have one catch with a more verbose log if it gets there.

您还可以有 2 个不同的 catch 语句,如果到达那里,则有一个带有更详细日志的 catch 语句。