mkyong headers getforobject example create cliente spring rest resttemplate

headers - spring create resttemplate



Spring MVC-Excepción de inicio de RestTemplate cuando ocurre http 404 (5)

Dado que es 2018 y espero que cuando la gente diga "Primavera" realmente signifique "Bota de Primavera" al menos, quería expandir las respuestas con un enfoque menos cubierto de polvo.

Todo lo que se menciona en las respuestas anteriores es correcto: debe utilizar un ResponseErrorHandler personalizado. Ahora, en el mundo de Spring Boot, la forma de configurarlo es un poco más simple que antes. Hay una clase conveniente llamada RestTemplateBuilder . Si lees la primera línea de su documento java dice:

Generador que se puede utilizar para configurar y crear un RestTemplate. Proporciona métodos de conveniencia para registrar convertidores, controladores de errores y UriTemplateHandlers.

En realidad tiene un método solo para eso:

new RestTemplateBuilder().errorHandler(new DefaultResponseErrorHandler()).build();

Además de eso, los chicos de Spring se dieron cuenta de los inconvenientes de un RestTemplate convencional RestTemplate mucho tiempo, y cómo puede ser especialmente doloroso en las pruebas. Crearon una clase conveniente, TestRestTemplate , que sirve como un envoltorio alrededor de RestTemplate y establecen su errorHandler en una implementación vacía:

private static class NoOpResponseErrorHandler extends DefaultResponseErrorHandler { @Override public void handleError(ClientHttpResponse response) throws IOException { } }

Tengo un servicio de descanso que envía un error 404 cuando no se encuentran los recursos. Aquí la fuente de mi controlador y la excepción que envía Http 404.

@Controller @RequestMapping("/site") public class SiteController { @Autowired private IStoreManager storeManager; @RequestMapping(value = "/stores/{pkStore}", method = RequestMethod.GET, produces = "application/json") @ResponseBody public StoreDto getStoreByPk(@PathVariable long pkStore) { Store s = storeManager.getStore(pkStore); if (null == s) { throw new ResourceNotFoundException("no store with pkStore : " + pkStore); } return StoreDto.entityToDto(s); } } @ResponseStatus(value = HttpStatus.NOT_FOUND) public class ResourceNotFoundException extends RuntimeException { private static final long serialVersionUID = -6252766749487342137L; public ResourceNotFoundException(String message) { super(message); } }

Cuando trato de llamarlo con RestTemplate con este código:

ResponseEntity<StoreDto> r = restTemplate.getForEntity(url, StoreDto.class, m); System.out.println(r.getStatusCode()); System.out.println(r.getBody());

Recibo esta excepción:

org.springframework.web.client.RestTemplate handleResponseError ATTENTION: GET request for "http://........./stores/99" resulted in 404 (Introuvable); invoking error handler org.springframework.web.client.HttpClientErrorException: 404 Introuvable

Estaba pensando que puedo explorar mi objeto de respuesta de entidad y hacer algunas cosas con el código de estado. Pero la excepción es el lanzamiento y mi aplicación se cae.

¿Existe una configuración específica para que restTemplate no envíe una excepción, sino que complete mi ResponseEntity?

Muchas gracias por la ayuda.

-

Loïc


Por lo que sé, no puede obtener una ResponseEntity real, pero el código de estado y el cuerpo (si corresponde) se pueden obtener de la excepción:

try { ResponseEntity<StoreDto> r = restTemplate.getForEntity(url, StoreDto.class, m); } catch (final HttpClientErrorException e) { System.out.println(e.getStatusCode()); System.out.println(e.getResponseBodyAsString()); }


Puede crear su propio contenedor RestTemplate que no genera excepciones, pero devuelve una respuesta con el código de estado recibido. (También podría devolver el cuerpo, pero eso dejaría de ser seguro para el tipo, por lo que en el código debajo del cuerpo permanece simplemente null ).

/** * A Rest Template that doesn''t throw exceptions if a method returns something other than 2xx */ public class GracefulRestTemplate extends RestTemplate { private final RestTemplate restTemplate; public GracefulRestTemplate(RestTemplate restTemplate) { super(restTemplate.getMessageConverters()); this.restTemplate = restTemplate; } @Override public <T> ResponseEntity<T> getForEntity(URI url, Class<T> responseType) throws RestClientException { return withExceptionHandling(() -> restTemplate.getForEntity(url, responseType)); } @Override public <T> ResponseEntity<T> postForEntity(URI url, Object request, Class<T> responseType) throws RestClientException { return withExceptionHandling(() -> restTemplate.postForEntity(url, request, responseType)); } private <T> ResponseEntity<T> withExceptionHandling(Supplier<ResponseEntity<T>> action) { try { return action.get(); } catch (HttpClientErrorException ex) { return new ResponseEntity<>(ex.getStatusCode()); } } }


RESTTemplate es bastante deficiente en esta área IMO. Aquí hay una buena publicación en el blog sobre cómo posiblemente pueda extraer el cuerpo de la respuesta cuando reciba un error:

http://springinpractice.com/2013/10/07/handling-json-error-object-responses-with-springs-resttemplate

A partir de hoy, hay una solicitud pendiente de JIRA de que la plantilla ofrece la posibilidad de extraer el cuerpo de respuesta:

https://jira.spring.io/browse/SPR-10961

El problema con la respuesta de Squatting Bear es que tendrías que interrogar el código de estado dentro del bloque catch, por ejemplo, si solo quieres lidiar con los 404

Así es como resolví esto en mi último proyecto. Puede haber mejores formas y mi solución no extrae ResponseBody en absoluto.

public class ClientErrorHandler implements ResponseErrorHandler { @Override public void handleError(ClientHttpResponse response) throws IOException { if (response.getStatusCode() == HttpStatus.NOT_FOUND) { throw new ResourceNotFoundException(); } // handle other possibilities, then use the catch all... throw new UnexpectedHttpException(response.getStatusCode()); } @Override public boolean hasError(ClientHttpResponse response) throws IOException { return response.getStatusCode().series() == HttpStatus.Series.CLIENT_ERROR || response.getStatusCode().series() == HttpStatus.Series.SERVER_ERROR; }

La excepción ResourceNotFoundException y UnexpectedHttpException son mis propias excepciones no comprobadas.

La hora de crear la plantilla de resto:

RestTemplate template = new RestTemplate(); template.setErrorHandler(new ClientErrorHandler());

Ahora obtenemos la construcción ligeramente más limpia al realizar una solicitud:

try { HttpEntity response = template.exchange("http://localhost:8080/mywebapp/customer/100029", HttpMethod.GET, requestEntity, String.class); System.out.println(response.getBody()); } catch (ResourceNotFoundException e) { System.out.println("Customer not found"); }


Recientemente tuve un caso de uso para esto. Mi solución:

public class MyErrorHandler implements ResponseErrorHandler { @Override public boolean hasError(ClientHttpResponse clientHttpResponse) throws IOException { return hasError(clientHttpResponse.getStatusCode()); } @Override public void handleError(ClientHttpResponse clientHttpResponse) throws IOException { HttpStatus statusCode = clientHttpResponse.getStatusCode(); MediaType contentType = clientHttpResponse .getHeaders() .getContentType(); Charset charset = contentType != null ? contentType.getCharset() : null; byte[] body = FileCopyUtils.copyToByteArray(clientHttpResponse.getBody()); switch (statusCode.series()) { case CLIENT_ERROR: throw new HttpClientErrorException(statusCode, clientHttpResponse.getStatusText(), body, charset); case SERVER_ERROR: throw new HttpServerErrorException(statusCode, clientHttpResponse.getStatusText(), body, charset); default: throw new RestClientException("Unknown status code [" + statusCode + "]"); } } private boolean hasError(HttpStatus statusCode) { return (statusCode.series() == HttpStatus.Series.CLIENT_ERROR || statusCode.series() == HttpStatus.Series.SERVER_ERROR); }