java 入参校验_java web项目如何优雅的进行入参的校验

本文介绍了在Java Web项目中如何利用javax.validation和Hibernate-Validator进行入参校验,通过自定义异常和统一异常处理,实现优雅的错误反馈。详细展示了如何创建校验实体类、Controller处理、异常捕获以及自定义业务校验。
摘要由CSDN通过智能技术生成

前言

之前看过有一个同事写的代码,他为了进行细粒度的返回值提示,针对每一种参数不合法的情况,都规定了一个code值,然后在service层去进行各种校验,捕捉各种异常,然后返回给controller不同的code码。controller再根据这些code码,返回不同的错误提示。

他这样做可以改进的地方有两处:

service层先根据错误类型返回不同的code码,controller再根据不同的code码返回不同的文案。进行了两步处理,代码量显得特别大。

在service层捕捉了大量异常,造成事务失效,为后人埋下了很多坑。

javax.validation

介绍

javax.validation 是基于JSR-303标准开发出来的,使用注解方式实现,及其方便,但是这只是一个接口,没有具体实现。

Hibernate-Validator是一个hibernate独立的包,可以直接引用,他实现了javax.validation同时有做了扩展,比较强大。

SpringBoot在spring-boot-starter-web中引入了hibernate-validator,所以我们在springboot的web项目中可以直接使用。

具体使用

我就直接堆代码了, 由于代码比较多,所以用了一种折叠的方式,不过貌似只有chrome等少数的几个浏览器才支持。

返回值的包装类:

展开源码

public class ResponseDataimplements Serializable {

private String code;

private String message;

private T data;

public String getCode() {

return code;

}

public void setCode(String code) {

this.code = code;

}

public String getMessage() {

return message;

}

public void setMessage(String message) {

this.message = message;

}

public T getData() {

return data;

}

public void setData(T data) {

this.data = data;

}

public static class ResponseDataBuilder{

private ResponseDataresponseData;

public ResponseDataBuilder() {

responseData = new ResponseData<>();

}

public ResponseDataBuilder code(String code) {

responseData.setCode(code);

return this;

}

public ResponseDataBuilder message(String message) {

responseData.setMessage(message);

return this;

}

public ResponseDataBuilder data(T data) {

responseData.setData(data);

return this;

}

public ResponseDataBuilder success() {

responseData.setCode("0000");

responseData.setMessage("请求成功");

return this;

}

public ResponseData build() {

return responseData;

}

}

}

请求实体类:

展开源码

public class Phone {

@NotEmpty(message = "手机名称不能为空")

private String name;

private String brand;

@Min(value = 1, message = "电话尺寸最小为1")

@Max(value = 10, message = "电话尺寸最大为10")

private Integer size;

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public String getBrand() {

return brand;

}

public void setBrand(String brand) {

this.brand = brand;

}

public Integer getSize() {

return size;

}

public void setSize(Integer size) {

this.size = size;

}

}

可以看到上面的代码中对字段"name"进行了非空校验,对字段"size"进行的大小的限制。

controller:

展开源码

@RestController

@RequestMapping("/test")

public class TestController {

@PostMapping("/a")

public ResponseData test(@RequestBody @Validated Phone phone) {

return new ResponseData.ResponseDataBuilder<>().success().build();

}

}

统一的异常处理类,主要用来捕捉所有的validate异常,统一返回值的结构:

展开源码

@ControllerAdvice

public class MyExceptionHandler {

private Logger log = LoggerFactory.getLogger(MyExceptionHandler.class);

@ExceptionHandler(ValidationException.class)

@ResponseBody

public ResponseData handleException(ValidationException exception) {

String errorMessage = exception.getMessage();

log.error(errorMessage);

return new ResponseData.ResponseDataBuilder<>().code("9999").message(errorMessage).build();

}

@ExceptionHandler(BindException.class)

@ResponseBody

public ResponseData handleException(BindException exception) {

String errorMessageStr = getErrorMessageStr(exception);

log.error(errorMessageStr);

return new ResponseData.ResponseDataBuilder<>().code("9999").message(errorMessageStr).build();

}

@ExceptionHandler(MethodArgumentNotValidException.class)

@ResponseBody

public ResponseData handleException(MethodArgumentNotValidException exception) {

String errorMessageStr = getErrorMessageStr(exception);

log.error(errorMessageStr);

return new ResponseData.ResponseDataBuilder<>().code("9999").message(errorMessageStr).build();

}

private String getErrorMessageStr(BindException exception) {

ListerrorMessages = exception.getBindingResult().getAllErrors().stream().map(ObjectError::getDefaultMessage).collect(Collectors.toList());

String errorMessageStr = errorMessages.stream().filter(message -> !StringUtils.isEmpty(message)).collect(Collectors.joining("--"));

return errorMessageStr;

}

private String getErrorMessageStr(MethodArgumentNotValidException exception) {

ListerrorMessages = exception.getBindingResult().getAllErrors().stream().map(ObjectError::getDefaultMessage).collect(Collectors.toList());

String errorMessageStr = errorMessages.stream().filter(message -> !StringUtils.isEmpty(message)).collect(Collectors.joining("--"));

return errorMessageStr;

}

}

我们怎么简单的运用

上面介绍的方法,可以对入参进行非业务的校验。但是在实际的项目中,我们还会遇到业务型的校验,比如:注册的时候判断用户名是否重复,这个时候我们就可以借鉴上面的思想进行一些优雅的校验。

自定义一个校验异常

展开源码

public class MyValidateException extends RuntimeException {

public MyValidateException() {

super();

}

public MyValidateException(String message) {

super(message);

}

public MyValidateException(String message, Throwable cause) {

super(message, cause);

}

public MyValidateException(Throwable cause) {

super(cause);

}

protected MyValidateException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {

super(message, cause, enableSuppression, writableStackTrace);

}

}

在service层去进行业务校验

展开源码

@Service

public class TestService {

Listlist = new ArrayList<>(Arrays.asList("Huawei"));

public void test(Phone phone) {

if (list.contains(phone.getName())) {

throw new MyValidateException("手机的名字已存在");

}

}

}

统一的异常处理

展开源码

@ControllerAdvice

public class MyExceptionHandler {

private Logger log = LoggerFactory.getLogger(MyExceptionHandler.class);

@ExceptionHandler(MyValidateException.class)

@ResponseBody

public ResponseData handleException(MyValidateException exception) {

String errorMessage = exception.getMessage();

log.error(errorMessage);

return new ResponseData.ResponseDataBuilder<>().code("9999").message(errorMessage).build();

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值