谷粒商城-全栈-26 商品服务-JSR303 统一异常处理
一、JSR3030基本用法学习
- 1)、给Bean添加校验注解:javax.validation.constraints,并定义自己的message提示
- 2)、开启校验功能@Valid
效果:校验错误以后会有默认的响应; - 3)、给校验的bean后紧跟一个BindingResult,就可以获取到校验的结果
给bean定义自己的message
/**
* 品牌名
*/
@NotBlank(message = "品牌名不能为空")
private String name;
还有其他的校验用法,可以看看
package com.atguigu.gulimall.product.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.util.Date;
import lombok.Data;
import org.hibernate.validator.constraints.URL;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
/**
* 品牌
*
* @author kaiyi
* @email corwienwong@gmail.com
* @date 2020-08-10 15:45:20
*/
@Data
@TableName("pms_brand")
public class BrandEntity implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 品牌id
*/
@TableId
private Long brandId;
/**
* 品牌名
*/
@NotBlank(message = "品牌名不能为空")
private String name;
/**
* 品牌logo地址
*/
@NotEmpty(message = "URL地址不能为空")
@URL(message = "logo必须是一个合法的url地址")
private String logo;
/**
* 介绍
*/
private String descript;
/**
* 显示状态[0-不显示;1-显示]
*/
private Integer showStatus;
/**
* 检索首字母
*/
@NotEmpty
@Pattern(regexp = "/^[a-zA-Z]$/", message = "检索首字母必须是一个字母")
private String firstLetter;
/**
* 排序
*/
@NotNull
@Min(value = 0, message = "排序必须大于等于0")
private Integer sort;
}
Controller 中需要校验的参数Bean前添加 @Valid 开启校验功能,紧跟在校验的Bean后添加一个BindingResult,BindingResult封装了前面Bean的校验结果。
@RestController
@RequestMapping("product/brand")
public class BrandController {
@RequestMapping("/save")
public R save (@Valid @RequestBody BrandEntity brand, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
Map<String , String> map = new HashMap<>();
bindingResult.getFieldErrors().forEach( (item) -> {
String message = item.getDefaultMessage();
String field = item.getField();
map.put( field , message );
} );
return R.error( 400 , "提交的数据不合法 !" ).put("data", map);
}
else
{
brandService.save(brand);
return R.ok();
}
}
}
二、异常的统一处理
难道在每个方法都写这么一大堆的异常结果响应处理吗,需要这么麻烦了?NO,参数校验不通过时,会抛出 BindingException 异常,可以在统一异常处理中,做统一处理,这样就不用在每个需要参数校验的地方都用 BindingResult 获取校验结果了。
做统一异常处理,可以使用SpringMVC提供的 @ControllerAdvice
系统错误码
1、在 gulimall-product 服务创建异常类文件:product/exception/GulimallExceptionControllerAdvice.java
package com.atguigu.gulimall.product.exception;
import com.atguigu.common.exception.BizCodeEnum;
import com.atguigu.common.utils.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.HashMap;
import java.util.Map;
/**
* 集中处理所有异常
*
* @author: kaiyi
* @create: 2020-08-21 00:56
*/
@Slf4j
//@ResponseBody
//@ControllerAdvice(basePackages = "com.atguigu.gulimall.product.controller")
// 处理product.controller包下边的异常
@RestControllerAdvice(basePackages = "com.atguigu.gulimall.product.controller")
public class GulimallExceptionControllerAdvice {
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public R handleValidException(MethodArgumentNotValidException e){
log.error("数据校验出现问题{},异常类型:{}", e.getMessage(), e.getClass());
BindingResult bindingResult = e.getBindingResult();
Map<String, String> errorMap = new HashMap<>();
bindingResult.getFieldErrors().forEach((fieldError)->{
errorMap.put(fieldError.getField(),fieldError.getDefaultMessage());
});
return R.error(BizCodeEnum.VALID_EXCEPTION.getCode(),
BizCodeEnum.VALID_EXCEPTION.getMsg()).put("data", errorMap);
}
/**
* 通用的异常处理方法
* @description 如果找不到具体的异常处理方法,则由该方法处理
*
* @param throwable
* @return
*/
@ExceptionHandler(value = Throwable.class)
public R handleException(Throwable throwable){
log.error("错误:", throwable);
return R.error(BizCodeEnum.UNKNOW_EXCEPTION.getCode(), BizCodeEnum.UNKNOW_EXCEPTION.getMsg());
}
}
2、在gulimall-common 服务创建通用的异常代码枚举类
由于以后随着业务的不断扩展,需要定义不同的异常码和规范标准化,所以,这里直接定义通用的异常枚举类。gulimall-common/src/main/java/com/atguigu/common/exception/BizCodeEnum.java
package com.atguigu.common.exception;
/***
* 错误码和错误信息定义类
* 1. 错误码定义规则为5为数字
* 2. 前两位表示业务场景,最后三位表示错误码。例如:100001。10:通用 001:系统未知异常
* 3. 维护错误码后需要维护错误描述,将他们定义为枚举形式
* 错误码列表:
* 10: 通用
* 001:参数格式校验
* 11: 商品
* 12: 订单
* 13: 购物车
* 14: 物流
*
*
*/
public enum BizCodeEnum {
UNKNOW_EXCEPTION(10000, "系统未知异常"),
VALID_EXCEPTION(10001, "参数格式校验失败");
private int code;
private String msg;
BizCodeEnum(int code, String msg){
this.code = code;
this.msg = msg;
}
public int getCode(){
return code;
}
public String getMsg(){
return msg;
}
}
3、修改原方法的异常处理product/controller/BrandController.java
/**
* 保存
*/
@RequestMapping("/save")
//@RequiresPermissions("product:brand:save")
public R save(@Validated @RequestBody BrandEntity brand/*,BindingResult result*/){
// if(result.hasErrors()){
// Map<String,String> map = new HashMap<>();
// //1、获取校验的错误结果
// result.getFieldErrors().forEach((item)->{
// //FieldError 获取到错误提示
// String message = item.getDefaultMessage();
// //获取错误的属性的名字
// String field = item.getField();
// map.put(field,message);
// });
//
// return R.error(400,"提交的数据不合法").put("data",map);
// }else {
//
// }
brandService.save(brand);
return R.ok();
}
通过统一异常处理之后,原来需要在方法里边写异常处理的方式现在不需要了,代码又恢复到原来简洁的样子。
4、Postman测试
使用POSTman进行测试,可以看到返回的异常结果非常规范,业务代码code也是我们定义的 10001,至此,统一封装完成。
为者常成,行者常至
自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)