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

Spring RestTemplate exchange POST HttpClientException with any non 200 OK response

所以我目前遇到的问题是

Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.web.client.HttpClientErrorException: 404 null] with root cause org.springframework.web.client.HttpClientErrorException 404 null

当我的服务器代码响应 OK 200 响应以外的任何内容时,在我的客户端代码中。所以在这种情况下,我故意在我的服务器代码中返回一个 404 响应,使用 header 和 body,并且没有 header 和 body,但我仍然得到我在服务器代码中响应的 HTTP 状态代码和 null 的相同异常,在这种情况下,我假设是响应的 body。最初在我的服务器代码中我总是返回一个 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);
}

所以目前在我的服务器代码中我没有在我的响应中添加任何 headers 但我已经尝试添加 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。 我试过在客户端和服务器代码中使用 headers,正如我在这里的各种帖子中读到的那样,这通常是导致出现此类异常的原因,这让我相信可能存在某些问题我的客户端代码中基本缺失,或者这是 exchange() 的预期行为,我误解了它。

这里没有任何问题。如果您收到错误的状态代码,将抛出此异常。

您只需要将客户端代码包装在 try-catch 中并捕获异常,然后用它做任何您想做的事情。

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

RestTemplate 在遇到错误响应代码时的默认行为是抛出异常。如果是 4xx,它是 HttpClientErrorException,如果是 5xx:HttpServerErrorException(都扩展 HttpStatusCodeException)。 Spring 通过使用 ResponseErrorHandler (and it's default 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