处理例外,逻辑错误并向客户提供有意义的消息非常重要,以避免Springboot中的RESTFUL API异常终止。
今天,我们将学习如何实施全球异常处理以及班级级别的异常处理。
先决条件:您必须了解Springboot和Restful API创建,JPA(Java Persistance API),以及在Java Core中进行异常处理的一些知识。
我们将使用 java-17 用 Spring Initilizr
创建春季启动模板项目我们需要该项目的以下依赖项。
pom.xml
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.bukhari</groupId>
<artifactId>exceptionhandling-validation</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>exceptionhandling-validation</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.properties文件
#Dabase Configuration
spring.application.name=Exception-Handling-Application
server.port=8082
spring.datasource.url=jdbc:mysql://localhost:3306/libdb
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.testWhileIdle=true
spring.datasource.validationQuery=SELECT 1
spring.jpa.hibernate.naming-strategy=org.hibernate.dialect.MySQL8Dialect
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.hibernate.ddl-auto=create
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.formate_sql=true
班级级别异常处理
代码的层次结构
实体书
package com.bukhari.exceptionhandlingvalidation.entity;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
@Entity
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String author;
private Double price;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
}
bookrepository
确保您从Entity.book软件包中导入本书,而不是从Java.awt.print
package com.bukhari.exceptionhandlingvalidation.repository;
import com.bukhari.exceptionhandlingvalidation.entity.Book;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface BookRepository extends JpaRepository<Book, Long> {
}
我们要处理的例外是
- 书籍名称不能为空。
- 书籍作者名称不能为空。
- 书籍的价格必须为$ 10或大于10美元。 (假设价格始终是非空的)。
书籍服务
package com.bukhari.exceptionhandlingvalidation.service;
import com.bukhari.exceptionhandlingvalidation.classexception.LibBusinessException;
import com.bukhari.exceptionhandlingvalidation.entity.Book;
import com.bukhari.exceptionhandlingvalidation.repository.BookRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class BookService {
@Autowired
private BookRepository bookRepository;
public Book addBook(Book book) {
//If the Name of the book is Empty
if (book.getName().isEmpty()) {
throw new LibBusinessException("801", "The Name of the Book can't be Empty");
}
//If the Name of the Author is Empty
if (book.getAuthor().isEmpty()) {
throw new LibBusinessException("802", "The Author's Name can't be Empty");
}
//If the Price is less than 10
if (book.getPrice().intValue() < 10) {
throw new LibBusinessException("803", "The Price of the Book must be $10 or greater than $10");
}
return bookRepository.save(book);
}
}
libbusinessException
package com.bukhari.exceptionhandlingvalidation.classexception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(HttpStatus.BAD_REQUEST)
public class LibBusinessException extends RuntimeException {
private String errorCode;
private String errorMessage;
public LibBusinessException(String errorCode, String errorMessage) {
super();
this.errorCode = errorCode;
this.errorMessage = errorMessage;
}
public String getErrorCode() {
return errorCode;
}
public void setErrorCode(String errorCode) {
this.errorCode = errorCode;
}
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
}
bookController
package com.bukhari.exceptionhandlingvalidation.controller;
import com.bukhari.exceptionhandlingvalidation.classexception.LibBusinessException;
import com.bukhari.exceptionhandlingvalidation.entity.Book;
import com.bukhari.exceptionhandlingvalidation.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("digital/lib")
public class LibraryController {
@Autowired
private BookService bookService;
@PostMapping("/book")
public ResponseEntity<?> saveBook(@RequestBody Book book) {
try {
Book savedBook = bookService.addBook(book);
return new ResponseEntity<Book>(savedBook, HttpStatus.CREATED);
} catch (LibBusinessException e) {
LibBusinessException libex = new LibBusinessException(e.getErrorCode(), e.getErrorMessage());
return new ResponseEntity<LibBusinessException>(libex, HttpStatus.BAD_REQUEST);
} catch (Exception e) {
LibBusinessException libex = new LibBusinessException("804", "Something went wrong in controller");
return new ResponseEntity<LibBusinessException>(libex, HttpStatus.BAD_REQUEST);
}
}
}
如果书名为空
Postman请求和响应
如果书籍作者名称为空
Postman请求和响应
如果书价小于$ 10
Postman请求和响应
使用ControllerAdvice验证和全局异常处理
让我们现在了解验证和全局异常处理
为此,我们将采用Appuser实体。 (pom.xml是相同的)。
appuser实体
package com.bukhari.exceptionhandlingvalidation.entity;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
@Entity
public class AppUser {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int userId;
private String name;
private String email;
private String mobile;
private String gender;
private int age;
private String nationality;
public AppUser() {
}
public AppUser(int userId, String name, String email, String mobile, String gender, int age, String nationality) {
this.userId = userId;
this.name = name;
this.email = email;
this.mobile = mobile;
this.gender = gender;
this.age = age;
this.nationality = nationality;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getNationality() {
return nationality;
}
public void setNationality(String nationality) {
this.nationality = nationality;
}
}
请求appuser
package com.bukhari.exceptionhandlingvalidation.request;
import jakarta.validation.constraints.*;
/**
* Global Exception Handling AppUserRequest
*/
public class AppUserRequest {
@NotNull(message = "username must not be null")
private String name;
@Email(message = "Invalid email address, Please provide a valid email")
private String email;
@Pattern(regexp = "^\\d{10}$", message = "Invalid mobile number, Please Provide a Valid Number")
private String mobile;
private String gender;
@Min(value = 18, message = "Age must be 18 or greater")
@Max(value = 60, message = "Age must be 60 or less")
private int age;
@NotBlank
private String nationality;
public AppUserRequest(String name, String email, String mobile, String gender, int age, String nationality) {
this.name = name;
this.email = email;
this.mobile = mobile;
this.gender = gender;
this.age = age;
this.nationality = nationality;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getNationality() {
return nationality;
}
public void setNationality(String nationality) {
this.nationality = nationality;
}
}
我们将验证并处理以下异常
- 名称不得为null。
- 电子邮件必须有效。
- 手机号码必须有效。
- 年龄必须在18至60之间。
- 国籍不能是空白的(非空和非净的)。
- 如果找不到用户,则将自定义类映射到异常。
为此,我们将@RestControllerAdvice
用作全局异常处理程序,与班级方法相比,它是一种更好的方法。 @RestControllerAdvice
映射到所有控制器,一旦触发了请求,请参阅任何映射匹配项的建议检查,并相应地发送响应。由于Spring为我们提供了@ExceptionHandler
注释以拦截任何Java以及逻辑异常,因此我们可以根据自己的要求实施。
RestControllerRadvice
package com.bukhari.exceptionhandlingvalidation.globalexception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.HashMap;
import java.util.Map;
/**
* Global Exception Handling ControllerAdvice
*/
@RestControllerAdvice
public class GlobalExceptionHandler {
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentNotValidException.class)
public Map<String, String> handleInvalidArgument(MethodArgumentNotValidException ex) {
Map<String, String> errorMap = new HashMap<>();
ex.getBindingResult().getFieldErrors().forEach(error -> {
errorMap.put(error.getField(), error.getDefaultMessage());
});
return errorMap;
}
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(UserNotFoundException.class)
public Map<String, String> handleBusinessException(UserNotFoundException ex) {
Map<String, String> errorMap = new HashMap<>();
errorMap.put("errorMessage", ex.getMessage());
return errorMap;
}
}
usernotfoundException的自定义类
package com.bukhari.exceptionhandlingvalidation.globalexception;
/**
* Global Exception Handling UserNotFoundException
*/
public class UserNotFoundException extends Exception{
public UserNotFoundException(String message) {
super(message);
}
}
appuserrepository
package com.bukhari.exceptionhandlingvalidation.repository;
import com.bukhari.exceptionhandlingvalidation.entity.AppUser;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
/**
* Global Exception Handling AppUserRepository
*/
@Repository
public interface AppUserRepository extends JpaRepository<AppUser,Integer> {
}
AppuserService
package com.bukhari.exceptionhandlingvalidation.service;
import com.bukhari.exceptionhandlingvalidation.entity.AppUser;
import com.bukhari.exceptionhandlingvalidation.globalexception.UserNotFoundException;
import com.bukhari.exceptionhandlingvalidation.repository.AppUserRepository;
import com.bukhari.exceptionhandlingvalidation.request.AppUserRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
/**
* Global Exception Handling AppUserService
*/
@Service
public class AppUserService {
@Autowired
private AppUserRepository appUserRepository;
public AppUser saveUser(AppUserRequest userRequest) {
AppUser user = new AppUser(0, userRequest.getName(), userRequest.getEmail(),
userRequest.getMobile(), userRequest.getGender(), userRequest.getAge(), userRequest.getNationality());
return appUserRepository.save(user);
}
public List<AppUser> getALlUsers() {
return appUserRepository.findAll();
}
public AppUser getUser(int id) throws UserNotFoundException {
Optional<AppUser> optional = appUserRepository.findById(id);
if (optional.isPresent()) {
return optional.get();
} else {
throw new UserNotFoundException("user not found with id : " + id);
}
}
}
appusercontroller
package com.bukhari.exceptionhandlingvalidation.controller;
import com.bukhari.exceptionhandlingvalidation.entity.AppUser;
import com.bukhari.exceptionhandlingvalidation.globalexception.UserNotFoundException;
import com.bukhari.exceptionhandlingvalidation.request.AppUserRequest;
import com.bukhari.exceptionhandlingvalidation.service.AppUserService;
import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/users")
public class AppUserController {
@Autowired
private AppUserService appUserService;
@PostMapping("/signup")
public ResponseEntity<AppUser> saveUser(@RequestBody @Valid AppUserRequest userRequest){
return new ResponseEntity<>(appUserService.saveUser(userRequest), HttpStatus.CREATED);
}
@GetMapping("/fetchAll")
public ResponseEntity<List<AppUser>> getAllUsers(){
return ResponseEntity.ok(appUserService.getALlUsers());
}
@GetMapping("/{id}")
public ResponseEntity<AppUser> getUser(@PathVariable int id) throws UserNotFoundException {
return ResponseEntity.ok(appUserService.getUser(id));
}
}
@Valid
注释用于验证有效载荷
邮递员请求和响应如下。
如果数据无效
如果找不到用户。
希望该教程有帮助。您从中学到了一些东西。如果有帮助,请确保遵循和喜欢。