@valid和自定义异常

问题的产生:

当有很多参数需要校验时,比如name,age,email等很多参数都需要判空,或者有长度限制时,如果后端写很多if-else就有很多代码,不美观,不优雅.前端每个参数都效验的话工作量也很大

本文旨在解决这个问题,本文使用@valid 注解来解决这个问题.

首先定义一个

统一结果返回

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@NoArgsConstructor
@AllArgsConstructor
@Data
public class Result<T> {

    private String msg;
    private Integer code;
    private T data;
    private static final Integer successCode = 200;
    private static Integer errorCode = 500;
    private static final String successMsg = "成功";
    private static final Object resultNoData = null;

    public static Result successNoResult() {
        return new Result(successMsg, successCode, resultNoData);
    }

    public static <T> Result<T> successWithResult(T data) {
        return new Result(successMsg, successCode, data);
    }

    public static Result successWithCodeAndMsg(Integer code, String msg) {
        return new Result(msg, code, resultNoData);
    }

    public static Result errorNoResult(String msg) {
        return new Result(msg, errorCode, resultNoData);
    }

    public static Result errorWithResult(String msg, Object data) {
        return new Result(msg, errorCode, data);
    }

    public static Result errorWithCodeAndMsg(Integer code, String msg) {
        return new Result(msg, code, resultNoData);
    }

}

@valid 注解简单使用

先看下简单使用,复杂的自己查api吧

首先在控制层的参数上加上该注解

import javax.validation.Valid;
import java.util.*;

@RestController
@RequestMapping("/test")
public class Test2 {

    @RequestMapping("test2")
    public Result<User> test2(@Valid User user){
        return Result.successWithResult(user);
    }
}

然后在实体类中加上如下注解

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

public class User {

    /**
     * @NotEmpty:不能为null,而且长度必须大于0
     * @NotBlank:只用在String上,表示传进来的值不能为null,而且调用trim()后,长度必须大于0
     * @NotNull:不能为null,但可以为empty
     * @Length(message = "名称不能超过个 {max} 字符", max = 10)
     * @Range(message = "年龄范围为 {min} 到 {max} 之间", min = 1, max = 100)
     */
    @NotNull( message = "ID不能为空")
    private Integer id;

    @NotBlank( message = "昵称不能为空")
    @Size( min = 2,max = 5,message ="昵称的字数个数必须在0和5之间" )
    private String name;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

请求该接口

http://localhost:8080/test/test2?id=5&name=xxwwwww

查看效果,如下

@valid和自定义异常-LMLPHP

可以看到,已经能表示该请求参数错误

但是还有个问题,我们不能直接这样子直接返回给前端,需要返回统一的结果

全局异常处理

Spring-boot对于异常的处理也做了不错的支持,

它提供了一个 @ControllerAdvice注解以及 @ExceptionHandler注解,

前者是用来开启全局的异常捕获,后者则是说明捕获哪些异常,对那些异常进行处理。如下

自定义异常

import lombok.Data;

@Data
public class DefinitionException extends RuntimeException {
    private Integer errorCode;
    private String errorMsg;

    public DefinitionException(){

    }
    public DefinitionException(Integer errorCode, String errorMsg) {
        this.errorCode = errorCode;
        this.errorMsg = errorMsg;
    }

}

异常处理

@ControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 处理自定义异常
     */
    @ExceptionHandler(value = DefinitionException.class)
    @ResponseBody
    public Result bizExceptionHandler(DefinitionException definitionException) {
        Result result=new Result();
        result.setCode(definitionException.getErrorCode());
        result.setMsg(definitionException.getErrorMsg());
        return result;
    }

    /**
     * 处理异常
     */
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public Result runtimeExceptionHandler(Exception exception) {
        Result result=new Result();
        result.setCode(500);
        result.setMsg(exception.getMessage());
        return result;
    }
}

测试代码

@RequestMapping("test3")
public Result<String> test3(){
int i=1/0;
return Result.successWithResult("test3");
}

@RequestMapping("test4")
public Result<String> test4(){
throw new DefinitionException(500,"啊哦,报错了");
}

查看结果

@valid和自定义异常-LMLPHP

@valid和自定义异常-LMLPHP

将@valid注解抛的异常也返回统一格式

我们再请求一下这个接口

http://localhost:8080/test/test2?id=5&name=xxwwwww

看下结果

@valid和自定义异常-LMLPHP

返回格式是统一的格式了,但是返回的信息不太友好,我们看看怎么优化

debug一下,看看这个是什么异常

@valid和自定义异常-LMLPHP

我们看到,这个异常是org.springframework.validation.BindException类的,

我们看下这个类的具体内容,我们只要我们想要的信息就行

@valid和自定义异常-LMLPHP

这里,我们只要这个信息就可以了我们改动后如下

    /**
     * 处理异常
     */
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public Result runtimeExceptionHandler(Exception exception) {
        Result result=new Result();
        if(exception instanceof BindException){//注解类异常
            StringBuilder sb = new StringBuilder();
            BindException bindException= (BindException) exception;
            BindingResult bindingResult = bindException.getBindingResult();
            List<ObjectError> allErrors = bindingResult.getAllErrors();
            for (ObjectError item : allErrors) {
                sb
                        .append(item.getDefaultMessage())
                        .append(',');
            }
            sb.deleteCharAt(sb.length()-1);
            result.setCode(500);
            result.setMsg(sb.toString());

        }
        return result;
    }

再请求该接口,得到结果

@valid和自定义异常-LMLPHP

到此为止,我们已经得到了我们想要的结果

优化代码

最后,我们在优化一下全局异常处理代码如下

import com.yoocar.util.Result;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;

@ControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 处理自定义异常
     */
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public Result exceptionHandler(Exception exception) {
        Result result=new Result();
        //自定义类型异常
        if(exception instanceof DefinitionException){
            DefinitionException definitionException= (DefinitionException) exception;
            result.setCode(definitionException.getErrorCode());
            result.setMsg(definitionException.getErrorMsg());
        }else if(exception instanceof BindException){//@valid注解抛出的异常
            //使用StringBuilder来拼接错误信息,减少对象开销
            StringBuilder stringBuilder = new StringBuilder();
            //获取并拼接所有错误信息
            BindException bindException= (BindException) exception;
            BindingResult bindingResult = bindException.getBindingResult();
            List<ObjectError> allErrors = bindingResult.getAllErrors();
            for (ObjectError item : allErrors) {
                stringBuilder.append(item.getDefaultMessage())
                        .append(',');
            }
            //删除最后一个逗号
            stringBuilder.deleteCharAt(stringBuilder.length()-1);
            result.setCode(600);//这里自定义了600用于表示参数有误
            result.setMsg(stringBuilder.toString());
        }else {//其他异常
            result.setCode(500);
            result.setMsg(exception.getMessage());
        }
        return result;
    }

}

至此,本文结束.文章中若有错误和疏漏之处,还请各位大佬不吝指出,谢谢大家!

04-25 04:51