Как сохранить сообщение об ошибке, выдаваемое во время внутреннего исключения 500, с помощью restTemplate

Я хочу знать, как лучше всего сохранять сообщения об ошибках при вызове нескольких связанных микросервисов: у меня есть внешний интерфейс 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);
    }

}

person Chrispie    schedule 08.05.2020    source источник


Ответы (2)


На вашем месте я хотел бы создать совет контроллера для обработки всех видов исключений. Затем я хотел бы создать класс ErrorMessage, который будет иметь настраиваемые поля errorCode, errorMessage в соответствии с требованиями. Исходя из этого совета контроллера, для любых исключений, возникающих в приложении, он создаст экземпляр ErrorMessage с такими подробностями, как errorCode и errorMessage, завернет его в объект ResponseEntity (со статусом HTTP) и вернется к другим микросервисам.

На стороне потребителя проверьте статус ответа и действуйте соответственно.

person amit    schedule 08.05.2020
comment
Спасибо, я добавил решение, с которым я пошел, или, по крайней мере, часть на стороне сервера, основанную на вашем предложении. - person Chrispie; 28.05.2020

Я думаю, что ответ, который вы ищете, - это создание реализации ExceptionMapper. Интерфейс предназначен для обработки исключений Java, которые сопоставляются с Response.

В вашем случае, если третья часть выдает исключение, которое обрабатывается реализацией ExceptionMapper, вы можете получить доступ к сообщению об ошибке и вернуть его в ответ.

public class ServiceExceptionMapper implements ExceptionMapper<ServiceException>
{
    /**
     * {@inheritDoc}
     */
    @Override
    public Response toResponse(ServiceException exception)
    {
        //grab the message from the exception and return it in the response
    }
person user1555543    schedule 16.05.2020