SimpleMappingExceptionResolver is an implementation of HandlerExceptionResolver. It allows us to configure the following:
- Mapping exception class names to view names.
- Mapping view names to response status codes, done internally via the
HttpServletResponse#setStatus(int) method.
- Specifying a default exception view.
- Specifying a default response status code.
If interested, check out the doResolveException() method of SimpleMappingExceptionResolver.java.
Example
JavaConfig class
package com.logicbig.example;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import java.util.List;
import java.util.Properties;
@EnableWebMvc
@ComponentScan
@Configuration
public class AppConfig implements WebMvcConfigurer {
@Override
public void configureHandlerExceptionResolvers(
List<HandlerExceptionResolver> exceptionResolvers) {
SimpleMappingExceptionResolver s =
new SimpleMappingExceptionResolver();
//exception to view name mapping
Properties p = new Properties();
p.setProperty(NullPointerException.class.getName(), "npeView");
p.setProperty(OrderIdNotValidException.class.getName(),
"OrderIdNotValidView");
s.setExceptionMappings(p);
//mapping status code with view response.
s.addStatusCode("npeView", 404);
//setting default error view
s.setDefaultErrorView("defaultErrorView");
//setting default status code
s.setDefaultStatusCode(400);
exceptionResolvers.add(s);
}
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.jsp("/WEB-INF/views/", ".jsp");
}
}
The Controller
package com.logicbig.example;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class ExampleController {
@Autowired
private DataService dataService;
@ResponseBody
@RequestMapping("/order/{orderId}")
public String handleRequest (@PathVariable("orderId") String orderId,
Model model) {
return dataService.getOrderDetail(orderId);
}
@ResponseBody
@RequestMapping("/data/{id}")
public String testHandler (@PathVariable("id") String id,
Model model) {
return dataService.getData(id);
}
@ResponseBody
@RequestMapping("/test")
public void testHandler2 () {
throw new RuntimeException("test exception");
}
}
package com.logicbig.example;
import org.springframework.stereotype.Repository;
import java.util.HashMap;
import java.util.Map;
@Repository
public class DataService {
private Map<String, Object> testData = new HashMap<>();
private Map<Integer, String> testOrders = new HashMap<>();
public DataService() {
testData.put("1", "this is test data");
testOrders.put(1, "Order 1");
testOrders.put(2, "Order 2");
}
public String getData(String id) {
//we purposely created an NPE scenario for the demo
//only id=1 will return test data
return testData.get(id).toString();
}
public String getOrderDetail(String orderId) {
try {
int i = Integer.parseInt(orderId);
String s = testOrders.get(i);
return s == null ? "Empty Order" : s;
} catch (NumberFormatException e) {
throw new OrderIdNotValidException(
"Order id is not valid: " + orderId);
}
}
}
In the above DataService class, we purposely created error scenarios.
public class OrderIdNotValidException extends RuntimeException {
public OrderIdNotValidException (String message) {
super(message);
}
}
JSP pages
src/main/webapp/WEB-INF/views/defaultErrorView.jsp
<%@ page language="java"
contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<html>
<body>
<h3>This is default exception page</h3>
<p>Exception: <b>${exception}</b></p>
</body>
</html>
Note that in the above JSP page, we use the ${exception} placeholder for the exception object. By default, SimpleMappingExceptionResolver uses the model attribute "exception", mapped to the exception object. This default attribute name can be changed by using: SimpleMappingExceptionResolver#setExceptionAttribute(String exceptionAttribute)
src/main/webapp/WEB-INF/views/OrderIdNotValidView.jsp
<%@ page language="java"
contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<html>
<body>
<h3>Not a valid Order Id</h3>
<p>The provided order Id is not valid: ${orderId}</p>
</body>
</html>
src/main/webapp/WEB-INF/views/npeView.jsp
<%@ page language="java"
contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<html>
<body>
<h3>Data not found</h3>
<p>No data found for id ${id}</p>
</body>
</html>
Running the example
To try examples, run embedded Jetty (configured in pom.xml of example project below):
mvn jetty:run
Output
Testing: /order/{orderId}
According to our DataService logic, only an integer is a valid order ID. An invalid order ID will throw an OrderIdNotValidException, which we have mapped to OrderIdNotValidView.jsp in our SimpleMappingExceptionResolver bean.
$ curl -s "http://localhost:8080/simple-mapping-exception-resolver/order/2 Order 2
$ curl -s "http://localhost:8080/simple-mapping-exception-resolver/order/abc
<html> <body> <h3>Not a valid Order Id</h3> <p>The provided order Id is not valid: abc</p> </body> </html>
Testing: /data/{id}
In DataService#getData(), only id=1 returns data; every other value throws a NullPointerException. This exception is mapped to npeView.jsp. We have also mapped this view to response status code 404:
$ curl -s "http://localhost:8080/simple-mapping-exception-resolver/data/1 this is test data
$ curl -s "http://localhost:8080/simple-mapping-exception-resolver/data/5
<html> <body> <h3>Data not found</h3> <p>No data found for id 5</p> </body> </html>
Testing: /test
The mapped handler throws a RuntimeException, which is not mapped to any view in our SimpleMappingExceptionResolver bean. In that case, the default error view is shown and the default status code is returned in the response:
$ curl -s "http://localhost:8080/simple-mapping-exception-resolver/test
<html> <body> <h3>This is default exception page</h3> <p>Exception: <b>java.lang.RuntimeException: test exception</b></p>
</body> </html>
Other URIs
Other URIs that are not mapped to any handler will also mapped to the default error view (Spring 6.1+ behavior). Pre Spring MVC 6.1 you will get the container's default 404 page. ( see last tutorialfor more information).
Example ProjectDependencies and Technologies Used: - spring-webmvc 7.0.6 (Spring Web MVC)
Version Compatibility: 3.2.9.RELEASE - 7.0.6 Version compatibilities of spring-webmvc with this example: Versions in green have been tested.
- jakarta.servlet-api 6.1.0 (Jakarta Servlet API documentation)
- JDK 25
- Maven 3.9.11
|