Spring MVC中的异常处理

引言

在Spring MVC中有两个级别的异常处理方式:

  • Controller级别: 它只能处理当前Controller类中出现的异常。
  • 全局的异常处理级别:所有的Spring MVC中的拦截机器(HandlerInterceptor),处理器(@Controller)中的异常。

当然,Spring MVC中有内置的异常处理对象,但是呈现的结果对于用户端不友好,所以实际项目我们一般会自定义异常处理。

全局异常处理对象的定义

我们可以在spring boot启动类所在的包或子包中定义Spring MVC全局异常处理类,这个类的特点是需要使用

RestControllerAdvice注解进行描述,并且可以在类中定义多个异常处理方法(需要使用@HandlerException

注解进行描述), 例如:

text 复制代码
package cn.tedu.weibo.exception;

import cn.tedu.weibo.common.response.ResultVO;
import cn.tedu.weibo.common.response.StatusCode;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;

/**
 * RestControllerAdvice 描述的类型为一个全局异常处理对象类型,
 * 当某个Controller方法中出现了异常,此时在Controller类内部,又没有定义对应的
 * 异常处理方法(使用ExcetpionHandler注解描述的方法),系统底层就会查找有没有定义全局异常处理对象。
 * 这个全局异常处理对象中有没有定义对应的异常处理方法,假如有就调用此方法处理异常。
 */

@Slf4j //lombok提供的日志注解,在代码层面会为我们提供一个org.slf4j.Logger对象
@RestControllerAdvice //=@ControllerAdvice+@ResponseBody
public class GlobalExceptionHandler {
    //private static final Logger log= LoggerFactory.getLogger(GlobalExceptionHandler.class);
    /**
     *  @ExceptionHandler 描述的方法为一个异常处理方法,在此注解内部可以定义具体的异常处理
     *  类型(例如RuntimeException.class),此注解描述的方法需要定义一个异常类型的形式参数,
     *  通过这个参数接收具体的异常对象(也可以接收其异常类型对应的子类类型的异常)。
     * @return
     */
    @ExceptionHandler(RuntimeException.class)
    public ResultVO doHandleRuntimeException(RuntimeException ex){
        log.error("error is {}",ex.getMessage());//日志级别trace<debug<info<warn<error
        //log中的{}为占位符,后面的ex.getMessage()返回的信息,会显示到{}这个位置
        return new ResultVO(0,ex.getMessage());
    }
}

说明,这里的@Slf4j注解为Lombok提供,此注解描述类时会在类中创建一个日志对象,基于日志对象可以输出一些日志,

日志的级别可以在application.properties文件中进行配置,例如logging.level.cn.tedu=debug,这里的debug

表示日志级别(trace<debug<info<warn<error),当配置的日志界别为debug时,当前这个日志级别以及比这个日志级

别高的日志级别会输出日志。

Spring MVC中的参数校验

引言

在实际项目我们需要对客户端传递到服务端的参数进行校验,用于判定请求参数的合法性,假如请求参数不合法,

不可以再去执行后续的业务了。那如何校验呢?一种方式是我们在控制层方法中每次都自己进行参数有效值的判断,

不合法可以抛出异常,但是工作量和代码复杂度会比较高。第二种方式就是采用市场上主流的Spring Validation

框架去实现校验。

Spring Validation的基本应用

第一步:添加依赖

text 复制代码
     <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>

第二步:通过注解对Controller类、方法参数、dto对象中的属性进行描述

对WeiboDTO中的Content属性进行NotNull标注

@Data
public class WeiboDTO {
    // 微博内容
    @NotBlank(message = "content can not be empty string") //不允许为空串
    private String content;
}

对WeiboController方法中的参数添加@Validated注解描述

text 复制代码
    @PostMapping("insert")
    @ApiOperation(value = "发布微博功能")
    public int insert(@Validated @RequestBody WeiboDTO weiboDTO, @ApiIgnore HttpSession session){
    }

第三步:访问WeiboController中的insert方法,给content传null,检测访问效果。

text 复制代码
POST http://localhost:8080/v1/weibo/insert
Content-Type: application/json

{
  "content": ""
}

第四步:自己做(可以通过搜索引擎自己进行拓展)-下午模拟面试结束

1)如何对验证时出现的异常进行处理。(重点)

text 复制代码
/**
* 对参数校验异常进行处理
* @param ex
* @return
*/
@ExceptionHandler(BindException.class)
public ResultVO doBindException(BindException ex){
ObjectError objectError = ex.getBindingResult().getAllErrors().get(0);
return new ResultVO(StatusCode.VALIDATE_ERROR,
objectError.getDefaultMessage());
}

2)查Spring Validation还有那些注解可以应用。(课后了解)

@NotNull:不允许为null值

可以用于任何类型

@NotEmpty:不允许为空字符串(长度为0的字符串),并且会检查是否为null值(为null时报错)

仅能用于字符串类型的参数

@NotBlank:不允许为空白,即不允许是"仅由空格、TAB等空白值组成的字符串"

仅能用于字符串类型的参数

@Range:通过配置min和max属性来限制数值类型参数的值区间,它不会检查是否为null(为null并不执行任何检查,且不会报错)

仅能用于整型参数

@Pattern:通过配置regexp属性来配置正则表达式

仅能用于字符串类型的参数

第六步:自己做(可以通过搜索引擎自己进行拓展)-下午模拟面试结束

对于非pojo参数(例如参数列表中的int id)的检查,需要先在当前方法所在的类上添加@Validated注解,例如:

text 复制代码
@RestController
@RequestMapping("/v1/weibo/")
@Api(tags = "02.微博模块")
@Slf4j
@Validated
public class WeiboController {

}

然后,可以在参数上添加对应的检查注解,例如:

text 复制代码
    @GetMapping("selectById")
    @ApiOperation(value = "微博详情功能")
    @ApiImplicitParam(name="id",value = "微博id",required = true,example = "1",dataType = "int")
    public WeiboDetailVO selectDetail(@Range(min = 1, message = "请提交合法的ID值!") int id){
        //System.out.println("id="+id);
        log.debug("id's value is {}",id);
        if(id==-1)
            throw new IllegalArgumentException("id值无效");
        // 无需校验用户登录状态
        return weiboMapper.selectDetail(id);
    }

最后进行访问测试,输入id值小于1的数据,比对进行进行分析

Spring Validation高级应用

Spring Validation在检查请求参数的格式时,会检查所有属性配置的规则,找出所有的错误,

如果希望实现"只要发现错误,就不再向后检查",需要将其配置为"快速失败",配置做法是使用配置类:

text 复制代码
@Configuration
public class ValidationConfiguration {

    @Bean
    public javax.validation.Validator validator() {
        return Validation.byProvider(HibernateValidator.class)
                .configure() // 开始配置
                .failFast(true) // 配置快速失败
                .buildValidatorFactory() // 构建Validator工厂
                .getValidator(); // 从Validator工厂中获取Validator对象
    }

}
相关推荐
guicai_guojia1 小时前
面试题篇: 跨域问题如何处理(Java和Nginx处理方式)
java·开发语言·nginx
Amagi.1 小时前
对比介绍Java Servlet API (javax.servlet)和Apache HttpClient这两个库
java·servlet·apache
易雪寒2 小时前
Maven从入门到精通(二)
java·maven
易雪寒2 小时前
Maven从入门到精通(三)
java·python·maven
AskHarries2 小时前
maven父子工程多模块如何管理统一的版本号?
java·spring boot·后端·maven
码农娟3 小时前
hutool 集合相关交集、差集
java
Good_tea_h3 小时前
如何实现Java中的多态性
java·开发语言·python
Flying_Fish_roe3 小时前
springcloud-GateWay
后端·spring·spring cloud
IT毕设梦工厂3 小时前
计算机毕业设计选题推荐-项目评审系统-Java/Python项目实战
java·spring boot·python·django·毕业设计·源码·课程设计
Flying_Fish_roe3 小时前
Cassandra 和 ScyllaDB
java