前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringBoot项目实战:自定义异常和统一参数验证(附源码)

SpringBoot项目实战:自定义异常和统一参数验证(附源码)

作者头像
田维常
发布2023-08-31 11:20:24
5460
发布2023-08-31 11:20:24
举报
文章被收录于专栏:Java后端技术栈cwnait

你好,我是田哥

在实际开发过程中,不可避免的是需要处理各种异常,异常处理方法随处可见,所以代码中就会出现大量的try {...} catch {...} finally {...} 代码块,不仅会造成大量的冗余代码,而且还影响代码的可读性,所以对异常统一处理非常有必要。为此,我们定义了一个统一的异常类BusinessException

自定义异常和统一校验参数已用于 充电桩项目 中。

自定义异常

代码语言:javascript
复制
/**
 * @author tianwc  
 * @version 1.0.0
 * @date 2023年06月11日 22:41
 * 博客地址:<a href="http://woaijava.cc/">博客地址</a>
 * <p>
 * 自定义异常
 */
@Getter
public class BusinessException extends RuntimeException {
    /**
     * http状态码
     */
    private int code;

    private Object object;

    public BusinessException(String message, int code, Object object) {
        super(message);
        this.code = code;
        this.object = object;
    }

    public BusinessException(ResultCode resultCode) {
        this.code = resultCode.getCode();
        this.object = resultCode.getMessage();
    }

    public BusinessException(ResultCode resultCode, String message) {
        this.code = resultCode.getCode();
        this.object = message;
    }

    public BusinessException(String message) {
        super(message);
    }
}

一些常见需要对参数进校验,项目中自定义了一个校验工具类ParamValidate

代码语言:javascript
复制
public class ParamValidate {

    public static void isNull(Object object, String message) {
        if (object == null) {
            throw new BusinessException(ResultCode.PARAMETER_EMPTY, message);
        }
    }

    public static void isTrue(boolean expression, String message) {
        if (!expression) {
            throw new BusinessException(ResultCode.PARAMETER_ERROR, message);
        }
    }

    public static void notEmpty(Collection collection, String message) {
        isNull(collection, message);
        if (collection.size() == 0) {
            throw new BusinessException(ResultCode.PARAMETER_ERROR, message);
        }
    }
}

工具使用:

代码语言:javascript
复制
@RestController
@RequestMapping("/test")
public class TestController {

    @Resource
    private MessageTemplateSingleton messageTemplateSingleton;

    @GetMapping("/index1")
    public CommonResult<MessageTemplateSingleton.Template> index() {
        ParamValidate.isNull(null, "参数为空");
        MessageTemplateSingleton.Template template = messageTemplateSingleton.getTemplate(1);
        return CommonResult.success(template);
    }
}

自定义异常统一处理:

代码语言:javascript
复制
@RestControllerAdvice
@Slf4j
public class ChargeStationAdvice {

    @ExceptionHandler(Exception.class)
    public CommonResult<String> doException(Exception e) {

        log.error("统一处理自定义异常机制,触发异常 msg ", e);
        String message = null;
        int errorCode = ResultCode.FAILED.getCode();
        //自定义异常
        if (e instanceof BusinessException) {
            BusinessException exception = (BusinessException) e;
            message = exception.getMessage();
            errorCode = exception.getCode();
        } else if (e instanceof HttpRequestMethodNotSupportedException) {
            message = "不支持GET/POST方法";
        } else if (e instanceof NoHandlerFoundException) {
            message = "请求接口不存在";
        }  else{
             message = "系统异常";
        }
        return CommonResult.failed(errorCode, message);
    }
}

测试:

GET http://localhost:9001/test/index1

返回:

代码语言:javascript
复制
{
  "code": 400007,
  "message": null,
  "data": null
}

到此我们的自定义异常的定义、处理、测试就搞定了。

但是,我们在上面使用到的是统一异常处理,我们在方法参数验证时候,也会用到统一异常处理。

统一参数验证

我们后台使用spring 为我们提供好的统一校验的工具spring-boot-starter-validation对请求进行校验。

pom依赖:

代码语言:javascript
复制
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

这里通过注解封装了几种常用的校验

  • @NotNull 不能为null
  • @NotEmpty 不能为null、空字符串、空集合
  • @NotBlank 不能为null、空字符串、纯空格的字符串
  • @Min 数字最小值不能小于x
  • @Max 数字最大值不能大于x
  • @Email 字符串为邮件格式
  • @Max 数字最大值不能大于x
  • @Size 字符串长度最小为x、集合长度最小为x
  • @Pattern 正则表达式

因为校验不通过时,会抛出各种异常,所以,我们需要对这些异常进行统一处理。

代码语言:javascript
复制
@RestControllerAdvice
@Slf4j
public class ChargeStationAdvice {

    @ExceptionHandler(Exception.class)
    public CommonResult<String> doException(Exception e) {

        log.error("统一异常处理机制,触发异常 msg ", e);
        String message = null;
        int errorCode = ResultCode.FAILED.getCode();
        //自定义异常
        if (e instanceof BusinessException) {
            BusinessException exception = (BusinessException) e;
            message = exception.getMessage();
            errorCode = exception.getCode();
        } else if (e instanceof HttpRequestMethodNotSupportedException) {
            message = "不支持GET/POST方法";
        } else if (e instanceof NoHandlerFoundException) {
            message = "请求接口不存在";
        } else if (e instanceof MissingServletRequestParameterException) {
            errorCode = ResultCode.PARAMETER_EMPTY.getCode();
            message = String.format("缺少必要参数[%s]", ((MissingServletRequestParameterException) e).getParameterName());
        } else if (e instanceof MethodArgumentNotValidException) {
            BindingResult result = ((MethodArgumentNotValidException) e).getBindingResult();
            FieldError error = result.getFieldError();
            errorCode = ResultCode.PARAMETER_EMPTY.getCode();
            message = error == null ? ResultCode.PARAMETER_ERROR.getMessage() : error.getDefaultMessage();
        } else if (e instanceof BindException) {
            errorCode = ResultCode.PARAMETER_EMPTY.getCode();
            message = e.getMessage();
        } else if (e instanceof ConstraintViolationException) {
            Optional<ConstraintViolation<?>> first = ((ConstraintViolationException) e).getConstraintViolations().stream().findFirst();
            errorCode = ResultCode.PARAMETER_EMPTY.getCode();
            message = first.get().getMessage();
        }
        return CommonResult.failed(errorCode, message);
    }
}

上面几个异常进行解释说明:

MissingServletRequestParameterException :加了@RequestParam注解,但是接口调用时没有传指定的参数(注意:是没有传,而不是传了,但是值是null)。

MethodArgumentNotValidException :经过测试,当校验的参数放在对象中,接口的请求方式是post请求,用@Valid @RequestBody方式接受参数时,如果报错,会被该捕获器捕获。

BindException :经过测试,当校验参数写在类中,接口请求方式是get请求时,报错会被该捕获器捕获。

ConstraintViolationException :传了值,但是不符合要求。@NotNull(message = “最大值不能为空”) @Min(value = 10,message = "参数必须大于10"),要求传非null值,且值必须大于10,否则会返回错误信息。经过测试,当校验参数直接写在接口上,而不是写在类中,报错会被该捕获器捕获。

参数校验实体类:

代码语言:javascript
复制
import javax.validation.constraints.Min;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.io.Serializable;

/**
 * @author tianwc  
 * @version 1.0.0
 * @date 2023年05月09日 20:03
 * 博客地址:<a href="http://woaijava.cc/">博客地址</a>
 */
@Data
public class TestDto implements Serializable {
    @NotNull
    @Min(value = 10,message = "参数必须大于10")
    private Long id;
    @NotEmpty(message = "name参数不能为空")
    private String name;
}

注意:每个注解对应的包路径。

我们在Controller层使用TestDto,并使用@Valid注解,使校验的注解生效:

代码语言:javascript
复制
@Slf4j
@RestController
@RequestMapping("/test")
public class TestController {

    @Resource
    private MessageTemplateSingleton messageTemplateSingleton; 

    @PostMapping("/index2")
    public CommonResult<MessageTemplateSingleton.Template> index2(@RequestBody @Valid TestDto testDto) {
        log.info("入参={}", testDto);
        MessageTemplateSingleton.Template template = messageTemplateSingleton.getTemplate(1);
        return CommonResult.success(template);
    }
}

注意:注解 @Valid 的全路径为:javax.validation.Valid;

测试:

POST http://localhost:9001/test/index2

Content-Type: application/json; charset=UTF-8

{ "id": 11, "name": "" }

返回:

代码语言:javascript
复制
{
  "code": 400007,
  "message": "name参数不能为空",
  "data": null
}
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-06-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Java后端技术全栈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 自定义异常
  • 统一参数验证
相关产品与服务
腾讯云服务器利旧
云服务器(Cloud Virtual Machine,CVM)提供安全可靠的弹性计算服务。 您可以实时扩展或缩减计算资源,适应变化的业务需求,并只需按实际使用的资源计费。使用 CVM 可以极大降低您的软硬件采购成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档