How to exclude ClientAbortException from SimpleMap

2020-08-10 08:30发布

问题:

I am using a SimpleMappingExceptionResolver that sends all exceptions to an view where it is nicely rendered. That works except one case: If the user requests a page, and then send and "Abort" (I don't know exactly how that works, but I noticed that if I click an HTTP post form submit button very fast and often the Firefox 7 somehow notify the server that it is not longer interested in the result.) Then the Tomcat 6 rises an ClientAbortException when one try to render the page, or write the the http response in any kind.

Now starts the trouble: the SimpleMappingExceptionResolver "catches" the exception and trys to render it nicely to an html page. This then causes in a Stream already closed exception which pollute the log file. (java.lang.IllegalStateException: getOutputStream() has already been called for this response)

What I have done so fare, is to register the an empty jsp page for the "ClientAbortException". But I feel that this is a Hack. On the other Hand I guess this is not a so uncommen problem, because I will expect it in nearly every spring application that renders all exception. So does anybody have experience with that problem, or has an idea of an not so hacky solution?

<bean
  class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"
  p:defaultErrorView="uncaughtException">
    <property name="exceptionMappings">
        <props>
           <prop key=".MissingServletRequestParameterException">
               resourceNotFound
           </prop>
           <prop key=".ClientAbortException">nothing</prop>
        </props>
     </property>
</bean>

回答1:

Extend SimpleMappingExceptionResolver, override doResolveException() method, and in case the exception name is ClientAbortException and response.isCommitted() return null instead of returning super.doResolveException().



回答2:

For newer versions of Spring, you can also use the @ExceptionHandler annotation, possibly with @ControllerAdvice (for global handling).

@ControllerAdvice
public class GlobalDefaultExceptionHandler {
    private final Logger logger = LoggerFactory.getLogger(getClass());

    @ExceptionHandler(ClientAbortException.class)
    public void clientAbortExceptionHandler(HttpServletRequest request, ClientAbortException e) {
        // This usually means the browser closed or disconnected or
        // something. We can't do anything. To avoid excessive stack traces
        // in log, just print a simple message and return null
        String username = "<NONE>";
        Principal principal = request.getUserPrincipal();
        if (principal != null) {
            username = principal.getName();
        }
        logger.warn("ClientAbortException: username={},remoteAddr={},userAgent={},requestedURL={}", username,
                request.getRemoteAddr(), request.getHeader("User-Agent"), request.getRequestURL());
    }

}


回答3:

IMHO, registering the nothing view for the Exception you don't want to handle is elegant way; as opposed to feeling of hack; as you can have even more than one exception you don't / won't want to render as compared to other exceptions.

I won't recommend coming up with a custom implementation for HandlerExceptionResolver; as it may increase maintainability costs.