How do I handle error states in servlet filters without showing the user a stack trace?

11,919

The specific handling you mention for web application exceptions is only defined within the context of a JAX-RS container, which, by the way, is not the same thing as a Servlet container.

Web filters are handled by the Servlet container, which does not know or care that a JAX-RS container exists within the same application server. It also does not know or care about web application exceptions. So when you throw the WAE from within the filter it is treated just the same as any other exception (server error with a stack trace, or a preconfigured error page if you set one up in your web application).

It would seem to me if you are indicating an error to the client you could simply do so from the filter, by writing directly to the response stream. But if you are trying to leverage some existing JAX-RS logic then a (RESTEasy specific) solution would be to flag the request as error'ed out in your filter, then generate a WAE in JAX-RS, using a provider class. Example:

@WebFilter(urlPatterns = "*")
public class ForwardingFilter implements Filter {

    @Override
    public void destroy() {
        return;
    }

    @Override
    public void doFilter(final ServletRequest request,
            final ServletResponse response, final FilterChain chain)
            throws IOException, ServletException {
        // Add an error response to be processed by the JAX-RS container.
        // This would obviously be based on some condition.
        request.setAttribute("errorResponse",
                Response.status(500).entity("Didn't work out!").build());
        chain.doFilter(request, response);
    }

    @Override
    public void init(FilterConfig arg0) throws ServletException {
        return;
    }
}

@Provider
@ServerInterceptor
@HeaderDecoratorPrecedence
@RequestScoped
public class ForwardingHandlerProvider implements PreProcessInterceptor {

    @Override
    public ServerResponse preProcess(final HttpRequest request,
            final ResourceMethod method) throws Failure,
            WebApplicationException {
        final Response errorResponse = (Response) request
                .getAttribute("errorResponse");
        if (errorResponse != null)
            throw new WebApplicationException(errorResponse);
        return null;
    }
}

Since the provider exists in JAX-RS land, the web application exception is processed according to the rules of Section 3.3.4 of the JAX-RS specification, and you get the desired response at the client side.

* EDIT:*

The bottom line is, there is no standard Java EE prescribed way (currently) to handle servlet exceptions in a centralized fashion similar to what is available in JAX-RS. Since you are using JBoss/RestEASY though, you could utilize the JBoss Seam Catch library to get pretty close.

@HandlesExceptions
public class ExceptionHandler {
    public void handleServletException(
            final @Handles @WebRequest CaughtException<ServletException> caught,
            @Context final HttpServletResponse response) {
        try {
            response.sendError(500, "An error occured");
        } catch (final IOException ioe) {
            System.err.println("Dumb IO Exception: " + ioe);
        }
    }
}

The above illustrates an exception handler, as described in the Seam Catch documentation. Note that the library is in massive flux right now, so you will want to utilize it only as a last resort.

Share:
11,919
Nathan Friedly
Author by

Nathan Friedly

I do a lot of JavaScript/Node.js/Web stuff, and more recently mobile development. I also do a little bit of a wide variety of other things including mentoring and training, IoT, marketing, analytics, AI/ML, automation, DevOps, sales optimization, performance optimization, etc. I currently work on the mobile apps team at Fullstory. Prior to that I founded ◢ Incline, helped launch the public IBM Watson APIs APIs, improved the security of quite a number of computers - including the US Air Force - while at Tanium, and did a few other things.

Updated on June 04, 2022

Comments

  • Nathan Friedly
    Nathan Friedly about 2 years

    I'm working on a Jetty/RESTEasy app. If I throw a WebApplicationException(myResponse) from one of my REST endpoints, it sends the given response to the client.

    When a filter detects an error, I want the same behavior:

    1. It should stop execution from proceeding, and
    2. It should give the user a clear, JSON-formatted error that does not include a stack trace.

    Obviously, just writing to the response stream and returning works from within the doFilter method. But this doesn't work for other methods called by doFilter.

    Throwing any exception will meet condition #1 but I haven't figured out a sane way to meet condition #2 then. (You can see my best attempt at the bottom.)

    As Perception explained in his answer, WebApplicationExceptions are treated like any other exception in the context of a Filter, and therefore give the user a nice ugly stack trace.

    So, to sum up my questions:

    • Do serveltt containers have any equivalent to throw new WebApplicationException(Response)?
    • And perhaps more importantly, how do other java projects handle this?

    I have this code in one filter and it works, but I'd prefer a more elegant solution that automatically applies to all filters:

    public void doFilter(final ServletRequest   request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException {
        try {
            doFilterOrThrow(request, response, chain);
        } catch (WebApplicationException e) {
            Response res = e.getResponse();
            ((HttpServletResponse) response).sendError(res.getStatus(), (String) res.getEntity());
        }
    }
    
  • Nathan Friedly
    Nathan Friedly about 11 years
    Thanks for the info, I think I understand the issue a little better now. However, to clarify: I'm looking for a way to both write to the response stream and stop all subsequent execution. The app I'm working on has multiple filters and some of them depend on data from the previous one. I could just return from the doFilter() method, but that doesn't work if the error state is detected in some other method that doFilter() calls. (At least not without additional logic...)
  • Perception
    Perception about 11 years
    If you need to stop processing immediately then you basically need to replicate, in the filter, the data that would been returned by the JAX-RS. There's simply no other way around that, since filters aren't executed in the JAX-RS container (but rather ahead of it). Another option would be to implement your filter logic in the pre process interceptor, though I get the feeling that there are intermediate filters and processes you are trying to skip way before that point.
  • Nathan Friedly
    Nathan Friedly about 11 years
    Is there a way to set up a generic error handler for any error that occurs in the servlet container, including in the filters? - Actually, I think this might be what I need: stackoverflow.com/questions/4207586/…
  • Nathan Friedly
    Nathan Friedly about 11 years
    At any rate, I don't think I'm going to get a better answer, so you get the bounty :)
  • Perception
    Perception about 11 years
    @NathanFriedly - I added some information on Seam Catch, which is a library that at least allows centralized handling of uncaught exceptions. It requires some fiddling to get working, and of course you will need to do the JSON mapping yourself, but its a solid start at least.
  • Nathan Friedly
    Nathan Friedly about 11 years
    Cool, thanks. It might be a month or two before I get back to this, so hopefully they will have settled down by then.