【问题标题】:ClientHttpResponse.getBody() throws ResourceAccessException when response code is 401ClientHttpResponse.getBody() 在响应码为 401 时抛出 ResourceAccessException
【发布时间】:2018-03-26 06:10:00
【问题描述】:

我正在尝试记录每个请求的请求-响应对。问题是当响应代码为401 时,ClientHttpResponse.getBody() 会抛出 ResourceAccessException 并且我无法读取响应正文。

这是 RestTemplate 配置

RestTemplate restTemplate = new RestTemplate();

// Added this requestFactory to make response object readable more than once.
ClientHttpRequestFactory requestFactory =
        new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory());
restTemplate.setRequestFactory(requestFactory);
restTemplate.getInterceptors().add(new RequestLoggingInterceptor(vCloudRequest.getAction(),httpHeaders));
    restTemplate.setErrorHandler(new RequestErrorHandler());

return restTemplate;

下面拦截器的最后一行抛出如下异常。

我该如何解决这个问题?

org.springframework.web.client.ResourceAccessException: I/O 错误 “https://example.com/api/sessions”的 POST 请求:服务器返回 HTTP 响应代码:401 用于 URL:https://example.com/api/sessions; 嵌套异常是 java.io.IOException:服务器返回 HTTP 响应 代码:401 用于 URL:https://example.com.11/api/sessions

这是拦截器的相关部分。

@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
    ClientHttpResponse response = execution.execute(request, body);

    String requestString = new String(body);

    // Calling this method because when we make a POST or PUT request and get an error
    // response.getBody() throws IOException. But if we call response.getStatusCode() it works fine.
    // I don't know the reason.
    // I asked a question on stackoverflow
    // https://stackoverflow.com/questions/47429978/resttemplate-response-getbody-throws-exception-on-4-and-5-errors-for-put-and
    response.getStatusCode();
    String responseString = new String(ByteStreams.toByteArray(response.getBody()), Charset.forName("UTF-8"));
...
}

这是自定义错误处理程序

公共类 RequestErrorHandler 实现 ResponseErrorHandler {

@Override
public boolean hasError(ClientHttpResponse response) throws IOException {
        if (!response.getStatusCode().is2xxSuccessful()) {
        return true;
    }
    return false;
}

@Override
public void handleError(ClientHttpResponse response) throws IOException {
    JAXBElement<ErrorType> root = null;

    try {
        JAXBContext jaxbContext = JAXBContext.newInstance(ErrorType.class);
        Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();

        root = jaxbUnmarshaller.unmarshal(new StreamSource(
                response.getBody()), ErrorType.class);
    } catch (JAXBException e) {
        throw new IOException("XML converting error. Cannot convert response to ErrorType");
    }

    ErrorType error = root.getValue();
    throw new VcloudException(error);
}
}

【问题讨论】:

  • 您期望401 的响应正文是什么?
  • 其实服务器是否发送响应体并不重要。 ,如果它没有发送任何响应正文,我想将 responseString 设置为空。但它会引发异常。
  • 你看到这个答案stackoverflow.com/a/47467572/4545442了吗?可能会有帮助..
  • 在您的错误消息Server returned HTTP response code: 401 for URL: https://example.com.11/api/sessions 中。此 URL 只是一个拼写错误还是实际上更改了要请求的 URL?
  • 另外,@SalihErikci 你确定异常是由response.getBody() 而不是ByteStreams.toByteArray(..) 引发的吗?因为如果抛出 I/O 异常,rest 模板倾向于用 ResourceAccessException 包装它

标签: java spring resttemplate


【解决方案1】:

使用 HttpComponentsClientHttpRequestFactory 代替 SimpleClientHttpRequestFactory。

同时添加对 'org.apache.httpcomponents:httpclient 的依赖。

当状态为 401 时,HttpComponentsClientHttpRequestFactory 可以读取正文。

【讨论】:

  • 我尝试了您的解决方案,但它给了我以下异常:sun.security.provider.certpath.SunCertPathBuilderException:无法找到请求目标的有效认证路径
  • 现在您遇到了另一个问题,与您的第一个问题没有严格关系。现在您必须导入证书。这个stackoverflow.com/questions/33497874/… 也许会有所帮助
【解决方案2】:

我不认为ResourceAccessExceptionresponse.getBody() 抛出,而是ByteStreams.toByteArray(...) 可能正在发送null 流作为响应。

如果response.getBody()null 或者它是ByteStreams.toByteArray(),您能否尝试调试。因为发生 I/O 错误时会抛出RescourceAccessException

【讨论】:

    【解决方案3】:

    当 httpstatus 不是 200 时,Spring RestTemplate 默认会生成错误。

    在我过去的项目中,我所做的是将自定义 org.springframework.web.client.ResponseErrorHandler 添加到其余模板中。 你可以这样写一个类:

    public class SimpleRestErrorHandler implements ResponseErrorHandler
    {
        private static final Logger logger = LoggerFactory.getLogger(SimpleRestErrorHandler.class.getName());
        @Override
        public boolean hasError(ClientHttpResponse response) throws IOException
        {
            int statusCode = response.getStatusCode().value();
            if( logger.isDebugEnabled() )
            {
    
                logger.debug("STATUS CODE ["+statusCode+"] ");
            }
            //Generate Error only when HTTP Status code is 500
            if( statusCode == 500 )
            {
    
                return true;
            }
            return false;
        }
    
        @Override
        public void handleError(ClientHttpResponse response) throws IOException
        {
          //Here you can manage the error (in this sample only the http status code = 500
        }
    
    }
    

    然后我将此添加到我的休息模板中:

    XML 配置示例

    <bean id="restTemplate" class="org.springframework.web.client.RestTemplate">        <constructor-arg ref="bufferingClientHttpRequestFactory" />         <property name="errorHandler" ref="simpleRestErrorHandler" />       <property name="messageConverters">             <list>
                    <bean
                        class="org.springframework.http.converter.StringHttpMessageConverter">
                        <constructor-arg value="#{T(java.nio.charset.Charset).forName('UTF-8')}" />
                    </bean>
                    <bean
                        class="org.springframework.http.converter.ByteArrayHttpMessageConverter" />
                    <bean
                        class="org.springframework.http.converter.ResourceHttpMessageConverter" />
                    <bean
                        class="org.springframework.http.converter.xml.SourceHttpMessageConverter" />
                    <bean
                        class="org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter" />
                    <bean
                        class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter" />
                    <bean
                        class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" />
                    <bean
                        class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" />          </list>         </property>         <property name="interceptors">          <!-- Custom interceptors -->        </property>     </bean> <bean id="bufferingClientHttpRequestFactory" name="bufferingClientHttpRequestFactory"       class="org.springframework.http.client.BufferingClientHttpRequestFactory">      <constructor-arg ref="simpleReqFact" />     </bean> <bean id="simpleRestErrorHandler" class="handlers.SimpleRestErrorHandler" />
    

    Java 配置

    @Config
    public class restTemplConfig {
     @Bean
     public RestTemplate restTempl() {
      RestTemplate result = new RestTemplate();
      //Add converters
      //Add custom error handler
      result.setErrorHandler(new SimpleRestErrorHandler());
     }
    }
    

    希望对你有用

    【讨论】:

    • 我已经有一个自定义处理程序并映射错误状态代码。但是在点击自定义错误处理程序之前,我在拦截器中得到了异常。
    • 可以发一下resttemplate的配置吗?
    • 添加了 resttemplate 配置。
    • 我看不到您将 ErrorHanlder 添加到 RestTemplate 的位置
    • 忘记添加该行。已添加
    猜你喜欢
    • 2015-08-02
    • 1970-01-01
    • 2017-12-31
    • 1970-01-01
    • 2013-11-16
    • 2017-05-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多