Я хочу знать, как лучше всего сохранять сообщения об ошибках при вызове нескольких связанных микросервисов: у меня есть внешний интерфейс angular, который вызывает серверную службу отдыха, которая вызывает другую службу отдыха, которая вызывает другую стороннюю службу.
Сторонний сервис несколько ненадежен. И я хочу, чтобы ответ от этой службы распространялся на мой внешний интерфейс.
Итак, чтобы упростить демонстрацию проблемы.
У меня есть класс управления в нижестоящем проекте (отдельный микросервис/приложение)
@RestController
@RequestMapping("/my-down-stream-service")
public class MyController {
@RequestMapping(value = "my-method")
public MyCustomResponse method1() {
//Some complex logic that catch exceptions and propogates a nice little message
throw new RuntimeException(“This is my exception that indicates what the response is to my 3rd party service”);
}
}
В другом микросервисе, вызывающем указанную выше службу, у меня есть restTemplate, выполняющий вызов вышеуказанной службы.
public MyResponse doIt() {
try {
restTemplate.postForEntity(“MyUrl…”, req, MyResponse.class);
} catch (final HttpStatusCodeException ex) {
//If I add a break point and inspect the exception here
}
}
Я вижу, что это внутреннее исключение 500, которое отправляется во внешний интерфейс. Если я пойду и получу ex.getResponseBodyAsString()
, я верну карту JSON с фактическими деталями исключения.
{
"timestamp": "2020-05-06T22:17:08.401+0200",
"status": 500,
"error": "Internal Server Error",
"exception": "java.lang.RuntimeException",
"message": "This is my exception that indicates what the response is to my 3rd party service",
"path": "…"
}
И я могу преобразовать это в карту, получить часть сообщения, создать новое исключение и выдать его.
new ObjectMapper().readValue(ex.getResponseBodyAsString(), HashMap.class).get("message")
Но это похоже на большую работу, которую нужно реализовать везде, где мне это нужно. Есть ли лучший способ сделать это?
Я также попытался создать свой собственный HttpStatus - Like 550 с моим «собственным пользовательским сообщением». Но вы не можете динамически устанавливать сообщение для кода HttpStatus во время выполнения. Даже не уверен, что это правильное предприятие или путь, по которому нужно идти.
В конце концов мое решение основано на предложении Амита
В конце концов я создал собственный класс, который расширяет пружины ResponseEntityExceptionHandler
. Если это путь к классу вашего приложения Springboot, оно перехватит исключение, прежде чем вернуть его из контроллера. Я также создал свое собственное исключение. Причина в том, что если я хочу, чтобы моя функциональность запускалась, я запускаю свое собственное исключение, и все остальные могут по-прежнему следовать обычному пути. Его можно изменить в любое время.
Также на стороне клиента мне пришлось передать исключение getBody() JSON для моего исключения. Но я не знал, было ли это моим исключением с самого начала. Поэтому я также добавил заголовок HTTP. И на стороне клиента я проверяю, присутствует ли этот заголовок, тогда я знаю, что тело является моим исключением, и я могу легко преобразовать JSON в свое исключение.
@ControllerAdvice
public class MyRestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler(value = {MyCustomException.class})
protected ResponseEntity<Object> handleConflict(final MyCustomException ex, final HttpServletResponse response) {
if (!response.containsHeader("MYTAG")) {
response.addHeader("EX_TYPE", "MYTAG");
}
//here you can go wild as to what type of or just the normal 500
//return ResponseEntity.status(ex.getHttpStatus()).body(ex); // 500 internal exception
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ex);
}
}