Spring Boot系列SpringBoot全局异常处理数据校验

Posted 一宿君

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Boot系列SpringBoot全局异常处理数据校验相关的知识,希望对你有一定的参考价值。

5.1 SpringBoot异常处理

我们知道在做SpingBoot时,难免会出现很多异常,想要快速解决这些异常必须要知道异常出在何处,又是何种错误,SpringBoot内置的已经有了一些异常处理,例如:RuntimeException、ArithmeticException、NullPointerException等等异常,但是仅仅这些异常是远远不够我们对于异常的断定和处理,所以才会延伸出局部异常处理、全局异常和深度异常处理等等,中心思想还是为了能够更加方便的了解出现异常时,知道是何种错误并快速处理,这样才不会盲目的去改bug。

SpringBoot默认的处理异常的机制,一旦程序中出现了异常会像/error 的 url 发送请求。默认BasicExceptionController 来处理/error 请求,然后跳转到默认显示异常的页面,也可以自定义error.html 页面。

5.1.1 Controller层局部异常处理

先看一段局部异常处理:

package com.kdcrm.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.HashMap;
import java.util.Map;

@Controller
public class ToIndexController {


    @RequestMapping("/testException")
    public void testException(){
        //制造运行时异常
        System.out.println(1/0);
    }

    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public Map exceptionHandler(Exception ex)
    {
        Map map=new HashMap<>();
        map.put("code",101);
        map.put("msg",ex.toString());
        return map;
    }
 
}

5.1.2 基于@ControllerAdvice注解的Controller层的全局异常处理

上述类中定义的异常处理,只能处理当前类中的异常,其他类中的捕获不到,所以我们引入了基于 @ControllerAdvice注解 的Controller层的全局异常处理:

package com.kdcrm.controller;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import java.util.HashMap;
import java.util.Map;


@ControllerAdvice
public class GlobalExceptionHandlerController {

	/**
     * 捕获Controller层的所有异常信息
     * @param ex
     * @return
     */
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public Map exceptionHandler(Exception ex)
    {
        Map map=new HashMap<>();
        map.put("code",203);
        map.put("msg",ex.toString());
        return map;
    }
}


也可以将异常信息跳转到指定页面内,便于管理:
error.html:

<!DOCTYPE html>
<html lang="en" mlns:th="http://www.thymeleaf.org" xmlns:mlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8">
    <title>error页面</title>
</head>
<body>
<!--接收异常信息-->
<h1 th:text="${msg}"></h1>
</body>
</html>

Controller层全局异常处理GlobalExceptionHandlerController:

package com.kdcrm.controller;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import java.util.HashMap;
import java.util.Map;

@ControllerAdvice
public class GlobalExceptionHandlerController {
 
    /**
     * 将异常信息跳转值指定error页面
     * @param ex
     * @return
     */
    @ExceptionHandler(value = Exception.class)
    public ModelAndView exceptionHandler2(Exception ex){
        ModelAndView mav = new ModelAndView();
        mav.addObject("msg",ex.toString());
        mav.setViewName("error");
        return mav;
    }
}

5.1.3 HandlerExceptionResolver处理异常解析器

上述是通过@ControllerAdvice注解的方式实现的全局异常处理,还有一种是实现HandlerExceptionResolver处理异常解析器接口的方式,两者原理是一样的,根据出现的是何种异常跳转至相应的页面,更加精准些:

package com.kdcrm.controller;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class GlobalExceptionHandlerController implements HandlerExceptionResolver {
    
    @Override
    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception ex) {
        ModelAndView mav = new ModelAndView();
        //判断不同异常类型,做不同的视图跳转
        if(ex instanceof ArithmeticException) {
            mav.setViewName("ArithmeticError");
        }else if (ex instanceof NullPointerException){
            mav.setViewName("NullpointerError");
        }else if (ex instanceof IllegalAccessException){
            mav.setViewName("IllegalAccessError");
        }

        mav.addObject("msg",ex.toString());

        return mav;

    }

}

5.2 SpringBoot数据校验

我们可能会经常需要对传入的参数进行校验,如果数据比较少的时候还比较容易处理,但当数据比较多的时候会显得比较麻烦,而且处理不当的时候,还会代码重复。这时候就需要对参数进行校验了,而Spring Boot采用的是Validation对数据进行校验。

  • SpringBoot对表单数据校验的技术特点,使用了Hibernate-validation校验框架。

首先引入校验包依赖文件:

<dependency>
	<groupId>org.hibernate.validator</groupId>
	<artifactId>hibernate-validator</artifactId>
	<version>7.0.0.Final</version>
</dependency>	            

JSR 303 是Bean验证的规范 ,Hibernate Validator 是该规范的参考实现,它除了实现规范要求的注解外,还额外实现了一些注解。
包括一下注解:

注解说明
@AsserrtFalse元素必须为false
@AssertTrue元素必须为true
@DecimalMax(value)元素必须为数字,最大值为指定value
@DecimalMin(value)元素必须为数字,最小值为指定value
@Digits(integer,fraction)元素必须为数字,其值必须在可接受的范围内
@Null元素必须为null
@NotNull元素必须不为null
@Min(value)元素必须为数字,其值大于等于指定value
@Max(value)元素必须为数字,其值小于等于指定value
@Size(min,max,message)元素大小必须在指定范围内,message加以说明
@Past元素必须是一个过去的日期
@Furture元素必须是一个将来的日期
@Pattern元素符合指定的正则表达式
@Email元素必须为电子邮箱格式
@Length(min,max,message)必须是字符串,其值在指定范围内,message加以说明
@NotBlank(message)字符串不能为null,且trim后不能为空字符串(长度非0),message加以说明
@NotEmpty被注释的字符串、集合、数组不能为空(长度非0)
@Range元素在合适的范围内
@SafeHtml元素必须是安全Html
@URL元素必须是有效URL

上述注解只是其中一部分,高亮注解是常用于Bean属性的校验,通过 @ConfigurationProperties(prefix = “user”) 引入配置文件并自动绑定属性:
User类:

package com.kdcrm.pojo;

import org.hibernate.validator.constraints.Length;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.annotation.Validated;
import javax.validation.constraints.*;
import java.util.List;
import java.util.Map;
import java.util.Set;

@Configuration
@ConfigurationProperties(prefix = "user")
@Validated
public class User {

    /**
     * @notNull 限制不能为空
     * @NotEmpty 元素不能为null,且不为空(字符串和集合的长度不能为0)
     * @NotBlank(message = "XXX不能为空"),判断字符串是否为null,或者去掉首尾空格是否为空串
     * @Length(min = 6,max = 10,message = "最短为6位,最大为10位")
     * @Min(value = "6") 限制最小值
     * @Max(value = "10") 限制最大值
     * @Email 判断邮箱是否合法
     * @NotEmpty 字符串、集合、数组不能为空(长度非0)
     */

    @NotNull
    @Length(min = 1,max = 3,message = "名字长度最小为1,最大为3")
    private String name;

    @NotNull
    @Min(value = 6)
    @Max(value = 16)
    private String password;

    @NotNull
    @Size(min = 1,max = 100,message = "最小1岁,最大100岁")
    private int age;

    @NotBlank(message = "性别不能为空")
    private String sex;

    @Email
    private String email;

    @NotEmpty
    private List list;

    @NotEmpty
    private Set set;

    @NotEmpty
    private Map map;

}

application.yml配置文件

user:
  name: wbs
  password: 123456
  age: 21222
  sex: 男男男男
  email: 184519@163.com
  list:
    - dog
    - cat
    - pig
  set:
    - dog
    - cat
    - pig
  map:
    k1: zs
    k2: ls
    k3: ww

Controller层进行对象数据校验,用@Valid注解,通过BindingResult接收校验结果:

package com.kdcrm.controller;

import com.kdcrm.pojo.User;
import com.kdcrm.service.UserService;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import io.swagger.annotations.ResponseHeader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;


/**
 * @author 一宿君(CSDN : qq_52596258)
 * @date 2021-08-06 08:52:36
 */
@Controller
public class PeopleController {

    @ResponseBody
    @RequestMapping("/addUser")
    public ModelAndView addUser(@Valid User user, BindingResult result, ModelAndView mav){
        if(result.hasErrors()){
            //如果有错误,跳转至添加页面
            mav.setViewName("addUser");
            return mav;
        }
        //如果没有错误,则将数据存入到request作用域中
        mav.addObject("user",user);
        mav.setViewName("UserIndex");
        return mav;
    }

}

以上是关于Spring Boot系列SpringBoot全局异常处理数据校验的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot2 系列教程(十三)Spring Boot 中的全局异常处理

Spring Boot2 系列教程 (十四) | 统一异常处理

Spring Boot系列——配置相关

spring boot框架学习3-spring boot核心

Spring Boot 全局懒加载

Spring Boot 全局懒加载