我想知道当调用多个链式微服务时,保存错误信息的最佳实践是什么。我有一个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--就像一个550的 "自己的自定义消息 "一样,但你不能为HttpStatus代码动态设置消息,也就是在Runtime。但是你不能动态地设置HttpStatus代码的消息,也就是在Runtime。甚至不知道这是否是正确的冒险或道路。
根据Amit的建议,我最终的解决方案是
最后,我终于创建了一个自定义类,该类扩展了springs ResponseEntityExceptionHandler
. 如果这个在你的springboot应用的类路径上,它将在从控制器返回之前拦截异常。我还创建了自己的异常。原因是这样如果我想让我的功能触发,我就启动我自己的异常,而其他人仍然可以按照正常的方式。可以随时更改。
另外在客户端我还得把异常的getBody()JSON投给我的异常。但我不知道它是否是我的异常开始的。所以我还添加了一些HTTP头。在客户端,我检查那个头是否存在,然后我知道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类,它将根据需求有自定义的errorCode、errorMessage字段。从这个控制器建议,对于应用程序中发生的任何类型的异常,它将创建一个ErrorMessage实例,其中包含错误代码和错误信息等细节,并将其包装成ResponseEntity对象(带有HTTP状态),然后返回给其他微服务。
在消费者端检查响应状态并采取相应的行动。
我想你要找的答案是创建一个实现的 ExceptionMapper. 该接口被设计用来处理映射到Response的java异常。
在你的例子中,如果第3部分抛出一个异常,而这个异常是由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
}