SpringMVC全局Web异常处理以及接口参数校验方案笔记

0 本文主要涉及

在基于Spring和SpringMVC的前后端分离的JavaWeb项目中一种异常处理以及接口参数校验方案介绍说明

1 方案简介

网上介绍的全局异常处理一种是老的基于ModelAndView后端返回的是错误页面并不适合前后端分离架构的后端使用,还有一种需要在每个controler里写点代码不是全局的方案,接下来介绍的是一种全局处理的的返回JSON格式数据的后端异常处理方案,顺带也处理了参数校验的逻辑(原理在代码中注释说明了)。

2 配置实现

0,pom.xml依赖配置

<!--Web-->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>${spring.version}</version>
</dependency>
<!--参数校验-->
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>5.4.1.Final</version>
</dependency>
<!--数据格式化-->
<!-- jackson -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>${jackson.version}</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>${jackson.version}</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>${jackson.version}</version>
</dependency>

1,代码配置实现

@RestControllerAdvice
public class MyGlobalExceptionHandler extends ResponseEntityExceptionHandler
{
    //不同环境返回不同的错误信息
    @Data
    @AllArgsConstructor
    class DevExceptionInfoType
    {
        //返回json数据的格式
        Integer exceptionCode;
        Date dateTime;
        String message;
        String exceptionType;
        List stackTreace;
    }

    @Data
    @AllArgsConstructor
    class ProdExceptionInfoType
    {
        //返回json数据的格式
        Integer exceptionCode;
        Date dateTime;
        String message;
    }

    //用户判断运行环境
    @Value("${debug.exception.info.show}")
    Boolean isDebugInfoShow=false;

    @Override
    //重写springmvc默认异常处理(查看源码发现各种异常最终在这个函数处理,不包含HttpStatus.INTERNAL_SERVER_ERROR 500
    public ResponseEntity<Object> handleExceptionInternal(Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request)
    {
        //参数校验异常处理
        if(ex instanceof BindException)
        {
            return ValidationExceptionHandler.defaultExceptionHandler((BindException) ex, headers, status, request);
        }
        //其他异常
        List<String> stackTreace = new ArrayList<>();
        for(StackTraceElement element : ex.getStackTrace())
        {
            stackTreace.add(element.toString());
        }
        if(isDebugInfoShow)
        {
            return new ResponseEntity<>(new DevExceptionInfoType(ex instanceof MyMessageException ?((MyMessageException)ex).exceptionCode:ExceptionCode.deault, new Date(), ex.toString(), ex.getClass().toString(), stackTreace), headers, status);
        }
        else
        {
            return new ResponseEntity<>(new ProdExceptionInfoType(ex instanceof MyMessageException ?((MyMessageException)ex).exceptionCode:ExceptionCode.deault, new Date(), ex.toString()), headers, status);
        }
    }

    //这里示例字处理了MyMessageException异常,实际按照业务可能会有多种异常
    //其他异常处理,返回HttpStatus.INTERNAL_SERVER_ERROR 500
    @ExceptionHandler(value = Exception.class)
    public Object defaultExceptionHandler(Exception ex)
    {
        List<String> stackTreace = new ArrayList<>();
        for(StackTraceElement element : ex.getStackTrace())
        {
            stackTreace.add(element.toString());
        }
        //输出错误信息到日志
        org.slf4j.LoggerFactory.getLogger(getClass()).error("\n" + ex.toString(), ex);
        if(isDebugInfoShow)
        {
            return new ResponseEntity<>(new DevExceptionInfoType(ex instanceof MyMessageException ?((MyMessageException)ex).exceptionCode:ExceptionCode.deault, new Date(), ex.getMessage(), ex.getClass().toString(), stackTreace), HttpStatus.INTERNAL_SERVER_ERROR);
        }
        else
        {
            return new ResponseEntity<>(new ProdExceptionInfoType(ex instanceof MyMessageException ?((MyMessageException)ex).exceptionCode:ExceptionCode.deault, new Date(), ex.getMessage()), HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }
}

2,使用示例

异常处理:在Service层只需要直接抛出自定义的的业务异常即可,可以附带一些错误信息定义在业务异常的类中,最后会返回到前端业务异常的数据,前端根据code判断错误类型并做相应处理即可。
参数校验:
简单的,在Controler入参增加@Validated注解,并且在入参的Bean对象中通过注解设置好校验条件(
校验条件写法:https://segmentfault.com/a/1190000006908069

@Data
@Accessors(chain = true)
public class TestQuery {
    @NotEmpty(message = "id不可为空")
    private String id;
}
public ResponseResult getTest(@Validated TestQuery testQueryParam) {
        return ResponseResult.create();
}

如果同一个参数的Bean要用在不同的方法做入参并且条件不一样的话就需要换一种写法,通过不同的接口进行分组校验

@Data
@Accessors(chain = true)
public class PlanInfoVO {
    public interface Add {
    }

    public interface Update {
    }

    @NotNull(message = "id不可为空", groups = {Update.class})
    @Null(message = "id必须为空", groups = {Add.class})
    private String id;
}
//校验的注解还需要加载分组信息
public ResponseResult getTest( @Validated({TestQuery.Add.class}) TestQuery testQueryParam) {
        return ResponseResult.create();
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值