java 自定义 Zuul 异常
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/36461493/
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
Customizing Zuul Exception
提问by Grinish Nepal
I have a scenario in Zuul where the service that the URL is routed too might be down . So the reponse body gets thrown with 500 HTTP Status and ZuulException in the JSON body response.
我在 Zuul 中有一个场景,其中路由 URL 的服务也可能关闭。因此响应主体在 JSON 主体响应中被抛出 500 HTTP 状态和 ZuulException。
{
"timestamp": 1459973637928,
"status": 500,
"error": "Internal Server Error",
"exception": "com.netflix.zuul.exception.ZuulException",
"message": "Forwarding error"
}
All I want to do is to customise or remove the JSON response and maybe change the HTTP status Code.
我想要做的就是自定义或删除 JSON 响应,并可能更改 HTTP 状态代码。
I tried to create a exception Handler with @ControllerAdvice but the exception is not grabbed by the handler.
我试图用@ControllerAdvice 创建一个异常处理程序,但处理程序没有抓取异常。
UPDATES:
更新:
So I extended the Zuul Filter I can see it getting into the run method after the error has been executed how do i change the response then. Below is what i got so far. I read somewhere about SendErrorFilter but how do i implement that and what does it do?
所以我扩展了 Zuul 过滤器,我可以看到它在执行错误后进入 run 方法,然后我该如何更改响应。以下是我到目前为止所得到的。我在某处阅读了有关 SendErrorFilter 的内容,但我如何实现它以及它的作用是什么?
public class CustomFilter extends ZuulFilter {
@Override
public String filterType() {
return "post";
}
@Override
public int filterOrder() {
return 1;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
final RequestContext ctx = RequestContext.getCurrentContext();
final HttpServletResponse response = ctx.getResponse();
if (HttpStatus.INTERNAL_SERVER_ERROR.value() == ctx.getResponse().getStatus()) {
try {
response.sendError(404, "Error Error"); //trying to change the response will need to throw a JSON body.
} catch (final IOException e) {
e.printStackTrace();
} ;
}
return null;
}
Added this to the class that has @EnableZuulProxy
将此添加到具有 @EnableZuulProxy 的类
@Bean
public CustomFilter customFilter() {
return new CustomFilter();
}
回答by Grinish Nepal
We finally got this working [Coded by one of my colleague]:-
我们终于得到了这个工作[由我的一位同事编码]:-
public class CustomErrorFilter extends ZuulFilter {
private static final Logger LOG = LoggerFactory.getLogger(CustomErrorFilter.class);
@Override
public String filterType() {
return "post";
}
@Override
public int filterOrder() {
return -1; // Needs to run before SendErrorFilter which has filterOrder == 0
}
@Override
public boolean shouldFilter() {
// only forward to errorPath if it hasn't been forwarded to already
return RequestContext.getCurrentContext().containsKey("error.status_code");
}
@Override
public Object run() {
try {
RequestContext ctx = RequestContext.getCurrentContext();
Object e = ctx.get("error.exception");
if (e != null && e instanceof ZuulException) {
ZuulException zuulException = (ZuulException)e;
LOG.error("Zuul failure detected: " + zuulException.getMessage(), zuulException);
// Remove error code to prevent further error handling in follow up filters
ctx.remove("error.status_code");
// Populate context with new response values
ctx.setResponseBody(“Overriding Zuul Exception Body”);
ctx.getResponse().setContentType("application/json");
ctx.setResponseStatusCode(500); //Can set any error code as excepted
}
}
catch (Exception ex) {
LOG.error("Exception filtering in custom error filter", ex);
ReflectionUtils.rethrowRuntimeException(ex);
}
return null;
}
}
回答by Vasile Rotaru
I had the same problem and was able to solve it in simpler way
我遇到了同样的问题,并且能够以更简单的方式解决它
Just put this into you Filter run()
method
只需将其放入您的 Filterrun()
方法中
if (<your condition>) {
ZuulException zuulException = new ZuulException("User message", statusCode, "Error Details message");
throw new ZuulRuntimeException(zuulException);
}
and SendErrorFilter
will deliver to the user the message with the desired statusCode
.
和SendErrorFilter
将提供给用户的消息与期望的statusCode
。
This Exception in an Exception pattern does not look exactly nice, but it works here.
异常模式中的这个异常看起来不太好,但它在这里工作。
回答by xxxception
The Zuul RequestContext doesn't contain the error.exception
as mentioned in this answer.
Up to date the Zuul error filter:
Zuul RequestContext 不包含本答案中error.exception
提到的。
最新的 Zuul 错误过滤器:
@Component
public class ErrorFilter extends ZuulFilter {
private static final Logger LOG = LoggerFactory.getLogger(ErrorFilter.class);
private static final String FILTER_TYPE = "error";
private static final String THROWABLE_KEY = "throwable";
private static final int FILTER_ORDER = -1;
@Override
public String filterType() {
return FILTER_TYPE;
}
@Override
public int filterOrder() {
return FILTER_ORDER;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
final RequestContext context = RequestContext.getCurrentContext();
final Object throwable = context.get(THROWABLE_KEY);
if (throwable instanceof ZuulException) {
final ZuulException zuulException = (ZuulException) throwable;
LOG.error("Zuul failure detected: " + zuulException.getMessage());
// remove error code to prevent further error handling in follow up filters
context.remove(THROWABLE_KEY);
// populate context with new response values
context.setResponseBody("Overriding Zuul Exception Body");
context.getResponse().setContentType("application/json");
// can set any error code as excepted
context.setResponseStatusCode(503);
}
return null;
}
}
回答by Stefan Isele - prefabware.com
Forwarding is often done by a filter, in this case the request does not even reach a controller. This would explain why your @ControllerAdvice does not work.
转发通常由过滤器完成,在这种情况下,请求甚至不会到达控制器。这将解释为什么您的 @ControllerAdvice 不起作用。
If you forward in the controller than the @ControllerAdvice should work. Check if spring creates an instance of the class annotated with @ControllerAdvice. For that place a breakpoint in the class and see whether it is hit.
如果您在控制器中转发,那么 @ControllerAdvice 应该可以工作。检查 spring 是否创建了用 @ControllerAdvice 注释的类的实例。为此,在类中放置一个断点,看看它是否被命中。
Add a breakpoint also in the controller method where the forwarding should happen. May be you accidently invoke another controller method than you inspect ?
在应该发生转发的控制器方法中也添加一个断点。可能您不小心调用了另一个控制器方法而不是检查?
These steps should help you resolve the issue.
这些步骤应该可以帮助您解决问题。
In your class annotated with @ControllerAdvice add an ExceptionHandler method annotated with @ExceptionHandler(Exception.class), that should catch every Exception.
在用@ControllerAdvice 注释的类中添加一个用@ExceptionHandler(Exception.class) 注释的ExceptionHandler 方法,它应该捕获每个异常。
EDIT :You can try to add your own filter that converts the error response returned by the Zuulfilter. There you can change the response as you like.
编辑:您可以尝试添加自己的过滤器来转换 Zuulfilter 返回的错误响应。在那里您可以根据需要更改响应。
How the error response can be customized is explained here :
此处解释了如何自定义错误响应:
exception handling for filter in spring
Placing the filter correctly may be a little tricky. Not exactly sure about the correct position, but you should be aware of the order of your filters and the place where you handle the exception.
正确放置过滤器可能有点棘手。不完全确定正确的位置,但您应该了解过滤器的顺序以及处理异常的位置。
If you place it before the Zuulfilter, you have to code your error handling after calling doFilter().
如果你把它放在 Zuulfilter 之前,你必须在调用 doFilter() 之后编写你的错误处理代码。
If you place it after the Zuulfilter, you have to code your error handling before calling doFilter().
如果将它放在 Zuulfilter 之后,则必须在调用 doFilter() 之前编写错误处理代码。
Add breakpoints in your filter before and after doFilter() may help to find the correct position.
在 doFilter() 之前和之后在过滤器中添加断点可能有助于找到正确的位置。
回答by Karim Masoud
Here are the steps to do it with @ControllerAdvice:
以下是使用@ControllerAdvice 执行此操作的步骤:
- First add a filter of type
error
and let it be run before theSendErrorFilter
in zuul itself. - Make sure to remove the key associated with the exception from the
RequestContext
to prevent theSendErrorFilter
from executing. - Use
RequestDispatcher
to forward the request to theErrorController
-- explained below. - Add a @RestController class and make it extends
AbstractErrorController
, and re-throw the exception again (add it in the step of executing your new error filter with (key, exception), get it from theRequestContext
in your controller).
- 首先添加一个类型的过滤器,
error
让它在SendErrorFilter
in zuul 本身之前运行。 - 确保从 中删除与异常关联的键
RequestContext
以防止SendErrorFilter
执行。 - 用于
RequestDispatcher
将请求转发到ErrorController
-- 解释如下。 - 添加一个@RestController 类并使其扩展
AbstractErrorController
,并再次重新抛出异常(在使用 (key, exception) 执行新错误过滤器的步骤中添加它,从RequestContext
控制器中获取它)。
The exception will now be caught in your @ControllerAdvice class.
现在将在您的 @ControllerAdvice 类中捕获异常。
回答by Ravi Gupta
The simplest solution is to follow first 4 steps.
1. Create your own CustomErrorController extends
AbstractErrorController which will not allow the
BasicErrorController to be called.
2. Customize according to your need refer below method from
BasicErrorController.
<pre><code>
@RequestMapping
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
Map<String, Object> body = getErrorAttributes(request,
isIncludeStackTrace(request, MediaType.ALL));
HttpStatus status = getStatus(request);
return new ResponseEntity<>(body, status);
}
</pre></code>
4. You can control whether you want exception / stack trace to be printed or not can do as mentioned below:
<pre><code>
server.error.includeException=false
server.error.includeStacktrace=ON_TRACE_PARAM
</pre></code>
====================================================
5. If you want all together different error response re-throw your custom exception from your CustomErrorController and implement the Advice class as mentioned below:
<pre><code>
@Controller
@Slf4j
public class CustomErrorController extends BasicErrorController {
public CustomErrorController(ErrorAttributes errorAttributes, ServerProperties serverProperties,
List<ErrorViewResolver> errorViewResolvers) {
super(errorAttributes, serverProperties.getError(), errorViewResolvers);
log.info("Created");
}
@Override
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL));
HttpStatus status = getStatus(request);
throw new CustomErrorException(String.valueOf(status.value()), status.getReasonPhrase(), body);
}
}
@ControllerAdvice
public class GenericExceptionHandler {
// Exception handler annotation invokes a method when a specific exception
// occurs. Here we have invoked Exception.class since we
// don't have a specific exception scenario.
@ExceptionHandler(CustomException.class)
@ResponseBody
public ErrorListWsDTO customExceptionHandle(
final HttpServletRequest request,
final HttpServletResponse response,
final CustomException exception) {
LOG.info("Exception Handler invoked");
ErrorListWsDTO errorData = null;
errorData = prepareResponse(response, exception);
response.setStatus(Integer.parseInt(exception.getCode()));
return errorData;
}
/**
* Prepare error response for BAD Request
*
* @param response
* @param exception
* @return
*/
private ErrorListWsDTO prepareResponse(final HttpServletResponse response,
final AbstractException exception) {
final ErrorListWsDTO errorListData = new ErrorListWsDTO();
final List<ErrorWsDTO> errorList = new ArrayList<>();
response.setStatus(HttpStatus.BAD_REQUEST.value());
final ErrorWsDTO errorData = prepareErrorData("500",
"FAILURE", exception.getCause().getMessage());
errorList.add(errorData);
errorListData.setErrors(errorList);
return errorListData;
}
/**
* This method is used to prepare error data
*
* @param code
* error code
* @param status
* status can be success or failure
* @param exceptionMsg
* message description
* @return ErrorDTO
*/
private ErrorWsDTO prepareErrorData(final String code, final String status,
final String exceptionMsg) {
final ErrorWsDTO errorDTO = new ErrorWsDTO();
errorDTO.setReason(code);
errorDTO.setType(status);
errorDTO.setMessage(exceptionMsg);
return errorDTO;
}
}
</pre></code>