How do I handle error states in servlet filters wi

2020-06-18 02:30发布

问题:

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());
    }
}

回答1:

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.