Java Netflix Feign - 通过微服务传播状态和异常
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/38786207/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Netflix Feign - Propagate Status and Exception through Microservices
提问by Pau
I'm using Netflix Feignto call to one operation of a Microservice A to other other operation of a Microservice B which validates a code using Spring Boot.
我正在使用Netflix Feign将微服务 A 的一个操作调用到微服务 B 的其他操作,后者使用 Spring Boot 验证代码。
The operation of Microservice B throws an exception in case of the validation has been bad. Then I handled in the Microservices and return a HttpStatus.UNPROCESSABLE_ENTITY
(422) like next:
微服务 B 的操作在验证错误的情况下抛出异常。然后我在微服务中处理并返回一个HttpStatus.UNPROCESSABLE_ENTITY
(422),如下所示:
@ExceptionHandler({
ValidateException.class
})
@ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
@ResponseBody
public Object validationException(final HttpServletRequest request, final validateException exception) {
log.error(exception.getMessage(), exception);
error.setErrorMessage(exception.getMessage());
error.setErrorCode(exception.getCode().toString());
return error;
}
So, when Microservice A calls to B in a interface as next:
因此,当微服务 A 在接口中调用 B 时,如下所示:
@Headers("Content-Type: " + MediaType.APPLICATION_JSON_UTF8_VALUE)
@RequestLine("GET /other")
void otherOperation(@Param("other") String other );
@Headers("Content-Type: " + MediaType.APPLICATION_JSON_UTF8_VALUE)
@RequestLine("GET /code/validate")
Boolean validate(@Param("prefix") String prefix);
static PromotionClient connect() {
return Feign.builder()
.encoder(new GsonEncoder())
.decoder(new GsonDecoder())
.target(PromotionClient.class, Urls.SERVICE_URL.toString());
}
and the validations fails It returns a internal error 500 with next message:
并且验证失败它返回内部错误 500 并带有下一条消息:
{
"timestamp": "2016-08-05T09:17:49.939+0000",
"status": 500,
"error": "Internal Server Error",
"exception": "feign.FeignException",
"message": "status 422 reading Client#validate(String); content:\n{\r\n \"errorCode\" : \"VALIDATION_EXISTS\",\r\n \"errorMessage\" : \"Code already exists.\"\r\n}",
"path": "/code/validate"
}
But I need to return the same as the Microservice operation B.
但是我需要返回和微服务操作B一样的。
Wich would be the best ways or techniques to propagate Status and Exceptions through microservices using Netflix Feign?
使用 Netflix Feign 通过微服务传播状态和异常的最佳方法或技术是什么?
采纳答案by Mathias Dpunkt
You could use a feign ErrorDecoder
你可以使用假装 ErrorDecoder
https://github.com/OpenFeign/feign/wiki/Custom-error-handling
https://github.com/OpenFeign/feign/wiki/Custom-error-handling
Here is an example
这是一个例子
public class MyErrorDecoder implements ErrorDecoder {
private final ErrorDecoder defaultErrorDecoder = new Default();
@Override
public Exception decode(String methodKey, Response response) {
if (response.status() >= 400 && response.status() <= 499) {
return new MyBadRequestException();
}
return defaultErrorDecoder.decode(methodKey, response);
}
}
For spring to pick up the ErrorDecoder you have to put it on the ApplicationContext:
为了春天拿起 ErrorDecoder 你必须把它放在 ApplicationContext 上:
@Bean
public MyErrorDecoder myErrorDecoder() {
return new MyErrorDecoder();
}
回答by Tugrul
Write your custom exception mapper and register it. You can customize responses.
编写您的自定义异常映射器并注册它。您可以自定义响应。
Complete example is here
完整的例子在这里
public class GenericExceptionMapper implements ExceptionMapper<Throwable> {
@Override
public Response toResponse(Throwable ex) {
return Response.status(500).entity(YOUR_RETURN_OBJ_HERE).build();
}
}
回答by jebeaudet
Shameless plug for a little library I did that uses reflection to dynamically rethrow checked exceptions (and unchecked if they are on the Feign interface) based on an error code returned in the body of the response.
我做的一个小库的无耻插件,它使用反射根据响应正文中返回的错误代码动态重新抛出已检查的异常(如果它们在 Feign 接口上则未检查)。
More information on the readme : https://github.com/coveo/feign-error-decoder
关于自述文件的更多信息:https: //github.com/coveo/feign-error-decoder
回答by Asif Malek
What we do is as follows:
我们的做法如下:
Share common jar which contains exceptions with both microservices.
与两个微服务共享包含异常的公共 jar。
1.) In microservices A convert exception to a DTO class lets say ErrorInfo. Which will contain all the attributes of your custom exception with a String exceptionType, which will contain exception class name.
1.) 在微服务中将异常转换为 DTO 类可以说 ErrorInfo。它将包含您的自定义异常的所有属性和一个字符串异常类型,它将包含异常类名称。
2.) When it is received at microservice B it will be handled by ErrorDecoder in microservice B and It will try to create an exception object from exceptionType as below:
2.) 当微服务 B 接收到它时,它将由微服务 B 中的 ErrorDecoder 处理,并尝试从 exceptionType 创建一个异常对象,如下所示:
@Override
public Exception decode(String methodKey, Response response) {
ErrorInfo errorInfo = objectMapper.readValue(details, ErrorInfo.class);
Class exceptionClass;
Exception decodedException;
try {
exceptionClass = Class.forName(errorInfo.getExceptionType());
decodedException = (Exception) exceptionClass.newInstance();
return decodedException;
}
catch (ClassNotFoundException e) {
return new PlatformExecutionException(details, errorInfo);
}
return defaultErrorDecoder.decode(methodKey, response);
}
回答by saintf
Since 2017 we've created a library that does this from annotations (making it fairly easy to, just like for requests/etc, to code this up by annotations).
自 2017 年以来,我们创建了一个从注释中执行此操作的库(使得通过注释对其进行编码变得相当容易,就像对于请求/等一样)。
it basically allows you to code error handling as follows:
它基本上允许您按如下方式编码错误处理:
@ErrorHandling(codeSpecific =
{
@ErrorCodes( codes = {401}, generate = UnAuthorizedException.class),
@ErrorCodes( codes = {403}, generate = ForbiddenException.class),
@ErrorCodes( codes = {404}, generate = UnknownItemException.class),
},
defaultException = ClassLevelDefaultException.class
)
interface GitHub {
@ErrorHandling(codeSpecific =
{
@ErrorCodes( codes = {404}, generate = NonExistentRepoException.class),
@ErrorCodes( codes = {502, 503, 504}, generate = RetryAfterCertainTimeException.class),
},
defaultException = FailedToGetContributorsException.class
)
@RequestLine("GET /repos/{owner}/{repo}/contributors")
List<Contributor> contributors(@Param("owner") String owner, @Param("repo") String repo);
}
You can find it in the OpenFeign organisation: https://github.com/OpenFeign/feign-annotation-error-decoder
你可以在 OpenFeign 组织中找到它:https: //github.com/OpenFeign/feign-annotation-error-decoder
disclaimer: I'm a contributor to feign and the main dev for that error decoder.
免责声明:我是 feign 的贡献者,也是该错误解码器的主要开发人员。
回答by moe1
OpenFeign's FeignExceptiondoesn't bind to a specific HTTP status (i.e. doesn't use Spring's @ResponseStatus
annotation), which makes Spring default to 500
whenever faced with a FeignException
. That's okay because a FeignException
can have numerous causes that can't be related to a particular HTTP status.
OpenFeign 的FeignException不绑定到特定的 HTTP 状态(即不使用 Spring 的@ResponseStatus
注释),这使得 Spring500
在遇到FeignException
. 没关系,因为 aFeignException
可以有许多与特定 HTTP 状态无关的原因。
However you can change the way that Spring handles FeignExceptions
. Simply define an ExceptionHandler
that handles the FeignException
the way you need it (see here):
但是,您可以更改 Spring 处理的方式FeignExceptions
。只需定义一个以您需要的方式ExceptionHandler
处理它FeignException
的方法(请参阅此处):
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(FeignException.class)
public String handleFeignStatusException(FeignException e, HttpServletResponse response) {
response.setStatus(e.status());
return "feignError";
}
}
This example makes Spring return the same HTTP status that you received from Microservice B. You can go further and also return the original response body:
此示例使 Spring 返回您从微服务 B 收到的相同 HTTP 状态。您还可以进一步返回原始响应正文:
response.getOutputStream().write(e.content());