Current implementation of exception handling in spring-cloud-zuul is quite ambigious. I tried to simplify it a little bit in my PR. Now I want to handle specific exceptions and return appropriate responses for them. Normally in any spring application I would create separate class annotated with ControllerAdvice and having methods with @ExceptionHandler for specific exception types. In spring-cloud-zuul exceptions get handled by BasicErrorController and none of ExceptionHandler annotated methods get executed. Is it a bug or an intenteded decision and what are the reasons?
We're also struggling with ZuulExceptions for whatever reason of downstream exception and it's really annoying.
Will be glad to see fix, thanks!
I wouldn't expect zuul exceptions to be handled by @ExceptionHandler. I'd like to wrap my head around the problems you are seeing with zuul exceptions.
For example if timeout occurs while routing to service client gets following response from Zuul:
{
"timestamp":1479314971425,
"status":500,
"error":"Internal Server Error",
"exception":"com.netflix.zuul.exception.ZuulException",
"message":"TIMEOUT"
}
This is default message that is produced by BasicErrorController. But I want to not only have my own message format in case of such or other ZuulException I would also like to handle exceptions the way it is done in standart spring applications via @ExceptionHandler. Right now to achieve this I need to extend AbstractErrorController and the basic implementation of custom exception handling looks like that:
@RestController
public class ZuulErrorController extends AbstractErrorController {
@Value("${error.path:/error}")
private String errorPath;
public ZuulErrorController(ErrorAttributes errorAttributes) {
super(errorAttributes);
}
@Override
public String getErrorPath() {
return errorPath;
}
@RequestMapping(value = "${error.path:/error}", produces = "application/json;charset=UTF-8")
public ResponseEntity error(HttpServletRequest request) {
HttpStatus status = getStatus(request);
final String errorMessage = getErrorMessage(request);
return ResponseEntity.status(status).body(generateErrorResponse(UNKNOWN_ERROR, errorMessage));
}
private String getErrorMessage(HttpServletRequest request) {
final Throwable exc = (Throwable) request.getAttribute("javax.servlet.error.exception");
return exc != null ? exc.getMessage() : "Unexpected error occurred";
}
}
Please advice how exception handling in Zuul can be done the right way.
Thanks.
When you say "spring application" I think you mean "spring mvc application". Zuul isn't a standard spring mvc application.
What does the response look like with your error controller?
Okay, I understand that. Still it would be nice to have something like @ExceptionHandler that gives an ability to code custom logic of exception handling in Zuul.
With my error controller I produce response that is common for our client:
{
"errorCode": -1,
"errorMessage": "Forwarding error"
}
Understood. Thanks for the update.
@spencergibb I have a similar use case my filter is calling an authentication method but if this authentication method throws any exception is there a way for me handle it in filter and then respond with a custom json format? BTW, I am currently using Camden.SR4 release. If any example will really help me. Thanks in advance.
I have no example.
@spencergibb This is what I did and could you please validate. Thanks in advance.
@stiyyagura We've solved a similar use case. I suggest that you open a StackOverflow question instead of taking over this thread, perhaps I can help you there. Put the link to your question here for reference.
@asarkar thank you for response. This is solved and old issue.
@stiyyagura Would you mind explaining what did you end up doing, so if someone stumbles upon this thread later, they'll have something to go on.
@asarkar I have explained my approach in the above. I am using error filter to handle the gateway errors and forwarding to /error path.
@stiyyagura
Error filter -> Error Controller -> ControllerAdvice
Is that right?
@spencergibb
When you say "spring application" I think you mean "spring mvc application". Zuul isn't a standard spring mvc application.
I've just looked into SendErrorFilter and it has following code inside run method:
RequestDispatcher dispatcher = request.getRequestDispatcher(
this.errorPath);
if (dispatcher != null) {
ctx.set(SEND_ERROR_FILTER_RAN, true);
if (!ctx.getResponse().isCommitted()) {
dispatcher.forward(request, ctx.getResponse());
}
}
It redirects rendering of exception to ErrorController of spring-boot. Isn't it a part of a spring-mvc application?
I suppose, that SendErrorFilter should be changed. It should not to redirect exception handling to spring-mvc part of an application. Please have a look at ErrorFilter in netflix. It handles exception and after it post filter is run that actually sends response to client.
@asarkar Yes it is ErrorFilter --> ErrorController --> ControllerAdvice.
And I give here more details:
error and let it be run before the SendErrorFilter in zuul itself.RequestContext to prevent the SendErrorFilter from executing.RequestDispatcher to forward the request to the ErrorController.AbstractErrorController), and re-throw the exception again (add it in the step of executing your new error filter with (key, exception), get it from the RequestContext in your controller).This module has entered maintenance mode. This means that the Spring Cloud team will no longer be adding new features to the module. We will fix blocker bugs and security issues, and we will also consider and review small pull requests from the community.
Most helpful comment
Understood. Thanks for the update.