java传参逻辑实体值统一检验_SpringBoot | 第八章:统一异常、数据校验处理

本文介绍了SpringBoot中如何实现全局的统一异常处理和数据校验。通过@ControllerAdvice和@ExceptionHandler创建异常处理类,返回自定义的错误响应格式。同时,利用JSR 303和Hibernate Validator进行参数校验,自定义校验注解,并针对不同异常类型进行特殊处理,提升API的友好性。
摘要由CSDN通过智能技术生成

前言

在web应用中,请求处理时,出现异常是非常常见的。所以当应用出现各类异常时,进行异常的捕获或者二次处理(比如sql异常正常是不能外抛)是非常必要的,比如在开发对外api服务时,约定了响应的参数格式,如respCode、respMsg,调用方根据错误码进行自己的业务逻辑。本章节就重点讲解下统一异常和数据校验处理。

springboot中,默认在发送异常时,会跳转值/error请求进行错误的展现,根据不同的Content-Type展现不同的错误结果,如json请求时,直接返回json格式参数。

浏览器访问异常时:

AAffA0nNPuCLAAAAAElFTkSuQmCC

使用postman访问时:

AAffA0nNPuCLAAAAAElFTkSuQmCC

统一异常处理

显然,默认的异常页是对用户或者调用者而言都是不友好的,所以一般上我们都会进行实现自己业务的异常提示信息。

创建全局的统一异常处理类

利用@ControllerAdvice和@ExceptionHandler定义一个统一异常处理类

@ControllerAdvice:控制器增强,使@ExceptionHandler、@InitBinder、@ModelAttribute注解的方法应用到所有的 @RequestMapping注解的方法。

@ExceptionHandler:异常处理器,此注解的作用是当出现其定义的异常时进行处理的方法

创建异常类:CommonExceptionHandler

@ControllerAdvice

public class CommonExceptionHandler {

/**

* 拦截Exception类的异常

* @param e

* @return

*/

@ExceptionHandler(Exception.class)

@ResponseBody

public Map exceptionHandler(Exception e){

Map result = new HashMap();

result.put("respCode", "9999");

result.put("respMsg", e.getMessage());

//正常开发中,可创建一个统一响应实体,如CommonResp

return result;

}

}

多余不同异常(如自定义异常),需要进行不同的异常处理时,可编写多个exceptionHandler方法,注解ExceptionHandler指定处理的异常类,如

/**

* 拦截 CommonException 的异常

* @param ex

* @return

*/

@ExceptionHandler(CommonException.class)

@ResponseBody

public Map exceptionHandler(CommonException ex){

log.info("CommonException:{}({})",ex.getMsg(), ex.getCode());

Map result = new HashMap();

result.put("respCode", ex.getCode());

result.put("respMsg", ex.getMsg());

return result;

}

由于加入了@ResponseBody,所以返回的是json格式,

AAffA0nNPuCLAAAAAElFTkSuQmCC

AAffA0nNPuCLAAAAAElFTkSuQmCC

说明异常已经被拦截了。

可拦截不同的异常,进行不同的异常提示,比如NoHandlerFoundException、HttpMediaTypeNotSupportedException、AsyncRequestTimeoutException等等,这里就不列举了,读者可自己加入后实际操作下。

对于返回页面时,返回ModelAndView即可,如

@ExceptionHandler(value = Exception.class)

public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {

ModelAndView mav = new ModelAndView();

mav.addObject("exception", e);

mav.addObject("url", req.getRequestURL());

mav.setViewName(DEFAULT_ERROR_VIEW);

return mav;

}

由于工作中都是才有前后端分离开发模式,所以一般上都没有直接返回资源页的需求了,一般上都是返回固定的响应格式,如respCode、respMsg、data,前端通过判断respCode的值进行业务判断,是弹窗还是跳转页面。

数据校验

在web开发时,对于请求参数,一般上都需要进行参数合法性校验的,原先的写法时一个个字段一个个去判断,这种方式太不通用了,所以java的JSR 303: Bean Validation规范就是解决这个问题的。

JSR 303只是个规范,并没有具体的实现,目前通常都是才有hibernate-validator进行统一参数校验。

JSR303定义的校验类型

Constraint

详细信息

@Null

被注释的元素必须为 null

@NotNull

被注释的元素必须不为 null

@AssertTrue

被注释的元素必须为 true

@AssertFalse

被注释的元素必须为 false

@Min(value)

被注释的元素必须是一个数字,其值必须大于等于指定的最小值

@Max(value)

被注释的元素必须是一个数字,其值必须小于等于指定的最大值

@DecimalMin(value)

被注释的元素必须是一个数字,其值必须大于等于指定的最小值

@DecimalMax(value)

被注释的元素必须是一个数字,其值必须小于等于指定的最大值

@Size(max, min)

被注释的元素的大小必须在指定的范围内

@Digits (integer, fraction)

被注释的元素必须是一个数字,其值必须在可接受的范围内

@Past

被注释的元素必须是一个过去的日期

@Future

被注释的元素必须是一个将来的日期

@Pattern(value)

被注释的元素必须符合指定的正则表达式

Hibernate Validator 附加的 constraint

Constraint

详细信息

@Email

被注释的元素必须是电子邮箱地址

@Length

被注释的字符串的大小必须在指定的范围内

@NotEmpty

被注释的字符串的必须非空

@Range

被注释的元素必须在合适的范围内

创建实体类

@Data

@Builder

@NoArgsConstructor

@AllArgsConstructor

@ApiModel

public class LoginForm {

@NotBlank(message = "账号名称不能为空")

@ApiModelProperty(value = "账号名称", dataType = "String", name = "account", example = "admin")

String account;

@ApiModelProperty(value = "账号密码", dataType = "String", name = "password", example = "admin")

@NotBlank(message = "账号密码不能为空")

String password;

}

然后在控制层方法里,加入@Valid即可,这样在访问前,会对请求参数进行检验。

@RestController

@Api(value = "登录接口", description = "登录相关接口", tags = "登录接口")

public class LogginControler extends BaseController {

@ApiOperation(value = "用户登录")

@PostMapping("/login")

public ResultVO login(@Valid @RequestBody LoginForm loginForm) {

return new ResultVO(ResponseStatus.SUCCESS, null);

}

}

启动,后访问   http://127.0.0.1:8080/login

AAffA0nNPuCLAAAAAElFTkSuQmCC不传递请求参数输出响应{"code":999,"msg":"账号密码不能为空","data":null}

不使用@valid的情况下,也可利用编程的方式编写一个工具类,进行实体参数校验

public class ValidatorUtil {

private static Validator validator = ((HibernateValidatorConfiguration) Validation

.byProvider(HibernateValidator.class).configure()).failFast(true).buildValidatorFactory().getValidator();

/**

* 实体校验

*

* @param obj

* @throws CommonException

*/

public static void validate(T obj) throws CommonException {

Set> constraintViolations = validator.validate(obj, new Class[0]);

if (constraintViolations.size() > 0) {

ConstraintViolation validateInfo = (ConstraintViolation) constraintViolations.iterator().next();

// validateInfo.getMessage() 校验不通过时的信息,即message对应的值

throw new CommonException("999", validateInfo.getMessage());

}

}

}

使用

@PostMapping("/login")

public ResultVO login(@RequestBody LoginForm loginForm) {

ValidatorUtil.validate(loginForm);

return new ResultVO(ResponseStatus.SUCCESS, null);

}

自定义校验注解

自定义注解,主要时实现ConstraintValidator的处理类即可,这里已编写一个校验常量的注解为例:参数值只能为特定的值。

自定义注解

@Documented

//指定注解的处理类

@Constraint(validatedBy = {ConstantValidatorHandler.class })

@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })

@Retention(RUNTIME)

public @interface Constant {

String message() default "{constraint.default.const.message}";

Class>[] groups() default {};

Class extends Payload>[] payload() default {};

String value();

}

注解处理类

public class ConstantValidatorHandler implements ConstraintValidator {

private String constant;

@Override

public void initialize(Constant constraintAnnotation) {

//获取设置的字段值

this.constant = constraintAnnotation.value();

}

@Override

public boolean isValid(String value, ConstraintValidatorContext context) {

//判断参数是否等于设置的字段值,返回结果

return constant.equals(value);

}

}

使用

@Constant(message = "verson只能为1.0",value="1.0")

String version;

运行:

AAffA0nNPuCLAAAAAElFTkSuQmCC

此时,自定义注解已生效。大家可根据实际需求进行开发。

大家看到在校验不通过时,返回的异常信息是不友好的,此时可利用统一异常处理,对校验异常进行特殊处理,特别说明下,对于异常处理类

共有以下几种情况(被@RequestBody和@RequestParam注解的请求实体,校验异常类是不同的)

@ControllerAdvice

@Slf4j

public class CommonExceptionHandler {

@ExceptionHandler(IllegalArgumentException.class)

@ResponseBody

public ResultVO exceptionHandler(Exception e) {

ResultVO result = new ResultVO(999, String.format("[%s]参数不正确", e.getMessage()), null);

return result;

}

@ExceptionHandler(MethodArgumentNotValidException.class)

@ResponseBody

public ResultVO handleBindException(MethodArgumentNotValidException ex) {

FieldError fieldError = ex.getBindingResult().getFieldError();

log.info("参数校验异常:{}({})", fieldError.getDefaultMessage(), fieldError.getField());

ResultVO result = new ResultVO(999, fieldError.getDefaultMessage(), null);

return result;

}

@ExceptionHandler(BindException.class)

@ResponseBody

public ResultVO handleBindException(BindException ex) {

//校验 除了 requestbody 注解方式的参数校验 对应的 bindingresult 为 BeanPropertyBindingResult

FieldError fieldError = ex.getBindingResult().getFieldError();

log.info("必填校验异常:{}({})", fieldError.getDefaultMessage(), fieldError.getField());

ResultVO result = new ResultVO(999, fieldError.getDefaultMessage(), null);

return result;

}

}

启动后,提示就友好了

AAffA0nNPuCLAAAAAElFTkSuQmCC

所以统一异常还是很有必要的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值