Handling exceptions in Spring MVC - bahkified/Notes GitHub Wiki

There are several ways to handle exceptions in Spring MVC. They include using the HTTP status codes, and several ways to automatically catch the exception and redirect to an error page.

Setting the HTTP status code

For this and other methods of handling exceptions, we create a custom exception that is thrown somewhere in the application.

@ResponseStatus(value=HttpStatus.NOT_FOUND, reason="Reason that will be displayed with the status code")
public class MyCustomException extends Exception {
    // ...
}

This class will always set the HTTP status code of the response to whatever is defined in the value=… parameter, but only if the exception is not otherwise handled by an exception handler. HttpStatus is an enum.

Using exception handlers

There are several ways of declaring exception handlers in Spring MVC including exception handling methods, classes and AOP advice that will be applied to the controllers. For the following sections, assume you have a class with a method that throws a MyCustomException.

Do not include the full stack trace of the exception in an error page that will be displayed to an end user!

@Controller
public class MyController {
    @RequestMapping("throwError")
    public String doThrowError() {
        throw new MyCustomException();
    }
}

Using @ExceptionHandler methods

@Controller
public class MyController {
    // …
    @ResponseStatus(value=HttpStatus.CONFLICT, reason="There was a conflict somewhere")
    @ExceptionHandler(MyCustomException.class)
    public void setAnotherStatus() {
        // nothing to do
    }

    @ExceptionHandler(MyCustomException.class)
    public String redirectToErrorPage() {
        return "errorPage";
    }
}

There are two distinct methods here to handle exceptions. The setAnotherStatus() method acts on MyCustomException and does nothing. The associated response status annotation is used to set any response status for this exception.

The redirectToErrorPage() method will act on MyCustomException and behaves like any @RequestMapping(…) method. This method will be called in the event that a MyCustomException is thrown somewhere in this controller.

NOTE: do not use two different methods to handle the same exception!

Using the SimpleMappingExceptionResolver

Spring provides this class as a convenient implementation of the HandlerExceptionResolver interface. This class provides ways to map exception classes to view names, log messages, specify a default error page, and a way of setting the exception attribute in the view model.

This class can be configured in the mvc context:

<bean id="simpleMappingExceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver>
    <property name="exceptionMappings">
        <map>
            <entry key="MyCustomException" value="myCustomErrorPage" />
        </map>
    </property>

    <property name="defaultErrorView" value="errorPage" />
    <property name="exceptionAttribute" value="ex" />

    <!-- Name of the logger to be used to log these exceptions -->
    <property name="warnLogCategory" value="example.MvcLogger" />
</bean>

You can also extend the SimpleMappingExceptionResolver class.

@Service
public class MyCustomExceptionResolver extends SimpleMappingExceptionResolver {
    public MyCustomExceptionResolver() {
        setWarnLogCategory(this.getClass().getName());
    }

    @Override
    public String buildLogMessage(Exception e, HttpServletRequest request) {
        return "Custom message: " + e.getLocalizedMessage();
    }

    @Override
    protected String determineViewName(Exception ex, HttpServletRequest request) {
        logger.error("Exception Resolver : " + ExceptionUtils.getStackTrace(ex));
        return "defaultErrorPage";
    }
}

With this class in place, a bean must be declared in the mvc context.

    <bean id="exceptionResolver" class="com.bahk.fun.exception.MyCustomExceptionResolver">
        <property name="defaultErrorView" value="uncaughtError"/>
    </bean>

Resources

⚠️ **GitHub.com Fallback** ⚠️