Spring Frameworkã® RestTemplate.class ã使ã£ãéä¿¡ã®ã¨ã©ã¼ãã³ããªã³ã°ã®ä»æ¹ã«ã¤ãã¦ã¾ã¨ãã¾ãã
ä»åã®ç°å¢
- Java 17
- Spring Boot 2.7.4
試ãã¦ã¿ã
Client APIãããªã¯ã¨ã¹ããåããAPIï¼Serverï¼
@RestController @RequestMapping("/server") public class ServerController { @GetMapping public ServerResponse index(@RequestParam(value = "status_code", required = false) Optional<Integer> statusCode) { HttpStatus status = statusCode .map(HttpStatus::resolve) .orElse(null); if (status != null && status.isError()) { throw new CustomException(status.getReasonPhrase(), status); } else { return new ServerResponse(1, "name"); } } @ExceptionHandler(CustomException.class) public ResponseEntity<ErrorResponse> handle(CustomException e) { return ResponseEntity .status(e.getStatus()) .body(new ErrorResponse(e.getMessage())); } }
public record ServerResponse(Integer id, String name) {
}
public record ErrorResponse (String message) {
}
ãªã¯ã¨ã¹ããã©ã¡ã¼ã¿ã§æå®ããã¹ãã¼ã¿ã¹ã³ã¼ãã400ç³»ã500ç³»ã ã£ããã¨ã©ã¼ã¬ã¹ãã³ã¹ãããã以å¤ã¯æ£å¸¸ç³»ã®ã¬ã¹ãã³ã¹ãè¿ãAPIã§ãã
Server APIã«ãªã¯ã¨ã¹ããæããAPIï¼Clientï¼
@RestController @RequestMapping("/client") public class ClientController { private final ServerService serverService; public ClientController(ServerService serverService) { this.serverService = serverService; } @GetMapping("/default") public ResponseEntity<ClientResponse> defaultHandler(@RequestParam(value = "status_code", required = false) Optional<Integer> statusCode) throws ServerRestTemplateException { ResponseEntity<ServerResponse> serverResponse = serverService.defaultHandlerGet(statusCode); return ResponseEntity.status(serverResponse.getStatusCode()) .body(ClientResponse.newInstance(serverResponse.getBody())); } @GetMapping("/custom") public ResponseEntity<ClientResponse> customHandler(@RequestParam(value = "status_code", required = false) Optional<Integer> statusCode) throws ServerRestTemplateException { ResponseEntity<ServerResponse> serverResponse = serverService.customHandlerGet(statusCode); if (serverResponse.getStatusCode().isError()) { // 4xx, 5xxã®å ´åãResponseEntityã®ã¬ã¹ãã³ã¹ããã£ã¯ãnew ServerResponse(null, null)ã¨ãªã throw new ServerRestTemplateException(new ErrorResponse("custom handler error"), serverResponse.getStatusCode()); } return ResponseEntity.status(serverResponse.getStatusCode()) .body(ClientResponse.newInstance(serverResponse.getBody())); } @ExceptionHandler(ServerRestTemplateException.class) public ResponseEntity<ErrorResponse> handleServerRestTemplateException(ServerRestTemplateException e) { return ResponseEntity.status(e.getStatus()) .body(e.getResponse()); } }
@Service public class ServerService { private static final String BASE_URI = "http://localhost:8081/api"; private static final String BASE_PATH = "/server"; private final RestTemplate defaultHandlerRestTemplate; private final RestTemplate customHandlerRestTemplate; private final ObjectMapper objectMapper; public ServerService( RestTemplateBuilder defaultHandlerRestTemplateBuilder, RestTemplateBuilder customHandlerRestTemplateBuilder, ObjectMapper objectMapper) { this.defaultHandlerRestTemplate = defaultHandlerRestTemplateBuilder.rootUri(BASE_URI).build(); this.customHandlerRestTemplate = customHandlerRestTemplateBuilder.rootUri(BASE_URI).build(); this.objectMapper = objectMapper; } /** * RestTemplateã使ã£ãéä¿¡ã®ã¨ã©ã¼ãã³ããªã³ã°ã« {@link org.springframework.web.client.DefaultResponseErrorHandler} ãå©ç¨ */ public ResponseEntity<ServerResponse> defaultHandlerGet(Optional<Integer> statusCode) throws ServerRestTemplateException { var requestEntity = buildRequestEntity(statusCode); try { return defaultHandlerRestTemplate.exchange(requestEntity, ServerResponse.class); } catch (HttpStatusCodeException e) { try { var errorResponse = objectMapper.readValue(e.getResponseBodyAsString(), ErrorResponse.class); throw new ServerRestTemplateException(errorResponse, e.getStatusCode()); } catch (IOException ioException) { throw new ServerRestTemplateException("invalid response"); } } catch (RestClientException e) { throw new ServerRestTemplateException("error"); } } /** * RestTemplateã使ã£ãéä¿¡ã®ã¨ã©ã¼ãã³ããªã³ã°ã« {@link com.b1a9idps.client.externals.handler.RestTemplateResponseErrorHandler} ãå©ç¨ */ public ResponseEntity<ServerResponse> customHandlerGet(Optional<Integer> statusCode) { var requestEntity = buildRequestEntity(statusCode); return customHandlerRestTemplate.exchange(requestEntity, ServerResponse.class); } private RequestEntity<Void> buildRequestEntity(Optional<Integer> statusCode) { var builder = UriComponentsBuilder.fromPath(BASE_PATH); if (statusCode.isPresent()) { builder.queryParam("status_code", statusCode.get()); } var uri = builder.toUriString(); return RequestEntity.get(uri).build(); } }
å®è¡
Server APIãClient APIã¨ãã«èµ·åããServer APIã«ãªã¯ã¨ã¹ããæãã¦ã¿ã¾ãã
GET /api/client/default
ãDefaultResponseErrorHandlerã§ã¨ã©ã¼ãã³ããªã³ã°ããã¨ã³ããã¤ã³ãã§ã GET /api/client/custom
ã¯ResponseErrorHandlerãå®è£
ããã¯ã©ã¹ã§ã¨ã©ã¼ãã³ããªã³ã°ããã¨ã³ããã¤ã³ãã§ãã
% curl 'http://localhost:8080/api/client/default' | jq . { "id": 1, "name": "name" } % curl 'http://localhost:8080/api/client/custom' | jq . { "id": 1, "name": "name" } % curl 'http://localhost:8080/api/client/default?status_code=403' | jq . { "message": "Forbidden" } % curl 'http://localhost:8080/api/client/custom?status_code=403' | jq . { "message": "custom handler error" }
解説
ããããã®ã¨ã©ã¼ãã³ããªã³ã°ã«ã¤ãã¦è§£èª¬ãã¾ãã
DefaultResponseErrorHandlerã§ã¨ã©ã¼ãã³ããªã³ã°
ããã©ã«ãã ã¨ãRestTemplateã§éä¿¡ãã¦èµ·ããã¨ã©ã¼ï¼ã¹ãã¼ã¿ã¹ã³ã¼ã400ç³»ã500ç³»ï¼ã¯ãDefaultResponseErrorHandlerã§ãã³ããªã³ã°ããã¾ãã
該å½ã³ã¼ããè¦ã¦ã¿ãã¨ãã®ããã«å®è£ ããã¦ãããéä¿¡ãã¦ã¹ãã¼ã¿ã¹ã³ã¼ã400ç³»ã¨500ç³»ãè¿ã£ã¦ããã¨HttpClientErrorExceptionãç¶æ¿ããä¾å¤ã¯ã©ã¹ãæãããã¾ãã
@Override public void handleError(ClientHttpResponse response) throws IOException { HttpStatus statusCode = HttpStatus.resolve(response.getRawStatusCode()); if (statusCode == null) { byte[] body = getResponseBody(response); String message = getErrorMessage(response.getRawStatusCode(), response.getStatusText(), body, getCharset(response)); throw new UnknownHttpStatusCodeException(message, response.getRawStatusCode(), response.getStatusText(), response.getHeaders(), body, getCharset(response)); } handleError(response, statusCode); } protected void handleError(ClientHttpResponse response, HttpStatus statusCode) throws IOException { String statusText = response.getStatusText(); HttpHeaders headers = response.getHeaders(); byte[] body = getResponseBody(response); Charset charset = getCharset(response); String message = getErrorMessage(statusCode.value(), statusText, body, charset); switch (statusCode.series()) { case CLIENT_ERROR: throw HttpClientErrorException.create(message, statusCode, statusText, headers, body, charset); case SERVER_ERROR: throw HttpServerErrorException.create(message, statusCode, statusText, headers, body, charset); default: throw new UnknownHttpStatusCodeException(message, statusCode.value(), statusText, headers, body, charset); } }
ãªã®ã§ãHttpStatusCodeExceptionãcatchãã¦ãã¨ã©ã¼æã®ã¬ã¹ãã³ã¹ãHttpStatusCodeException#getResponseBodyAsStringã§åå¾ãã¦ãã¾ãã
public ResponseEntity<ServerResponse> defaultHandlerGet(Optional<Integer> statusCode) throws ServerRestTemplateException { var requestEntity = buildRequestEntity(statusCode); try { return defaultHandlerRestTemplate.exchange(requestEntity, ServerResponse.class); } catch (HttpStatusCodeException e) { try { var errorResponse = objectMapper.readValue(e.getResponseBodyAsString(), ErrorResponse.class); throw new ServerRestTemplateException(errorResponse, e.getStatusCode()); } catch (IOException ioException) { throw new ServerRestTemplateException("invalid response"); } } catch (RestClientException e) { throw new ServerRestTemplateException("error"); } }
ResponseErrorHandlerãå®è£ ããã¯ã©ã¹ã§ã¨ã©ã¼ãã³ããªã³ã°
RestTemplateã§éä¿¡ãã¦ã¨ã©ã¼ãçºçããéã«ä¾å¤ãæãã¦ã»ãããªãã¨ããå ´åã«ã¯ãResponseErrorHandlerãå®è£ ããã¯ã©ã¹ãç¨æãã¾ãã ãã®ä¾ã§ã¯ãã¹ãã¼ã¿ã¹ã³ã¼ã400ç³»ã¨500ç³»ãè¿ã£ã¦ãã¦ãä½ãããªãããã«å®è£ ãã¦ãã¾ãã
public class RestTemplateResponseErrorHandler extends DefaultResponseErrorHandler { @Override public void handleError(ClientHttpResponse response) throws IOException { } }
ããã¦ãRestTemplateã§ãã®ErrorHandlerãå©ç¨ããããã«ãã¾ãã
@Configuration(proxyBeanMethods = false) public class RestTemplateConfig { @Bean public RestTemplateBuilder defaultHandlerRestTemplateBuilder() { return new RestTemplateBuilder(); } @Bean public RestTemplateBuilder customHandlerRestTemplateBuilder() { return new RestTemplateBuilder() .errorHandler(new RestTemplateResponseErrorHandler()); } }
å
ã«èª¬æããããã«ãã¹ãã¼ã¿ã¹ã³ã¼ã400ç³»ã500ç³»ãè¿ã£ã¦ãã¦ãä½ãããªãã®ã§ã new ServerResponse(null, null)
ãã¬ã¹ãã³ã¹ããã£ã¨ãã¦è¿ããã¾ãã
public ResponseEntity<ServerResponse> customHandlerGet(Optional<Integer> statusCode) { var requestEntity = buildRequestEntity(statusCode); return customHandlerRestTemplate.exchange(requestEntity, ServerResponse.class); }
ã¬ã¹ãã³ã¹ããã£ããã¯ã¨ã©ã¼ãã©ãããå¤æã§ããªãã®ã§ãã¹ãã¼ã¿ã¹ã³ã¼ãã§ã¨ã©ã¼ãã©ãããå¤æãã¦ãã¾ãã
GetMapping("/custom") public ResponseEntity<ClientResponse> customHandler(@RequestParam(value = "status_code", required = false) Optional<Integer> statusCode) throws ServerRestTemplateException { ResponseEntity<ServerResponse> serverResponse = serverService.customHandlerGet(statusCode); if (serverResponse.getStatusCode().isError()) { // 4xx, 5xxã®å ´åãResponseEntityã®ã¬ã¹ãã³ã¹ããã£ã¯ãnew ServerResponse(null, null)ã¨ãªã throw new ServerRestTemplateException(new ErrorResponse("custom handler error"), serverResponse.getStatusCode()); } return ResponseEntity.status(serverResponse.getStatusCode()) .body(ClientResponse.newInstance(serverResponse.getBody())); } @ExceptionHandler(ServerRestTemplateException.class) public ResponseEntity<ErrorResponse> handleServerRestTemplateException(ServerRestTemplateException e) { return ResponseEntity.status(e.getStatus()) .body(e.getResponse()); }
ã¾ã¨ã
ResponseErrorHandlerãå®è£ ããã¯ã©ã¹ã§ã¨ã©ã¼ãã³ããªã³ã°ãè¡ãã¨ãä¾å¤å¦çãæ¸ããªãã¦è¯ããªããªãåé¢ãå¼ã³åºããAPIããã®ã¨ã©ã¼ã¬ã¹ãã³ã¹ã失ãããããªã¨æãã¾ããã¹ãã¼ã¿ã¹ã³ã¼ãããããã£ã¦å¼ã³åºãå´ã®ã·ã¹ãã ã§ã¨ã©ã¼ã¬ã¹ãã³ã¹ãå®å ¨ã«æ±ºããå ´åãªãæå¹ããªã¨æãã¾ãã
DefaultResponseErrorHandlerã§ã¨ã©ã¼ãã³ããªã³ã°ããå ´åã¯ãä¾å¤å¦çãæ¸ãã®ãåå«ã§ããå¼ã³åºããAPIããã®ã¨ã©ã¼ã¬ã¹ãã³ã¹ãè¿ããã¨ãã§ãã¾ããï¼ãã¾ãæ»ã£ã¦ããã¨ã©ã¼ã¬ã¹ãã³ã¹ããã®ã¾ã¾è¿ãã¨ããã±ã¼ã¹ã¯ãªããããããªãã§ããï¼