Spring MVC Exception Handling – SimpleMappingExceptionResolver
Assume that the web application is throwing some exception from the backend and it is coming in the UI as below
How bad it is for the user to see this error right?
So we need to handle the exception gracefully in the application.
When we handle it properly , we can display the user friendly message.
Lets see how Spring MVC helps in Exception Handling
In Spring MVC,We can handle exception in many ways but we will see popular 3 ways
1)SimpleMappingExceptionResolver
This is the implementation of HandlerExceptionResolver class.
In this method , Each exception class will be mapped to specific view page.
2)Controller based ExceptionHandler
In this method,All the exceptions thrown by a specific controller can be handled by defining ExceptionHandler methods inside that controller. Any exceptions thrown outside that controller will not be handled here.
3)Global ExceptionHandler
In this method , All the exceptions thrown from any controller in the application can be handled by defining a single class which is annotated with @ControllerAdvice
Enough with the Theory , lets go ahead with each approach Step by Step.
1)SimpleMappingExceptionResolver
We will create a simple Login project to find the user based on user name. and if user enters invalid credentials we will throw an exception and will handle it gracefully.
Project structure
Create custom Exception class
- package com.kb.exception;
- public class InvalidUserException extends RuntimeException{
- private static final long serialVersionUID = 1L;
- private String errorCode;
- private String errorMessage;
- public InvalidUserException(String message) {
- this.errorMessage=message;
- }
- public InvalidUserException(String errorCode,String message) {
- this.errorCode = errorCode;
- this.errorMessage=message;
- }
- public String getErrorCode() {
- return errorCode;
- }
- public String getErrorMessage() {
- return errorMessage;
- }
- }
package com.kb.exception; public class InvalidUserException extends RuntimeException{ private static final long serialVersionUID = 1L; private String errorCode; private String errorMessage; public InvalidUserException(String message) { this.errorMessage=message; } public InvalidUserException(String errorCode,String message) { this.errorCode = errorCode; this.errorMessage=message; } public String getErrorCode() { return errorCode; } public String getErrorMessage() { return errorMessage; } }
Create the model class which holds user details
- package com.kb.model;
- public class User {
- private String userName;
- private String password;
- public String getUserName() {
- return userName;
- }
- public void setUserName(String userName) {
- this.userName = userName;
- }
- public String getPassword() {
- return password;
- }
- public void setPassword(String password) {
- this.password = password;
- }
- }
package com.kb.model; public class User { private String userName; private String password; public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
Create the login controller class
- package com.kb.controllers;
- import org.springframework.stereotype.Controller;
- import org.springframework.ui.Model;
- import org.springframework.web.bind.annotation.ModelAttribute;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RequestMethod;
- import com.kb.exception.InvalidUserException;
- import com.kb.model.User;
- @Controller
- public class LoginController {
- @RequestMapping(value="/displayLoginPage",method=RequestMethod.GET)
- public String displayLoginPage(Model model){
- User user = new User();
- model.addAttribute("user", user);
- return "/login";
- }
- @RequestMapping(value="/doLogin",method=RequestMethod.POST)
- public String doLogin(@ModelAttribute User user,Model model){
- String userName=user.getUserName();
- String password = user.getPassword();
- if("kb".equals(userName) && "1234".equals(password)){
- model.addAttribute("user", user);
- return "/home";
- }
- else{
- //Log the exception
- throw new InvalidUserException("INVALID_USER","User is not Valid for this site");
- }
- }
- @RequestMapping(value="/getGenericException",method=RequestMethod.GET)
- public String getGenericException(Model model) throws Exception{
- throw new Exception();
- }
- }
package com.kb.controllers; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import com.kb.exception.InvalidUserException; import com.kb.model.User; @Controller public class LoginController { @RequestMapping(value="/displayLoginPage",method=RequestMethod.GET) public String displayLoginPage(Model model){ User user = new User(); model.addAttribute("user", user); return "/login"; } @RequestMapping(value="/doLogin",method=RequestMethod.POST) public String doLogin(@ModelAttribute User user,Model model){ String userName=user.getUserName(); String password = user.getPassword(); if("kb".equals(userName) && "1234".equals(password)){ model.addAttribute("user", user); return "/home"; } else{ //Log the exception throw new InvalidUserException("INVALID_USER","User is not Valid for this site"); } } @RequestMapping(value="/getGenericException",method=RequestMethod.GET) public String getGenericException(Model model) throws Exception{ throw new Exception(); } }
In the above controller, we have a method displayLoginPage() which displays the login page.
We also have a method doLogin() which checks the credentials and displays home page if credentials are valid.
If credentials are invalid , we throw an exception InvalidUserException which we handle as below.
There is one more method getGenericException() which is intentionally used to throw some general exception just to show how any unhandled exception is handled automatically through Exception resolver mapping.
Create the spring configuration file
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
- xmlns:context="http://www.springframework.org/schema/context"
- xmlns:mvc="http://www.springframework.org/schema/mvc"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
- http://www.springframework.org/schema/context
- http://www.springframework.org/schema/context/spring-context-3.2.xsd
- http://www.springframework.org/schema/mvc
- http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">
- <context:component-scan base-package="com.kb.*" />
- <mvc:annotation-driven />
- <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
- <property name="prefix" value="/WEB-INF/pages/" />
- <property name="suffix" value=".jsp" />
- </bean>
- <bean
- class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
- <property name="exceptionMappings">
- <map>
- <entry key="InvalidUserException" value="userNotFound"/>
- </map>
- </property>
- <property name="defaultErrorView" value="genericError"/>
- </bean>
- </beans>
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd"> <context:component-scan base-package="com.kb.*" /> <mvc:annotation-driven /> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/pages/" /> <property name="suffix" value=".jsp" /> </bean> <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <property name="exceptionMappings"> <map> <entry key="InvalidUserException" value="userNotFound"/> </map> </property> <property name="defaultErrorView" value="genericError"/> </bean> </beans>
So in the above file, we have defined SimpleMappingExceptionResolver, it has a property called exceptionMappings where we can specify the list of exceptions and their corresponding view page to display.
There is also a property called defaultErrorView which is very useful property,
It maps any other unhandled exceptions in the application to the corresponding view page(genericError.jsp in this case)
Create the login jsp
- <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
- <html>
- <head>
- <title>Spring MVC Exception Handling</title>
- </head>
- <body>
- <h2>Enter below details to Login</h2>
- <form:form method="POST" modelAttribute="user" action="doLogin">
- <table>
- <tr>
- <td>Enter your User Name</td>
- <td><form:input path="userName" /></td>
- </tr>
- <tr>
- <td>Enter your password:</td>
- <td><form:password path="password" showPassword="true"/></td>
- </tr>
- <tr>
- <td><input type="submit" name="submit" value="Click here to Login"></td>
- </tr>
- </table>
- </form:form>
- </body>
- </html>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%> <html> <head> <title>Spring MVC Exception Handling</title> </head> <body> <h2>Enter below details to Login</h2> <form:form method="POST" modelAttribute="user" action="doLogin"> <table> <tr> <td>Enter your User Name</td> <td><form:input path="userName" /></td> </tr> <tr> <td>Enter your password:</td> <td><form:password path="password" showPassword="true"/></td> </tr> <tr> <td><input type="submit" name="submit" value="Click here to Login"></td> </tr> </table> </form:form> </body> </html>
Create the home jsp
- <%@ page language="java" contentType="text/html; charset=ISO-8859-1"
- pageEncoding="ISO-8859-1"%>
- <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
- <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
- <html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
- <title>Login Success</title>
- </head>
- <body>
- <div align="center">
- <h2>Welcome ${user.userName}! You have been successfully Logged in.</h2>
- </div>
- </body>
- </html>
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Login Success</title> </head> <body> <div align="center"> <h2>Welcome ${user.userName}! You have been successfully Logged in.</h2> </div> </body> </html>
This home jsp is displaed after successful login.
Create the userNotFound jsp
- <%@ page language="java" contentType="text/html; charset=ISO-8859-1"
- pageEncoding="ISO-8859-1"%>
- <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
- <html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
- <title>Insert title here</title>
- </head>
- <body>
- <h2> Sorry User is not valid for this site</h2>
- </body>
- </html>
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Insert title here</title> </head> <body> <h2> Sorry User is not valid for this site</h2> </body> </html>
The above jsp page is displayed when the user enters invalid credentials as we are throwing the InvalidUserException in the controller.
We are also mapping the InvalidUserException inside spring configuration file for the above jsp page.
Create the genericError jsp page
- <%@ page language="java" contentType="text/html; charset=ISO-8859-1"
- pageEncoding="ISO-8859-1"%>
- <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
- <html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
- <title>Insert title here</title>
- </head>
- <body>
- <h2> Sorry, some technical error, Please try again</h2>
- </body>
- </html>
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Insert title here</title> </head> <body> <h2> Sorry, some technical error, Please try again</h2> </body> </html>
The above jsp page is displayed when any other exception apart from InvalidUserException is thrown.
Create the web.xml
- <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
- version="3.1">
- <display-name>Simple Mapping Exception Resolver</display-name>
- <!-- Spring MVC dispatcher servlet -->
- <servlet>
- <servlet-name>mvc-dispatcher</servlet-name>
- <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
- <init-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>
- /WEB-INF/spring-mvc.xml,
- </param-value>
- </init-param>
- <load-on-startup>1</load-on-startup>
- </servlet>
- <servlet-mapping>
- <servlet-name>mvc-dispatcher</servlet-name>
- <url-pattern>/</url-pattern>
- </servlet-mapping>
- <listener>
- <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
- </listener>
- <!-- Loads Spring Security configuration file -->
- <context-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>
- /WEB-INF/spring-mvc.xml,
- </param-value>
- </context-param>
- </web-app>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <display-name>Simple Mapping Exception Resolver</display-name> <!-- Spring MVC dispatcher servlet --> <servlet> <servlet-name>mvc-dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/spring-mvc.xml, </param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>mvc-dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- Loads Spring Security configuration file --> <context-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/spring-mvc.xml, </param-value> </context-param> </web-app>
Create the pom.xml
- <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <groupId>Spring</groupId>
- <artifactId>ExceptionHandling</artifactId>
- <packaging>war</packaging>
- <version>0.0.1-SNAPSHOT</version>
- <name>ExceptionHandling Maven Webapp</name>
- <url>http://maven.apache.org</url>
- <properties>
- <org.springframework.version>4.2.0.RELEASE</org.springframework.version>
- </properties>
- <dependencies>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <version>3.8.1</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-web</artifactId>
- <version>${org.springframework.version}</version>
- </dependency>
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-webmvc</artifactId>
- <version>${org.springframework.version}</version>
- </dependency>
- <dependency>
- <groupId>javax.servlet</groupId>
- <artifactId>servlet-api</artifactId>
- <version>2.5</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>javax.servlet.jsp.jstl</groupId>
- <artifactId>javax.servlet.jsp.jstl-api</artifactId>
- <version>1.2.1</version>
- </dependency>
- <dependency>
- <groupId>taglibs</groupId>
- <artifactId>standard</artifactId>
- <version>1.1.2</version>
- </dependency>
- </dependencies>
- <build>
- <finalName>SimpleMappingExceptionResolver</finalName>
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-compiler-plugin</artifactId>
- <version>2.5.1</version>
- <configuration>
- <source>1.8</source>
- <target>1.8</target>
- </configuration>
- </plugin>
- </plugins>
- </build>
- </project>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>Spring</groupId> <artifactId>ExceptionHandling</artifactId> <packaging>war</packaging> <version>0.0.1-SNAPSHOT</version> <name>ExceptionHandling Maven Webapp</name> <url>http://maven.apache.org</url> <properties> <org.springframework.version>4.2.0.RELEASE</org.springframework.version> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${org.springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${org.springframework.version}</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet.jsp.jstl</groupId> <artifactId>javax.servlet.jsp.jstl-api</artifactId> <version>1.2.1</version> </dependency> <dependency> <groupId>taglibs</groupId> <artifactId>standard</artifactId> <version>1.1.2</version> </dependency> </dependencies> <build> <finalName>SimpleMappingExceptionResolver</finalName> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.5.1</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build> </project>
Now deploy the project in web server and access the below url
http://localhost:8080/SimpleMappingExceptionResolver/displayLoginPage
Enter username as kb and password as 1234
Home page is displayed, no issues.
Enter invalid credentials , I am giving username as xyz and password as 1111 as below
Now see the output
The above view page is coming from userNotFound.jsp.
When the user enters invalid credentials, our login controller will throw InvalidUserException and
We have configured the same exception in spring configuration file for the userNotFound.jsp
Now access the below url which actually throws general exception
http://localhost:8080/SimpleMappingExceptionResolver/getGenericException
Just to see how any unhandled exceptions are handled by SimpleMappingExceptionResolver by using the property ‘defaultErrorView’.
see the output
See that , the view page coming here is genericError.jsp
As spring configuration file is defined with Exception resolver and
defaultErrorView is mapped to genericError.jsp, the above view page is displayed.
Easy to understand.Good one.
Thanks.
That’s a good tutorial KB.
I just have one question. How to access the values of errorMessage and errorCode of InvalidUserException Class in the JSP?
Thanks in advance.
Got it.
${exception.errorMessage} where errorMessage is the name of the variable in the custom exception class.
Thanks Venkatesh!!
You can access exception attributes using default value added inside model attribute by spring automatically.
By default spring adds exception object inside model attribute using a key “exception”.
Hence you can access it in view using ${exception.errorMessage} etc.
You can also change this default key from “exception” to any other key by defining the exception property inside SimpleMappingExceptionResolver bean definition.
Hi as per my knowledge if we use @controller advice and @Exception handler we can handle exception occurred at controller . Please can you post demo on handling exception at different layer like service layer and dao layer.
@repository and @service layer
Thanks in advance
Hi Nijan,
The idea behind providing exception handling at controller layer is that Controllers are the starting point in the back end flow which calls service and so on…
So any exception thrown in service or DAO layer can be cascaded easily to controller which solves the problem of handling exception at all the layers.
I don’t think its required for service and DAO layer specifically , but I will check it and update you.
Thank you!!