Hi!
I'm trying to reject a request in a pre-filter (for example an authentication error).
For that, i'm doing the following in the run method:
if(!auth) {
ctx.unset();
ctx.setResponseStatusCode(401);
ctx.setResponseBody("example");
}
Doing this, zull log a nullpointer exception:
[ERROR] 2017-06-01 12:10:15.295 [qtp380274260-29] FilterProcessor - Filter threw Exception
com.netflix.zuul.exception.ZuulException: Filter threw Exception
at com.netflix.zuul.FilterProcessor.processZuulFilter(FilterProcessor.java:227) ~[zuul-core-1.3.0.jar!/:1.3.0]
at com.netflix.zuul.FilterProcessor.runFilters(FilterProcessor.java:157) ~[zuul-core-1.3.0.jar!/:1.3.0]
at com.netflix.zuul.FilterProcessor.error(FilterProcessor.java:105) [zuul-core-1.3.0.jar!/:1.3.0]
at com.netflix.zuul.ZuulRunner.error(ZuulRunner.java:112) [zuul-core-1.3.0.jar!/:1.3.0]
at com.netflix.zuul.http.ZuulServlet.error(ZuulServlet.java:145) [zuul-core-1.3.0.jar!/:1.3.0]
at com.netflix.zuul.http.ZuulServlet.service(ZuulServlet.java:90) [zuul-core-1.3.0.jar!/:1.3.0]
at org.springframework.web.servlet.mvc.ServletWrappingController.handleRequestInternal(ServletWrappingController.java:157) [spring-webmvc-4.3.8.RELEASE.jar!/:4.3.8.RELEASE]
at org.springframework.cloud.netflix.zuul.web.ZuulController.handleRequest(ZuulController.java:44) [spring-cloud-netflix-core-1.3.1.RELEASE.jar!/:1.3.1.RELEASE]
at org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter.handle(SimpleControllerHandlerAdapter.java:50) [spring-webmvc-4.3.8.RELEASE.jar!/:4.3.8.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963) [spring-webmvc-4.3.8.RELEASE.jar!/:4.3.8.RELEASE]
I have more pre-filters, post-filters, etc...
As a result:
The response status code is ok, but the response body isn't sent to the client and the nullpointerexception trace is logged...
I saw some examples, people saying that ctx.unset() method needs to be called in order to stop the request being routed, is this the correct way?
I tried to replace the ctx.unset() by ctx.setSendZuulResponse(false) and the effect is the same....
What is the right approach to implement this kind of situations where we want to stop the request and return a error code to the client with a body?
spring-cloud-starter-zuul: 1.3.1.RELEASE
spring-boot-starter-parent; 1.5.3.RELEASE
spring-cloud-dependencies: Dalston.SR1
I think you could do it like this in your auth-filter:
if (!auth) {
throw new ZuulRuntimeException(new ZuulException(
"not auth", HttpStatus.UNAUTHORIZED.value(), "not auth"));
}
And the response will be:
HTTP/1.1 401 Unauthorized
Content-Type: application/json;charset=utf-8
Transfer-Encoding: chunked
{"timestamp":1496314171649,"status":401,"error":"Unauthorized","exception":"com.netflix.zuul.exception.ZuulException","message":"not auth"}
just one solution to reject a request, hope helpful for you.
Hi lowzj,
Thank you.
I tried already your solution and is working more or less as expected.
Anyway, i continue seeing the exception being logged the post-filters are executed.
Here's the exception log:
[WARN ] 2017-06-01 12:57:51.442 [qtp380274260-34] SendErrorFilter - Error during filtering
com.netflix.zuul.exception.ZuulException: auth bad credentials
at com.pack.pre.AuthFilter.throwError(RateLimitFilter.java:115) ~[gateway-api-1.0.0-SNAPSHOT.jar!/:1.0.0-SNAPSHOT]
at com.pack.pre.AuthFilter.run(RateLimitFilter.java:81) ~[gateway-api-1.0.0-SNAPSHOT.jar!/:1.0.0-SNAPSHOT]
at com.netflix.zuul.ZuulFilter.runFilter(ZuulFilter.java:112) ~[zuul-core-1.3.0.jar!/:1.3.0]
at com.netflix.zuul.FilterProcessor.processZuulFilter(FilterProcessor.java:193) ~[zuul-core-1.3.0.jar!/:1.3.0]
at com.netflix.zuul.FilterProcessor.runFilters(FilterProcessor.java:157) ~[zuul-core-1.3.0.jar!/:1.3.0]
at com.netflix.zuul.FilterProcessor.preRoute(FilterProcessor.java:133) ~[zuul-core-1.3.0.jar!/:1.3.0]
at com.netflix.zuul.ZuulRunner.preRoute(ZuulRunner.java:105) ~[zuul-core-1.3.0.jar!/:1.3.0]
at com.netflix.zuul.http.ZuulServlet.preRoute(ZuulServlet.java:125) ~[zuul-core-1.3.0.jar!/:1.3.0]
at com.netflix.zuul.http.ZuulServlet.service(ZuulServlet.java:74) ~[zuul-core-1.3.0.jar!/:1.3.0]
Do you have the same behaviour? is this normal?
Is there a way to avoid post-filters being executed? exception log not being logged out?
Thanks in advance!
Yes, these log messages are expected, they are logged by the error filter SendErrorFilter#run().
If you don't want to log the exception, you could add a property: logging.level.org.springframework.cloud.netflix.zuul.filters.post.SendErrorFilter=error.
Usually, the post-filters in sc-zuul will not be executed. If you customize a post-filter, you should check RequestContext.getCurrentContext().getThrowable() == null in the method shouldFilter(), just like: SendResponseFilter#shouldFilter()
Thank you Lowzj!
That solves the problem!
Send error filter is usually used for exceptions (failures) during. Turning the error logging on the filter is a bad idea, then you won't know when real errors happen. I'd say run you pre filter after the PreDecorationFilter and remove the serviceId attribute (not unset());
I don't see a null pointer exception in your report either.
Hi @spencergibb ,
Thank you for your reply.
Here's the full stacktrace of the NPE (at the bottom):
```[ERROR] 2017-06-02 16:02:08.214 [qtp1375394559-61] FilterProcessor - Filter threw Exception
com.netflix.zuul.exception.ZuulException: Filter threw Exception
at com.netflix.zuul.FilterProcessor.processZuulFilter(FilterProcessor.java:227) ~[zuul-core-1.3.0.jar!/:1.3.0]
at com.netflix.zuul.FilterProcessor.runFilters(FilterProcessor.java:157) ~[zuul-core-1.3.0.jar!/:1.3.0]
at com.netflix.zuul.FilterProcessor.error(FilterProcessor.java:105) [zuul-core-1.3.0.jar!/:1.3.0]
at com.netflix.zuul.ZuulRunner.error(ZuulRunner.java:112) [zuul-core-1.3.0.jar!/:1.3.0]
at com.netflix.zuul.http.ZuulServlet.error(ZuulServlet.java:145) [zuul-core-1.3.0.jar!/:1.3.0]
at com.netflix.zuul.http.ZuulServlet.service(ZuulServlet.java:90) [zuul-core-1.3.0.jar!/:1.3.0]
at org.springframework.web.servlet.mvc.ServletWrappingController.handleRequestInternal(ServletWrappingController.java:157) [spring-webmvc-4.3.8.RELEASE.jar!/:4.3.8.RELEASE]
at org.springframework.cloud.netflix.zuul.web.ZuulController.handleRequest(ZuulController.java:44) [spring-cloud-netflix-core-1.3.1.RELEASE.jar!/:1.3.1.RELEASE]
at org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter.handle(SimpleControllerHandlerAdapter.java:50) [spring-webmvc-4.3.8.RELEASE.jar!/:4.3.8.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963) [spring-webmvc-4.3.8.RELEASE.jar!/:4.3.8.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897) [spring-webmvc-4.3.8.RELEASE.jar!/:4.3.8.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970) [spring-webmvc-4.3.8.RELEASE.jar!/:4.3.8.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861) [spring-webmvc-4.3.8.RELEASE.jar!/:4.3.8.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:687) [javax.servlet-api-3.1.0.jar!/:3.1.0]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846) [spring-webmvc-4.3.8.RELEASE.jar!/:4.3.8.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:790) [javax.servlet-api-3.1.0.jar!/:3.1.0]
at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:841) [jetty-servlet-9.4.4.v20170414.jar!/:9.4.4.v20170414]
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1634) [jetty-servlet-9.4.4.v20170414.jar!/:9.4.4.v20170414]
at org.springframework.boot.web.filter.ApplicationContextHeaderFilter.doFilterInternal(ApplicationContextHeaderFilter.java:55) [spring-boot-1.5.3.RELEASE.jar!/:1.5.3.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.3.8.RELEASE.jar!/:4.3.8.RELEASE]
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1621) [jetty-servlet-9.4.4.v20170414.jar!/:9.4.4.v20170414]
at org.springframework.boot.actuate.trace.WebRequestTraceFilter.doFilterInternal(WebRequestTraceFilter.java:110) [spring-boot-actuator-1.5.3.RELEASE.jar!/:1.5.3.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.3.8.RELEASE.jar!/:4.3.8.RELEASE]
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1621) [jetty-servlet-9.4.4.v20170414.jar!/:9.4.4.v20170414]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) [spring-web-4.3.8.RELEASE.jar!/:4.3.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.3.8.RELEASE.jar!/:4.3.8.RELEASE]
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1621) [jetty-servlet-9.4.4.v20170414.jar!/:9.4.4.v20170414]
at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:105) [spring-web-4.3.8.RELEASE.jar!/:4.3.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.3.8.RELEASE.jar!/:4.3.8.RELEASE]
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1621) [jetty-servlet-9.4.4.v20170414.jar!/:9.4.4.v20170414]
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81) [spring-web-4.3.8.RELEASE.jar!/:4.3.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.3.8.RELEASE.jar!/:4.3.8.RELEASE]
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1621) [jetty-servlet-9.4.4.v20170414.jar!/:9.4.4.v20170414]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197) [spring-web-4.3.8.RELEASE.jar!/:4.3.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.3.8.RELEASE.jar!/:4.3.8.RELEASE]
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1621) [jetty-servlet-9.4.4.v20170414.jar!/:9.4.4.v20170414]
at org.springframework.boot.actuate.autoconfigure.MetricsFilter.doFilterInternal(MetricsFilter.java:106) [spring-boot-actuator-1.5.3.RELEASE.jar!/:1.5.3.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.3.8.RELEASE.jar!/:4.3.8.RELEASE]
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1621) [jetty-servlet-9.4.4.v20170414.jar!/:9.4.4.v20170414]
at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:541) [jetty-servlet-9.4.4.v20170414.jar!/:9.4.4.v20170414]
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143) [jetty-server-9.4.4.v20170414.jar!/:9.4.4.v20170414]
at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:548) [jetty-security-9.4.4.v20170414.jar!/:9.4.4.v20170414]
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132) [jetty-server-9.4.4.v20170414.jar!/:9.4.4.v20170414]
at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:190) [jetty-server-9.4.4.v20170414.jar!/:9.4.4.v20170414]
at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1595) [jetty-server-9.4.4.v20170414.jar!/:9.4.4.v20170414]
at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:188) [jetty-server-9.4.4.v20170414.jar!/:9.4.4.v20170414]
at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1253) [jetty-server-9.4.4.v20170414.jar!/:9.4.4.v20170414]
at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:168) [jetty-server-9.4.4.v20170414.jar!/:9.4.4.v20170414]
at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:481) [jetty-servlet-9.4.4.v20170414.jar!/:9.4.4.v20170414]
at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1564) [jetty-server-9.4.4.v20170414.jar!/:9.4.4.v20170414]
at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:166) [jetty-server-9.4.4.v20170414.jar!/:9.4.4.v20170414]
at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1155) [jetty-server-9.4.4.v20170414.jar!/:9.4.4.v20170414]
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141) [jetty-server-9.4.4.v20170414.jar!/:9.4.4.v20170414]
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132) [jetty-server-9.4.4.v20170414.jar!/:9.4.4.v20170414]
at org.eclipse.jetty.server.Server.handle(Server.java:564) [jetty-server-9.4.4.v20170414.jar!/:9.4.4.v20170414]
at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:317) [jetty-server-9.4.4.v20170414.jar!/:9.4.4.v20170414]
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:251) [jetty-server-9.4.4.v20170414.jar!/:9.4.4.v20170414]
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:279) [jetty-io-9.4.4.v20170414.jar!/:9.4.4.v20170414]
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:110) [jetty-io-9.4.4.v20170414.jar!/:9.4.4.v20170414]
at org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:124) [jetty-io-9.4.4.v20170414.jar!/:9.4.4.v20170414]
at org.eclipse.jetty.util.thread.Invocable.invokePreferred(Invocable.java:128) [jetty-util-9.4.4.v20170414.jar!/:9.4.4.v20170414]
at org.eclipse.jetty.util.thread.Invocable$InvocableExecutor.invoke(Invocable.java:222) [jetty-util-9.4.4.v20170414.jar!/:9.4.4.v20170414]
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:294) [jetty-util-9.4.4.v20170414.jar!/:9.4.4.v20170414]
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:199) [jetty-util-9.4.4.v20170414.jar!/:9.4.4.v20170414]
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:672) [jetty-util-9.4.4.v20170414.jar!/:9.4.4.v20170414]
at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:590) [jetty-util-9.4.4.v20170414.jar!/:9.4.4.v20170414]
at java.lang.Thread.run(Unknown Source) [?:1.8.0_131]
Caused by: java.lang.NullPointerException
at org.springframework.cloud.netflix.zuul.filters.post.SendErrorFilter.run(SendErrorFilter.java:76) ~[spring-cloud-netflix-core-1.3.1.RELEASE.jar!/:1.3.1.RELEASE]
at com.netflix.zuul.ZuulFilter.runFilter(ZuulFilter.java:112) ~[zuul-core-1.3.0.jar!/:1.3.0]
at com.netflix.zuul.FilterProcessor.processZuulFilter(FilterProcessor.java:193) ~[zuul-core-1.3.0.jar!/:1.3.0]
... 66 more
I already check and my filter is executed after the PreDecoratorFilter.
But i don't have a serviceId to remove, i have this configuration:
zuul.routes.arb:
stripPrefix: false
url: http://localhost:8100
path: /appserver/**
```
And i want to stop all the filters, not continuing executing some of them.
So, what's the correct approach to stop all the filter execution and return an error code back to the client?
What does your exception class look like?
You need an Error Filter and an Error Controller.
@Component
public class AuthenticationFilter extends ZuulFilter {
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return 7;
}
@Override
public boolean shouldFilter() {
//some logic
return true;
}
@Override
public Object run() {
//some logic
if (!auth) {
RequestContext ctx = RequestContext.getCurrentContext();
ctx.set("error.status_code", HttpStatus.UNAUTHORIZED.value());
throw new RuntimeException("Not Authorized");
}
}
}
@Component
public class ErrorFilterFilter extends ZuulFilter {
private static final String ERROR_STATUS_CODE = "error.status_code";
@Override
public String filterType() {
return "error";
}
@Override
public int filterOrder() {
return 0;
}
@Override
public boolean shouldFilter() {
RequestContext context = RequestContext.getCurrentContext();
return !context.containsKey(ERROR_STATUS_CODE) && context.getResponseStatusCode() == HttpStatus.INTERNAL_SERVER_ERROR.value();
}
@Override
public Object run() {
RequestContext context = RequestContext.getCurrentContext();
context.put(ERROR_STATUS_CODE, HttpStatus.INTERNAL_SERVER_ERROR.value());
return null;
}
}
@Controller
@Slf4j
public class CustomErrorController implements ErrorController {
@Value("${error.path:/error}")
private String errorPath;
@Override
public String getErrorPath() {
return errorPath;
}
public void setPublish(boolean publish) {
this.publish = publish;
}
@RequestMapping(value = "${error.path:/error}", produces = "application/json")
public
@ResponseBody
ResponseEntity<ErrorInfo> error(HttpServletRequest request,
HttpServletResponse response) {
// just a POJO
ErrorInfo info = new ErrorInfo();
Integer status = (Integer)request.getAttribute("javax.servlet.error.status_code");
status != null ? status : HttpStatus.INTERNAL_SERVER_ERROR.value();
info.setCode(status);
Throwable exc = (Throwable) request.getAttribute("javax.servlet.error.exception");
String msg = exc != null ? exc.getMessage() : "Unexpected error occurred";
info.setMessage(msg);
return ResponseEntity.status(info.getCode()).body(info);
}
}
Closing due to age of the question. If you would like us to look at this issue, please comment and we will look at re-opening the issue.
Most helpful comment
Yes, these log messages are expected, they are logged by the error filter SendErrorFilter#run().
If you don't want to log the exception, you could add a property:
logging.level.org.springframework.cloud.netflix.zuul.filters.post.SendErrorFilter=error.Usually, the post-filters in sc-zuul will not be executed. If you customize a post-filter, you should check
RequestContext.getCurrentContext().getThrowable() == nullin the methodshouldFilter(), just like: SendResponseFilter#shouldFilter()