How To Handle Exceptions In Restful Web Service
When you develop a Spring Bool RESTful service, you as a programmer are responsible for handling exceptions in the service. For instance, by properly treatment exceptions, you tin end the disruption of the normal menstruum of the application. In addition, proper exception treatment ensures that the code doesn't break when an exception occurs.
Another important thing is to ensure as a programmer is not to send whatsoever exceptions or error stacks to clients. Exception and error letters sent to clients should be brusque and meaningful.
In this postal service, I will explain how to gracefully handle exceptions in Jump Boot RESTful services.
Dependency
For this post, nosotros will create a Sprinfg Boot RESTful service that performs CRUD operations on Blog entities. We will use embedded H2 as the database. The following code shows the dependencies of the application in the pom.xml
file.
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-spider web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>bound-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <version>one.iv.200</version> </dependency>
Exception Handling in Jump Instance
In the context of our Blog
RESTful service, the awarding may encounter several types of exceptions. For case, the database may be downward. Another scenario can be a user trying to save an already existing web log. Or a user trying to access a weblog nonetheless to be published.
Y'all should handle such scenarios gracefully in the application.
As an example, for database failure, the awarding throws SQLException.
Instead of returning the exception stack trace to client, you should return a meaningful exception message.
The Entity Class
The code for the Weblog
Entity course is this.
Blog.java
@Entity public class Weblog { @Id private int blogId; individual String blogTitle; individual Cord blogCreator; private int yearOfPost; // No-Args and Parametrized Constructor //Getters and Setters }
It is a JPA Entity form annotated with the @Entity
annotation and corresponding getters and setters for the fields.
The Repository
This is the Blog Repository
Interface.
BlogRepository.java
package org.springframework.guru.repository; import org.springframework.guru.model.Web log; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Repository; @Repository public interface BlogRepository extends CrudRepository<Blog,Integer> { }
Here, the BlogRepository
extends the CrudRepository
of Spring Data JPA.
Custom Exception Classes
In our application, we volition create custom exception classes. Such classes enable us to customize an exception according to the callers' needs.
Nosotros will create 2 custom exception classes:
-
BlogAlreadyExistsException
: Is thrown when a user tries to add an already existing blog. -
BlogNotFoundException
: Is thrown when a user tries to admission a weblog that is not nowadays.
The code of the BlogAlreadyExistsException
class is this.
BlogAlreadyExistsException.coffee
packet org.springframework.guru.exception; public class BlogAlreadyExistsException extends RuntimeException { private String message; public BlogAlreadyExistsException(String message) { super(bulletin); this.message = message; } public BlogAlreadyExistsException() { } }
The code for the BlogNotFoundException
class is this.
BlogNotFoundException.coffee
bundle org.springframework.guru.exception; public class BlogNotFoundException extends RuntimeException { private String bulletin; public BlogNotFoundException(String message) { super(message); this.bulletin = message; } public BlogNotFoundException() { } }
The Service
This is the BlogService
interface which has various methods to perform operations on Web log
entities.
BlogService.java
package org.springframework.guru.service; import org.springframework.guru.exception.BlogAlreadyExistsException; import org.springframework.guru.exception.BlogNotFoundException; import org.springframework.guru.model.Weblog; import coffee.util.List; public interface BlogService { Blog saveBlog(Blog blog) throws BlogAlreadyExistsException; List getAllBlogs() throws BlogNotFoundException; Weblog getBlogById(int id) throws BlogNotFoundException; }
In the preceding BlogService interface, the saveBlog()
method declares that it throws BlogAlreadyExistsException
. The 2 other methods, getAllBlogs()
and getBlogById()
declares that they throw BlogNotFoundException
.
The service implementation class for BlogService
is this.
BlogServiceImpl.coffee
@Service public form BlogServiceImpl implements BlogService { individual BlogRepository blogRepository; @Autowired public BlogServiceImpl(BlogRepository blogRepository) { this.blogRepository = blogRepository; } @Override public Blog saveBlog(Web log weblog) { if (blogRepository.existsById(blog.getBlogId())) { throw new BlogAlreadyExistsException(); } Blog savedBlog = blogRepository.save(blog); return savedBlog; } @Override public List getAllBlogs() { return (List) blogRepository.findAll(); } @Override public Blog getBlogById(int id) throws BlogNotFoundException { Blog blog; if (blogRepository.findById(id).isEmpty()) { throw new BlogNotFoundException(); } else { blog = blogRepository.findById(id).become(); } return blog; } }
The preceding BlogServiceImpl
form implements the methods declared in the BlogService
interface.
There are ii paths in exception handling. One is the code handles the exception using a attempt-grab block. The other is to propagate back a custom exception to the caller. The preceding service class uses the latter arroyo.
Line 12 – Line iii checks if the blog already exists in the database. If truthful the method throws a BlogAlreadyExistsException
. Else, the method saves the Blog
object.
Line 27 – Line 28 throws a BlogNotFoundException
if the Blog
with the specified Id is not nowadays in the database.
The Controller
The code for the BlogController
is this.
BlogController.java
@RestController @RequestMapping("api/v1") public grade BlogController { private BlogService blogService; @Autowired public BlogController(BlogService blogService) { this.blogService = blogService; } @PostMapping("/blog") public ResponseEntity saveBlog(@RequestBody Blog weblog) throws BlogAlreadyExistsException { Blog savedBlog = blogService.saveBlog(blog); render new ResponseEntity<>(savedBlog, HttpStatus.CREATED); } @GetMapping("/blogs") public ResponseEntity<List> getAllBlogs() throws BlogNotFoundException { return new ResponseEntity<List>((List) blogService.getAllBlogs(), HttpStatus.OK); } @GetMapping("weblog/{id}") public ResponseEntity getBlogById(@PathVariable("id") int id) throws BlogNotFoundException { return new ResponseEntity(blogService.getBlogById(id), HttpStatus.OK); }
The preceding controller class is not treatment the custom exceptions. Instead, it throws the exceptions back to the caller – which in our scenario is a REST client. This is not what nosotros want – straight sending back exceptions to clients.
Instead, we should handle the exception and send dorsum a brusk and meaningful exception message to the client. Nosotros tin use different approaches to accomplish this.
Approach 1: Traditional try-catch Block
The first approach is to use Coffee attempt-catch block to handle the exception in the controller methods. The code to handle BlogNotFoundException
in the getBlogById()
method is this.
@GetMapping("web log/{id}") public ResponseEntity getBlogById(@PathVariable("id") int id) { attempt{ render new ResponseEntity(blogService.getBlogById(id), HttpStatus.OK); } grab(BlogNotFoundException blogNotFoundException ){ return new ResponseEntity(blogNotFoundException.getMessage(), HttpStatus.CONFLICT); } }
In the preceding code, the phone call to the BlogService.getBlogById()
method is wrapped in a try
block. If a method telephone call to getBlogById()
throws BlogNotFoundException
, the catch
cake handles the exception. In the take hold of
cake, the ResponseEntity
object is used to send a custom error bulletin with a condition code every bit a response.
Approach 2: Spring @ExceptionHandler Annotation
Spring provides the @ExceptionHandler
notation to handle exceptions in specific handler classes or handler methods.
Jump configuration will discover this notation and register the method as an exception handler. The method will handle the exception and its subclasses passed to the annotation.
@ExceptionHandler(value = BlogAlreadyExistsException.class) public ResponseEntity handleBlogAlreadyExistsException(BlogAlreadyExistsException blogAlreadyExistsException) { return new ResponseEntity("Blog already exists", HttpStatus.CONFLICT); }
When whatever method in the controller throws the BlogAlreadyExistsException
exception, Jump invokes the handleBlogAlreadyExistsException()
method. This method returns a ResponseEntity
that wraps a custom fault message and a status lawmaking.
When you run the application and send a POST request to add an existing blog, you volition become this output.
Approach 3: Global Exception Handling with @ControllerAdvice
The @ExceptionHandler
annotation is only active for that detail class where it is alleged. If you want a global exception handler y'all can use Spring AOP. A global exception handler provides a standard mode of handling exceptions throughout the application. In improver, it considerably reduces the corporeality of code written for exception treatment.
The Spring @ExceptionHandler
forth with @ControllerAdvice
of Spring AOP enables a mechanism to handle exceptions globally.
The code for the GlobalExceptionHandler
class is this.
GlobalExceptionHandler.java
@ControllerAdvice public grade GlobalExceptionHandler { @Value(value = "${information.exception.message1}") private String message1; @Value(value = "${data.exception.message2}") private String message2; @Value(value = "${data.exception.message3}") private String message3; @ExceptionHandler(value = BlogNotFoundException.class) public ResponseEntity blogNotFoundException(BlogNotFoundException blogNotFoundException) { return new ResponseEntity(message2, HttpStatus.NOT_FOUND); } @ExceptionHandler(value = Exception.course) public ResponseEntity<> databaseConnectionFailsException(Exception exception) { return new ResponseEntity<>(message3, HttpStatus.INTERNAL_SERVER_ERROR); } }
The @ControllerAdvice
annotation in Line ane consolidates multiple @ExceptionHandlers
into a single, global exception handling component.
The @Value
annotation injects exception messages specified in the application.backdrop
file into the fields.
The application.properties
file is this.
information.exception.message1=BlogAlreadyExists data.exception.message2=BlogNotFound data.exception.message3=DataConnectivityisLost
Permit's send a GET Request tolocalhost:8080/api/v1/blog/2
to remember an unpublished blog. The response is shown in this Figure.
Yous can detect the source code of this postal service on Github
For in-depth cognition on the Spring Framework and Spring Boot, you can check my Udemy Best Seller Grade Spring Framework 5: Beginner to Guru
Source: https://springframework.guru/exception-handling-in-spring-boot-rest-api/
Posted by: mcallisterhessium.blogspot.com
0 Response to "How To Handle Exceptions In Restful Web Service"
Post a Comment