Loading...

springboot全局异常处理和JSR303数据校验

avatar

李强

2020-08-05

JAVA

在it技术日益发达的今天,越来越多的人涌入it行业,但在这些人中,大多数的人都会选择做java开发,原因不言而喻,java不仅生态越来越好,而且岗位需求也很大,而一谈到java,那就离不开spring,spring可以说是做java开发所离不开的框架,spring的强大之处,也能在工作中一一体会到,而我们今天所谈论的springboot,就是在spring的基础上进一步封装的更加强大的框架。

在以往的日常开发中,最烦劳的莫过于程序跑着跑着出异常了,然后一步一步去定位找到问题,解决问题,没有一个统一的异常解决方案,这让人很是苦恼,而在有了springboot以后,由于集成了springMVC的功能,加上springboot本身的自动配置,这就让我们的异常处理变的很容易了,以最小的配置去实现功能强大的异常处理,那么,就让我们继续往下看吧。

##环境准备 ** jdk1.8 maven springboot**

首先准备maven的依赖,如下 ##导入依赖

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.6.RELEASE</version>
    <relativePath/>
</parent>
<dependency>
	 <groupId>org.springframework.boot</groupId>
 	 <artifactId>spring-boot-starter-web</artifactId>
</dependency>

##异常注解介绍

@ControllerAdvice:标注在类上,表明该类是一个全局异常处理类
@RestControllerAdvice:功能同上,只不过多了一个功能,在接收异常时,以json的格式返回错误信息,相似性参考@Controller和@RestControllerAdvice
@ExceptionHandler:该注解标注在方法上,表明该方法用来接受哪个类型的异常
				 Class<? extends Throwable>[] value() default {};
				 接受的异常类型数组。写Throwable.class,就是接受接受所有类型的异常
				 

好了,介绍了基本异常处理注解,接下来就是代码的编写和测试了 ##代码编写 先来测试最简单也是最基本的异常处理 ###controller层编写

	@GetMapping("/hello")
    public String hello(){
        System.out.println("hello world");
		//假如刚好执行到这一步出现了异常
		//模拟一个出现异常的业务场景
		int i = 1 / 0;
        return "success";
    }

###全局异常处理编写 让我们来看看springboot是怎么处理异常的

@Slf4j  //lombok的日志记录
//注意:这里如果用的是@ControllerAdvice的话,下面的方法要加@ResponseBody
@RestControllerAdvice(basePackages = //包名,指定controller包所在的位置)
public class GlobalExceptionControllerAdvice {

    @ExceptionHandler(value = {Throwable.class})
    public CommResult handleException(Throwable throwable //要接收的异常的类,Throwable表示接收所有的异常类型){
		log.error("接收到的异常类型为:{}",throwable.getClass());
        log.error("接收到的错误信息为:{}:",throwable.getMessage());
		//返回json格式的错误信息
        return CommResult.error(500,"服务器内部错误");
    }

}

好了,一个简单的全局异常处理,就写完了,现在就让我们测试一下吧

现在,一个最基本的全局异常处理就完成了,测试也有了效果,现在开始进入下一个主题 ##JSR303 谈起JSR,不免想起java规范,没错,JSR就是java规范,它里面定义了java的很多规范,既然是规范,那么就要有具体的落地实现,现在就来谈谈JSR303吧 在官网有这么一句描述,对javaBean的验证,简单来说呢,它就是来对javaBean做数据校验的, 它的具体落地实现就是hebernate validation,这是hebernate对它的一种实现,spring对它也进行了实现,两种都可以使用 ###进入正题 让我们来看看一些校验注解

@Null  		//标注的属性为null
@NotNull 	//标注的属性值不为null
@NotBlank  	//标注的属性不能为空
@NotEmpty   //同上
@Paattern   //传入的参数要和规定的正则想匹配
@Min		//指定传入参数的最小值
@Max		//指定传入参数的最大值
@URL		//传入的参数必须是一个URL地址
@Email		//传入的参数必须是一个Email
@Size		//指定传入参数的大小
@Future		//传入的参数必须是一个未来的时间
除开这些,还有很多的校验注解等等...
这些注解不仅可以标注在JavaBean上,还可以标注在方法、构造器、注解类型等...

好了,介绍完上面一些常用的注解,现在就来使用看看吧,代码如下 ###javaBean

首先定义一个JavaBean,其内容如下
	@NotNull(message = "传入的id不能为null")
	private Long id;
	
	@NotBlank(message = "姓名不能为空")
	private String name;
	
	@NotEmpty(message = "logo不能为空")
	@URL(message = "logo必须是一个URL地址")
	private String logo;
	
	@NotBlank
	private String descript;

上面定义了一个简单的JavaBean,并使用了一些常用的注解,其它和注解也可以参考上面 ###controller

@PostMapping
public CommResult save(@Valid //只需要在提交的参数前加一个注解就行 @RequestBody User user){
	//模拟执行保存方法
	userService.save(user);
	return CommResult.ok();
}

使用数据校验就是这么简单,现在前端传过来的数据都会先校验一遍,所有字段都合法了才会进行下一步操作。 那么,问题来了,在JavaBean上面,除了定义校验注解,还在上面自定义了一些信息,在校验不通过时,如何才能知道,如何才能处理,如何才能拿到自定义的消息呢?接着往下看

BindingResult

@PostMapping
public CommResult save(@Valid //只需要在提交的参数前加一个注解就行 @RequestBody User user,BindingResult bindingResult){
	log.error("是否有数据校验错误:{}",bindingResult.hasErrors());
	//模拟执行保存方法
	userService.save(user);
	return CommResult.ok();
}

其实,拿到错误消息,也很简单,只需要在传入的参数位置加一个BindingResult就可以了,通过BindingResult就可以拿到数据校验的错误信息。

但是controller只是来用来处理请求,不做过多的业务处理,所以,BindingResult就不可以放在那里了,这时,就要用到我们的上面所说的全局异常处理了。

##整合环境搭建 javaBean

public class User{
	@NotNull(message = "传入的id不能为null")
	private Long id;
	
	@NotBlank(message = "姓名不能为空")
	private String name;
	
	@NotEmpty(message = "logo不能为空")
	@URL(message = "logo必须是一个URL地址")
	private String logo;
	
	@NotBlank
	private String descript;
}
因为懒,我就直接用上面的了

UserController

@PostMapping
public CommResult save(@Valid @RequestBody User user){
	//模拟执行保存方法
	userService.save(user);
	return CommResult.ok();
}

GlobalExceptionAdvice

@Slf4j
@RestContrllerAdvice(basePackage = {com.lq.controller})
public class GlobalExceptionAdvice{

	//这一个方法专门来接收数据校验出现的异常
	@ExceptionHandler(value = BindException.class)
	public CommonResult exceptionHandl(BindException e){
		log.error("出现异常:{},错误类型为:{}",e.getMessage(), e.getClass());
		//获取BindingResult
		BindingResult bindingResult = e.getBindingResult();
		//获取所有出现校验错误的字段
		List<FieldError> fieldErrors = bindingResult.getFieldErrors()
		Map<String,Object> map = new HashMap<>();
		fieldErrors.forEach(fieldError -> {
			//把校验错误的字段名作为key,校验的错误信息作为value,一起放入map
			map.put(fieldError.getField(), fieldError.getDefaultMessage());
		};
		return CommonResult.error().put("fieldErrors",map);
	}

}

以上,就是JSR303和全局异常处理结合使用起来的效果,一些其它的细节我就不说了,比如,可以用BindingResult获取其它的信息,有兴趣的可以试试,测试我也懒的测试了,拜拜┏(^0^)┛

-->

Other article