Spring RestTemplate 与任何非 200 OK 响应交换 POST HttpClientException

Posted

技术标签:

【中文标题】Spring RestTemplate 与任何非 200 OK 响应交换 POST HttpClientException【英文标题】:Spring RestTemplate exchange POST HttpClientException with any non 200 OK response 【发布时间】:2018-10-29 16:39:13 【问题描述】:

所以我目前遇到的问题是我得到了一个

Servlet.service() 用于 servlet [dispatcherServlet] 在上下文中的路径 [] 抛出异常 [请求处理失败;嵌套异常是 org.springframework.web.client.HttpClientErrorException: 404 null] 有根本原因 org.springframework.web.client.HttpClientErrorException 404 null

在我的客户端代码中,当我的服务器代码响应不是 OK 200 响应时。因此,在这种情况下,我故意在我的服务器代码中返回一个 404 响应,并使用标头和正文,并且没有标头和正文,但我仍然得到与我在服务器代码中响应的 HTTP 状态代码相同的异常和null 在这种情况下,我认为它是响应的主体。最初在我的服务器代码中,我总是返回一个ResponseEntity<>("Sent", HttpStatus.OK),但是由于在我的服务器代码中,我在其他地方发出 HTTP 请求,如果它以 200 OK 以外的任何内容进行响应,那么我的客户端代码将不知道它,因此我将返回从我的服务器代码中的 HTTP 请求返回的实际响应返回到我遇到此问题的客户端代码。

客户端代码

public String callFruitBasket(Fruit fruitRequest) 
    HttpHeaders requestHeaders = new HttpHeaders();
    requestHeaders.setContentType(MediaType.APPLICATION_JSON);

    HttpEntity<Fruit> fruit = new HttpEntity<>(fruitRequest, requestHeaders);
    System.out.println("Fruit headers: " + fruit.getHeaders());

    ResponseEntity<String> response = restTemplate.exchange(fruitBasketUrl, HttpMethod.POST, fruit, String.class);
    System.out.println("Full response: " + response);

    return response.getBody();

服务器代码

@PostMapping("/fruitBasket/send")
public ResponseEntity<String> sendFruitBasket(@RequestBody Fruit fruit) 
   // works fine, old response
   // return new ResponseEntity<>("Sent", HttpStatus.OK);

   return new ResponseEntity<>("Baaad Request", HttpStatus.BAD_REQUEST);

因此,目前在我的服务器代码中,我没有在响应中添加任何标头,但我已经尝试添加 Content-Type 但是我发现我的客户端代码中仍然出现相同的异常,所以我是 100 % 确定问题出在我的客户端代码中。最初,当我通过 200 OK 响应时,完整响应的打印输出在客户端代码中很好,并显示:

Full Response: <200 OK,Sent,Content-Type=[text/plain;charset=UTF-8], Content-Length=[4], Date=[Sat, 19 May 2018 09:10:21 GMT]>

我对任何其他 Http 状态代码(如 400 和 404)的期望是相同的,但响应中使用 400 或 404 而不是 200 OK。 我已经尝试过使用客户端和服务器代码中的标头,正如我在此处的各种帖子中所读到的那样,这通常是出现此类异常时的原因,这使我相信我的客户端可能缺少一些基本的东西代码或这是 exchange() 的预期行为,我误解了它。

【问题讨论】:

您是否没有得到正确的响应代码和消息并且每次都得到 400。你能告诉我你是如何初始化 RestTemplate 的吗? @Art 现在解决了这个问题,谢谢,添加了 try catch 我可以从异常中检索所有内容。 【参考方案1】:

RestTemplate 在遇到错误响应代码时的默认行为是抛出异常。如果是 4xx,它是 HttpClientErrorException,如果是 5xx:HttpServerErrorException(都扩展 HttpStatusCodeException)。 Spring 通过使用 ResponseErrorHandler 实现了这一点(并且它是默认的 imlpementation - DefaultResponseErrorHandler)

处理这种情况的一种方法是抓住它们:

try 
    ResponseEntity<String> response = restTemplate.exchange(fruitBasketUrl, HttpMethod.POST, fruit, String.class);
 catch(HttpClientErrorException e) 
    //handle 4xx errors
 catch(HttpServerErrorException e) 
    //handle 5xx errors

如果您需要自定义此行为(某些其他 API 在向某些请求发送合法响应时使用这些代码,您可能希望像处理 2xx 响应一样处理这些请求),您可以通过以下方式创建自己的 ResponseErrorHandler 实现实现它或扩展DefaultResponseHandler,然后在初始化期间使用 RestTemplate 注册您的处理程序:

public class MyResponseErrorHandler extends DefaultResponseErrorHandler 

    @Override
    public boolean hasError(ClientHttpResponse response) throws IOException 
        // check if response code is an error in here or just use default implementation
    

    @Override
    public void handleError(ClientHttpResponse response) throws IOException 
        // handle different response codes
        // (default spring behaviour is throwing an exception)
    

并注册:

RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(new MyResponseErrorHandler());
// now RestTemplate's behaviour for error status codes is customized

【讨论】:

感谢您的回复,我应该可以接受 @htshame 提到的 try catch 已经对其进行了测试,但很高兴知道如果需要我可以进一步自定义它。【参考方案2】:

这里没有错。如果您收到错误的状态码,将引发此异常。

您只需将客户端代码包装在 try-catch 中并捕获异常,然后对它执行任何您想做的操作。

try 
    ResponseEntity<String> response = restTemplate.exchange(fruitBasketUrl, HttpMethod.POST, fruit, String.class);
 catch (HttpStatusCodeException e) 
    e.getMessage();

【讨论】:

干杯,我没想到将异常包装在 try catch >

以上是关于Spring RestTemplate 与任何非 200 OK 响应交换 POST HttpClientException的主要内容,如果未能解决你的问题,请参考以下文章

WebClient 与 RestTemplate

WebClient 非阻塞客户端 RestTemplate 阻塞式客户端

Spring WebClient vs. RestTemplate

Spring - WebClient & RestTemplate

Spring WebClient 作为 RestTemplate 的替代品

如何使用 spring 记录 RestTemplate 请求和响应?