In this tutorial, we will show you how to handle exceptions in Spring Boot REST API application.
Spring Boot Exception Handling:
This guide helps you to understand how to define generic/global exception handlers in spring boot application. Here I am going to implement a complete Spring boot restful service with all the CRUD operations and handling proper exception handling.
Technologies:
- Spring Boot Strater 2.0.4 RELEASE
- Spring Boot Starter WEB
- Spring Boot Starter JDBC
- MySql Connector
- Java 1.8
Application Structure:
Dependencies:
<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.onlinetutorialspoint</groupId>
<artifactId>Spring-Boot-ExceptionHandling</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Spring-Boot-ExceptionHandling</name>
<description>Spring Boot Exception Handling Example</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Creating a Model class:
package com.onlinetutorialspoint.model;
public class Item {
private int id;
private String name;
private String category;
public Item() {
}
public Item(int id, String name, String category) {
this.id = id;
this.name = name;
this.category = category;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
}
Preparing Spring Boot Error Response classes. This is the main response class representing response status, error message, and timestamp.
package com.onlinetutorialspoint.exception;
public class ItemErrorResponse {
private int status;
public ItemErrorResponse() {
}
private String message;
private long timeStamp;
public ItemErrorResponse(int status, String message, long timeStamp) {
this.status = status;
this.message = message;
this.timeStamp = timeStamp;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public long getTimeStamp() {
return timeStamp;
}
public void setTimeStamp(long timeStamp) {
this.timeStamp = timeStamp;
}
}
Create ItemNotFoundException class, responsible for handling scenarios like when an item is not available in our database.
package com.onlinetutorialspoint.exception;
public class ItemNotFoundException extends RuntimeException{
public ItemNotFoundException(String message) {
super(message);
}
public ItemNotFoundException(String message, Throwable cause) {
super(message, cause);
}
public ItemNotFoundException(Throwable cause) {
super(cause);
}
}
Create a global ItemExceptionHandler, which handles ItemNotFoundException and all types of exceptions like 403, 500 and so on.
package com.onlinetutorialspoint.exception;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice
public class ItemExceptionHandler {
@ExceptionHandler
public ResponseEntity<ItemErrorResponse> handleException(ItemNotFoundException ine){
ItemErrorResponse errorResponse = new ItemErrorResponse();
errorResponse.setStatus(HttpStatus.NOT_FOUND.value());
errorResponse.setMessage(ine.getMessage());
errorResponse.setTimeStamp(System.currentTimeMillis());
return new ResponseEntity<ItemErrorResponse>(errorResponse,HttpStatus.NOT_FOUND);
}
@ExceptionHandler
public ResponseEntity<ItemErrorResponse> handleException(Exception ex){
ItemErrorResponse errorResponse = new ItemErrorResponse();
errorResponse.setStatus(HttpStatus.BAD_REQUEST.value());
errorResponse.setMessage(ex.getMessage());
errorResponse.setTimeStamp(System.currentTimeMillis());
return new ResponseEntity<ItemErrorResponse>(errorResponse,HttpStatus.BAD_REQUEST);
}
}
@ControllerAdvice is used to apply the exception handling technique across the application. This annotation can be used on top of the class; then the class will act as a controller-advice.
@ControllerAdvice is something similar to an interceptor or a filter. It Pre-processes the request to the controller and Post-process the response to handle exceptions.
Define exception handler methods using @ExceptionHandler annotation given by the Spring Framework. It is going to return ResponseEntity.
ResponseEntity is a simple wrapper of HTTP response object; it provides fine-grained control to specify HTTP status codes, HTTP headers and response body.
We are creating ItemRepository having all CRUD operations.
package com.onlinetutorialspoint.repo;
import com.onlinetutorialspoint.exception.ItemNotFoundException;
import com.onlinetutorialspoint.model.Item;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public class ItemRepository {
@Autowired
JdbcTemplate template;
/*Getting all Items from table*/
public List<Item> getAllItems(){
List<Item> items = template.query("select id, name,category from item",(result,rowNum)->new Item(result.getInt("id"),
result.getString("name"),result.getString("category")));
return items;
}
/*Getting a specific item by item id from table*/
public Item getItem(int itemId){
Item item = null;
String query = "SELECT * FROM ITEM WHERE ID=?";
try{
item = template.queryForObject(query,new Object[]{itemId},new BeanPropertyRowMapper<>(Item.class));
}catch(Exception e){
throw new ItemNotFoundException("Item Not Found : "+itemId);
}
return item;
}
/*Adding an item into database table*/
public int addItem(Item item){
String query = "INSERT INTO ITEM VALUES(?,?,?)";
return template.update(query,new Object[] {
Integer.valueOf(item.getId()),item.getName(),item.getCategory()
});
}
/*delete an item from database*/
public int deleteItem(int id){
String query = "DELETE FROM ITEM WHERE ID =?";
int size = template.update(query,id);
if(size == 0){
throw new ItemNotFoundException("No Item Found to delete: "+id);
}
return size;
}
/*update an item from database*/
public void updateItem(Item item){
String query = "UPDATE ITEM SET name=?, category=? WHERE id =?";
template.update(query,
new Object[] {
item.getName(),item.getCategory(), Integer.valueOf(item.getId())
});
}
}
Creating Spring Boot RestController class
package com.onlinetutorialspoint.controller;
import com.onlinetutorialspoint.exception.ItemNotFoundException;
import com.onlinetutorialspoint.model.Item;
import com.onlinetutorialspoint.repo.ItemRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.util.UriComponentsBuilder;
import java.util.List;
@RestController
public class ItemController {
@Autowired
ItemRepository itemRepo;
@RequestMapping("/getAllItems")
@ResponseBody
public ResponseEntity<List<Item>> getAllItems(){
List<Item> items = itemRepo.getAllItems();
return new ResponseEntity<List<Item>>(items, HttpStatus.OK);
}
@GetMapping("/item/{itemId}")
@ResponseBody
public ResponseEntity<Item> getItem(@PathVariable int itemId){
if(itemId <= 0){
throw new ItemNotFoundException("Invalid ItemId");
}
Item item = itemRepo.getItem(itemId);
return new ResponseEntity<Item>(item, HttpStatus.OK);
}
@PostMapping(value = "/addItem",consumes = {"application/json"},produces = {"application/json"})
@ResponseBody
public ResponseEntity<Item> addItem(@RequestBody Item item,UriComponentsBuilder builder){
itemRepo.addItem(item);
HttpHeaders headers = new HttpHeaders();
headers.setLocation(builder.path("/addItem/{id}").buildAndExpand(item.getId()).toUri());
return new ResponseEntity<Item>(headers, HttpStatus.CREATED);
}
@PutMapping("/updateItem")
@ResponseBody
public ResponseEntity<Item> updateItem(@RequestBody Item item){
if(item != null){
itemRepo.updateItem(item);
}
return new ResponseEntity<Item>(item, HttpStatus.OK);
}
@DeleteMapping("/delete/{id}")
@ResponseBody
public ResponseEntity<Void> deleteItem(@PathVariable int id){
itemRepo.deleteItem(id);
return new ResponseEntity<Void>(HttpStatus.ACCEPTED);
}
}
Main class
package com.onlinetutorialspoint;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Run the application:
mvn spring-boot:run
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Spring-Boot-ExceptionHandling 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] >>> spring-boot-maven-plugin:2.0.4.RELEASE:run (default-cli) > test-compile @ Spring-Boot-ExceptionHandling >>>
[INFO]
[INFO] --- maven-resources-plugin:3.0.2:resources (default-resources) @ Spring-Boot-ExceptionHandling ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 1 resource
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.7.0:compile (default-compile) @ Spring-Boot-ExceptionHandling ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- maven-resources-plugin:3.0.2:testResources (default-testResources) @ Spring-Boot-ExceptionHandling ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory E:\work\Spring-Boot-ExceptionHandling\src\test\resources
[INFO]
[INFO] --- maven-compiler-plugin:3.7.0:testCompile (default-testCompile) @ Spring-Boot-ExceptionHandling ---
[INFO] No sources to compile
[INFO]
[INFO] <<< spring-boot-maven-plugin:2.0.4.RELEASE:run (default-cli) < test-compile @ Spring-Boot-ExceptionHandling <<<
[INFO]
[INFO]
[INFO] --- spring-boot-maven-plugin:2.0.4.RELEASE:run (default-cli) @ Spring-Boot-ExceptionHandling ---
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.0.4.RELEASE)
2018-08-27 11:40:24.880 INFO 4584 --- [ main] com.onlinetutorialspoint.Application : Starting Application on DESKTOP-RN4SMHT with PID 4584 (E:\work\Spring-Boot-Excepti
onHandling\target\classes started by Lenovo in E:\work\Spring-Boot-ExceptionHandling)
2018-08-27 11:40:24.896 INFO 4584 --- [ main] com.onlinetutorialspoint.Application : No active profile set, falling back to default profiles: default
2018-08-27 11:40:25.146 INFO 4584 --- [ main] ConfigServletWebServerApplicationContext : Refreshing org.springframework.boot.web.servlet.context.AnnotationConfigServletWeb
ServerApplicationContext@2de77997: startup date [Mon Aug 27 11:40:25 IST 2018]; root of context hierarchy
2018-08-27 11:40:31.161 INFO 4584 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2018-08-27 11:40:31.317 INFO 4584 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2018-08-27 11:40:31.317 INFO 4584 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/8.5.32
2018-08-27 11:40:31.348 INFO 4584 --- [ost-startStop-1] o.a.catalina.core.AprLifecycleListener : The APR based Apache Tomcat Native library which allows optimal performance in pro
duction environments was not found on the java.library.path: [C:\Program Files\Java\jdk1.8.0_161\bin;C:\WINDOWS\Sun\Java\bin;C:\WINDOWS\system32;C:\WINDOWS;C:\ProgramData\Oracle\Java
\javapath;C:\oraclexe\app\oracle\product.2.0\server\bin;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\Program Files (x86)\
MySQL\MySQL Server 5.5\bin;C:\php;C:\Apache24;C:\Apache24\bin;C:\Program Files\Java\jdk1.8.0_161\bin;D:\Softwares\apache-maven-3.5.2\bin;C:\Program Files\Git\cmd;C:\Program Files\Git
\mingw64\bin;C:\Program Files\Git\usr\bin;D:\Softwares\apache-ant-1.10.2\bin;C:\ProgramData\chocolatey\bin;;C:\WINDOWS\System32\OpenSSH\;C:\Program Files\nodejs\;C:\Users\Lenovo\AppD
ata\Local\Programs\Python\Python36\Scripts\;C:\Users\Lenovo\AppData\Local\Programs\Python\Python36\;C:\Users\Lenovo\AppData\Local\Microsoft\WindowsApps;C:\Users\Lenovo\AppData\Local\
atom\bin;C:\Users\Lenovo\AppData\Local\Microsoft\WindowsApps;;C:\Program Files\Microsoft VS Code\bin;C:\Program Files\Docker Toolbox;C:\Users\Lenovo\AppData\Roaming\npm;.]
2018-08-27 11:40:31.567 INFO 4584 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2018-08-27 11:40:31.583 INFO 4584 --- [ost-startStop-1] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 6468 ms
2018-08-27 11:40:31.786 INFO 4584 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean : Servlet dispatcherServlet mapped to [/]
2018-08-27 11:40:31.786 INFO 4584 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'characterEncodingFilter' to: [/*]
2018-08-27 11:40:31.786 INFO 4584 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2018-08-27 11:40:31.786 INFO 4584 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'httpPutFormContentFilter' to: [/*]
2018-08-27 11:40:31.786 INFO 4584 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'requestContextFilter' to: [/*]
2018-08-27 11:40:32.911 INFO 4584 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.
web.servlet.resource.ResourceHttpRequestHandler]
2018-08-27 11:40:33.833 INFO 4584 --- [ main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.web.servlet.context.Annota
tionConfigServletWebServerApplicationContext@2de77997: startup date [Mon Aug 27 11:40:25 IST 2018]; root of context hierarchy
2018-08-27 11:40:34.041 INFO 4584 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/item/{itemId}],methods=[GET]}" onto public org.springframework.http.Res
ponseEntity<com.onlinetutorialspoint.model.Item> com.onlinetutorialspoint.controller.ItemController.getItem(int)
2018-08-27 11:40:34.056 INFO 4584 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/delete/{id}],methods=[DELETE]}" onto public org.springframework.http.Re
sponseEntity<java.lang.Void> com.onlinetutorialspoint.controller.ItemController.deleteItem(int)
2018-08-27 11:40:34.056 INFO 4584 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/getAllItems]}" onto public org.springframework.http.ResponseEntity<java
.util.List<com.onlinetutorialspoint.model.Item>> com.onlinetutorialspoint.controller.ItemController.getAllItems()
2018-08-27 11:40:34.056 INFO 4584 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/updateItem],methods=[PUT]}" onto public org.springframework.http.Respon
seEntity<com.onlinetutorialspoint.model.Item> com.onlinetutorialspoint.controller.ItemController.updateItem(com.onlinetutorialspoint.model.Item)
.........
.........
Access Application:
Getting all Items :
Getting a specific Item:
Getting unavailable Item:
Deleting unavailable Item:
Invalid Item code format (General Exception handler)
Download Source from GIT:
Happy Learning 🙂