如何使用 restTemplate 保留在 500 内部异常期间抛出的错误消息

How to preserve the error message that is thrown during a 500 internal exception using restTemplate

我想知道在调用多个链接的微服务时保留错误消息的最佳做法是什么:我有一个 angular 前端调用后端休息服务调用另一个休息服务调用另一个第 3 方服务。

第 3 方服务有点不可靠。我希望将该服务的响应传播到我的前端。

所以为了演示问题更容易。

我在下游项目中有一个控件 class(单独的 micro-service/application)

@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”);
    }
}

另一方面micro-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 - 就像我的 "Own custom message" 的 550。但是您不能在运行时动态设置 HttpStatus 代码的消息。甚至不确定这是否是正确的冒险或走下坡路。

最后我的解决方案是根据Amit的建议

我最终创建了一个扩展弹簧 ResponseEntityExceptionHandler 的自定义 class。如果这是在您的 springboot 应用程序的 class 路径上,它将在从控制器返回异常之前拦截异常。我还创建了自己的例外。这样做的原因是,如果我希望触发我的功能,我会触发我自己的异常,其他人仍然可以按照正常方式进行。它可以随时更改。

同样在客户端,我不得不将异常的 getBody() JSON 转换为我的异常。但我不知道这是否是我的例外。所以我也添加了一些 HTTP header。在客户端,我检查 header 是否存在,然后我知道 body 是我的例外,我可以轻松地将 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);
    }

}

如果我是你,我想创建一个控制器建议来处理所有类型的异常。然后我想创建一个 ErrorMessage class,它将根据要求具有自定义 errorCode、errorMessage 字段。根据此控制器建议,对于应用程序中发生的任何类型的异常,它将创建一个 ErrorMessage 实例,其中包含 errorCode 和 errorMessage 等详细信息,并包装到 ResponseEntity 对象(具有 HTTP 状态)和 return 到其他微服务。

在消费者端检查响应状态并采取相应行动。

我认为您正在寻找的答案是创建 ExceptionMapper 的实现。该接口旨在处理映射到 Response 的 java 异常。

在您的情况下,如果第 3 部分抛出由 ExceptionMapper 实现处理的异常,您可以访问错误消息和响应中的 return。

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
    }