springBoot2.X配置全局捕获异常的操作

springBoot2.X配置全局捕获异常

先来看一段代码:当传入的id是0的时候,就会报异常。

@RestController
public class HelloController {
    @GetMapping("/getUser")
    public String getUser(int id) {
        int j = 1 / id;
        return "SUCCESS" + j;
    }
}

访问时:

我们知道这个页面要是给用户看到,用户可能不知道这是什么。

方法一:将异常捕获

@GetMapping("/getUser")
    public String getUser(int id) {
        int j;
        try {
            j = 1 / id;
        } catch (Exception e) {
            return "系统异常";
        }
        return "SUCCESS" + j;
    }

这种方法当然可以,但是当我们有很多方法时,需要在每个方法上都加上。

哎,太鸡肋了吧。

那么都没有全局的拦截处理呢?

当然了

方法二:通过@ControllerAdvice注解配置

/**
 * @Author 刘翊扬
 * @Date 2020/9/30 11:39 下午
 * @Version 1.0
 */
@ControllerAdvice(basePackages = "com.yiyang.myfirstspringdemo.controller")
public class GlobalExceptionHandler {
    @ExceptionHandler(RuntimeException.class)
    @ResponseBody
    public Map<String,Object> errorResult()  {
        Map<String, Object> map = new HashMap<>();
        map.put("errorCode", "500");
        map.put("errorMsg", "全局捕获异常");
        return map;
    }
}
  • @ExceptionHandler 表示拦截异常
  • @ControllerAdvice 是 controller 的一个辅助类,最常用的就是作为全局异常处理的切面类
  • @ControllerAdvice 可以指定扫描范围

注意:下面还需要在启动类上加上,否则诶呦效果

package com.yiyang.myfirstspringdemo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication(scanBasePackages = {"com.yiyang.myfirstspringdemo.error", "com.yiyang.myfirstspringdemo.controller"})
public class MyFirstSpringDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyFirstSpringDemoApplication.class, args);
    }
}

在启动类上,将扫描包范围controller和全局异常处理类,加上去。

这样当我们在访问的时候,出现的异常提示信息就是我们在全局异常处理中设置的返回值。

springboot2.x 全局异常处理的正确方式

在web项目中,异常堆栈信息是非常敏感的。因此,需要一个全局的异常处理,捕获异常,给客户端以友好的错误信息提示。基于 Spring boot 很容易实现全局异常处理。

相关jar依赖引入

<!-- Spring Boot 启动父依赖 -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.6.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
</properties>

<dependencies>
    <!-- Spring Boot Web 依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

全局异常控制器

package com.yb.demo.common.handler;
import com.yb.demo.common.enums.CodeEnum;
import com.yb.demo.common.exception.BizException;
import com.yb.demo.pojo.response.Result;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.validation.BindException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.validation.ValidationException;
import java.util.StringJoiner;

/**
 * 全局异常处理
 * <p>
 * 规范:流程跳转尽量避免使用抛 BizException 来做控制。
 *
 * @author daoshenzzg@163.com
 * @date 2019-09-06 18:02
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 处理自定义异常
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(BizException.class)
    public Result handleBizException(BizException ex) {
        Result result = Result.renderErr(ex.getCode());
        if (StringUtils.isNotBlank(ex.getRemark())) {
            result.withRemark(ex.getRemark());
        }
        return result;
    }

    /**
     * 参数绑定错误
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(BindException.class)
    public Result handleBindException(BindException ex) {
        StringJoiner sj = new StringJoiner(";");
        ex.getBindingResult().getFieldErrors().forEach(x -> sj.add(x.getDefaultMessage()));
        return Result.renderErr(CodeEnum.REQUEST_ERR).withRemark(sj.toString());
    }

    /**
     * 参数校验错误
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(ValidationException.class)
    public Result handleValidationException(ValidationException ex) {
        return Result.renderErr(CodeEnum.REQUEST_ERR).withRemark(ex.getCause().getMessage());
    }

    /**
     * 字段校验不通过异常
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
        StringJoiner sj = new StringJoiner(";");
        ex.getBindingResult().getFieldErrors().forEach(x -> sj.add(x.getDefaultMessage()));
        return Result.renderErr(CodeEnum.REQUEST_ERR).withRemark(sj.toString());
    }

    /**
     * Controller参数绑定错误
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public Result handleMissingServletRequestParameterException(MissingServletRequestParameterException ex) {
        return Result.renderErr(CodeEnum.REQUEST_ERR).withRemark(ex.getMessage());
    }

    /**
     * 处理方法不支持异常
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
    public Result handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException ex) {
        return Result.renderErr(CodeEnum.METHOD_NOT_ALLOWED);
    }

    /**
     * 其他未知异常
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(value = Exception.class)
    public Result handleException(Exception ex) {
        log.error(ex.getMessage(), ex);
        return Result.renderErr(CodeEnum.SERVER_ERR);
    }
}

个性化异常处理

自定义异常

在实际web开发过程中,往往会遇到在某些场景下需要终止当前流程,直接返回。那么,通过抛出自定义异常,并在全局异常中捕获,用以友好的提示客户端。

/**
 * 业务异常跳转。
 *
 * @author daoshenzzg@163.com
 * @date 2019-09-09 14:57
 */
@Data
public class BizException extends RuntimeException {
    private static final long serialVersionUID = 1L;
    private CodeEnum code;
    private String remark;

    public BizException(CodeEnum code) {
        super(code.getMessage());
        this.code = code;
    }

    public BizException withRemark(String remark) {
        this.remark = remark;
        return this;
    }
}

使用方式如下:

/**
     * 添加学生
     *
     * @param student
     * @return
     */
    public Student1DO addStudent(Student1DO student) {
        if (StringUtils.isNotBlank(student.getStudName())) {
            // 举例扔个业务异常,实际使用过程中,应该避免扔异常
            throw new BizException(CodeEnum.REQUEST_ERR).withRemark("studName不能为空");
        }
        student1Mapper.insert(student);
        return student;
    }

返回效果如下:

{
"code": -400,
"msg": "请求错误(studName不能为空)",
"data": {},
"ttl": 0
}

根据阿里巴巴规范,流程控制还是不要通过抛异常的方式。在正常开发过程中,应避免使用这种方式。

【强制】异常不要用来做流程控制,条件控制,因为异常的处理效率比条件分支低。

使用 Spring validation 完成数据后端校验

定义实体类,加上validation相关注解

package com.yb.demo.pojo.model.db1;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import javax.validation.constraints.Min;
import javax.validation.constraints.Size;

/**
 * @author daoshenzzg@163.com
 * @date 2019-08-05 17:58
 */
@Data
@TableName("student")
public class Student1DO {
    private Long id;
    @Size(max = 8, message = "studName长度不能超过8")
    private String studName;
    @Min(value = 12, message = "年龄不能低于12岁")
    private Integer studAge;
    private String studSex;
    @TableField(fill = FieldFill.INSERT)
    private Integer createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Integer updateTime;
}

在Controller 方法上加上 @Validated 注解

@PostMapping("/student/add")
public Result addStudent(@Validated @RequestBody Student1DO student) {
    student = studentService.addStudent(student);
    return Result.renderOk(student);
}

实际效果如下:

{
"code": -400,
"msg": "请求错误(年龄不能低于12岁)",
"data": {},
"ttl": 0
}

结束语

具体代码见:https://github.com/daoshenzzg/springboot2.x-example

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • 详解Spring Boot2 Webflux的全局异常处理

    本文首先将会回顾Spring 5之前的SpringMVC异常处理机制,然后主要讲解Spring Boot 2 Webflux的全局异常处理机制. SpringMVC的异常处理 Spring 统一异常处理有 3 种方式,分别为: 使用 @ExceptionHandler 注解 实现 HandlerExceptionResolver 接口 使用 @controlleradvice 注解 使用 @ExceptionHandler 注解 用于局部方法捕获,与抛出异常的方法处于同一个Controller类

  • SpringBoot项目优雅的全局异常处理方式(全网最新)

    前言 在日常项目开发中,异常是常见的,但是如何更高效的处理好异常信息,让我们能快速定位到BUG,是很重要的,不仅能够提高我们的开发效率,还能让你代码看上去更舒服,SpringBoot的项目已经对有一定的异常处理了,但是对于我们开发者而言可能就不太合适了,因此我们需要对这些异常进行统一的捕获并处理. 一.全局异常处理方式一 SpringBoot中,@ControllerAdvice 即可开启全局异常处理,使用该注解表示开启了全局异常的捕获,我们只需在自定义一个方法使用@ExceptionHandl

  • Springboot之自定义全局异常处理的实现

    前言: 在实际的应用开发中,很多时候往往因为一些不可控的因素导致程序出现一些错误,这个时候就要及时把异常信息反馈给客户端,便于客户端能够及时地进行处理,而针对代码导致的异常,我们一般有两种处理方式,一种是throws直接抛出,一种是使用try..catch捕获,一般的话,如果逻辑的异常,需要知道异常信息,我们往往选择将异常抛出,如果只是要保证程序在出错的情况下 依然可以继续运行,则使用try..catch来捕获. 但是try..catch会导致代码量的增加,让后期我们的代码变得臃肿且难以维护.当

  • Spring Boot处理全局统一异常的两种方法与区别

    前言 在后端发生异常或者是请求出错时,前端通常显示如下 Whitelabel Error Page This application has no explicit mapping for /error, so you are seeing this as a fallback. Fri Jun 07 15:38:07 CST 2019 There was an unexpected error (type=Not Found, status=404). No message available

  • springBoot2.X配置全局捕获异常的操作

    springBoot2.X配置全局捕获异常 先来看一段代码:当传入的id是0的时候,就会报异常. @RestController public class HelloController { @GetMapping("/getUser") public String getUser(int id) { int j = 1 / id; return "SUCCESS" + j; } } 访问时: 我们知道这个页面要是给用户看到,用户可能不知道这是什么. 方法一:将异常

  • Vant 中的Toast设置全局的延迟时间操作

    在引入Toast的配置文件里面配置如下: import { Toast } from 'vant'; Vue.use(Toast); Toast.setDefaultOptions({ duration: 800 }); // duration延迟时间 --完. 补充知识:vant ui库 toast 的使用及封装 最近在写一个项目,表单项较多,那必然前端做验证也是必须的了,一个一个写太繁琐,封装起来直接调用即可: 1.新建 toast.js import { Toast } from 'van

  • Spring下Filter过滤器配置全局异常处理的详细步骤

    Spring下Filter过滤器配置全局异常处理 Filter中出现的异常,spring的全局异常处理器是无法捕获的,所以filter拦截器中出现的异常会直接的抛向浏览器,在浏览器中显示500错误. 而我当前的项目中,是在Filter中判断用户是否有携带Token访问,如果没有,则抛出异常,让其做登录操作.而且异常信息要处理成json格式返回给前端.这就很尴尬了. 好了废话说多了,上解决方案: 结局方案: Filter拦截器中直接抛出异常信息 @Component public class Ad

  • mysql 5.7.20常用下载、安装和配置方法及简单操作技巧(解压版免安装)

    话说凌晨刚折腾完一台MySQL 5.7.19版本的安装,未曾料到早上MySQL官方就发布了最新的5.7.20版本.这个版本看似更新不多,但是加入了一个我们所急需的功能. MySQL 5.7.20版本新增了参数group-replication-member-weight,用来表示选主时服务器的优先级.若没有这个优先级,则之前版本的MGR会选择一个或许不是用户想要的节点,这是一个令人头疼的问题.相信5.7.20版本新增的该参数能解决一些用户的痛点. 1. 下载: mysql-5.7.20是解压版免

  • ASP.NET Core MVC 配置全局路由前缀

    ASP.NET Core MVC 配置全局路由前缀 前言 大家好,今天给大家介绍一个 ASP.NET Core MVC 的一个新特性,给全局路由添加统一前缀.严格说其实不算是新特性,不过是Core MVC特有的. 应用背景 不知道大家在做 Web Api 应用程序的时候,有没有遇到过这种场景,就是所有的接口都是以 /api 开头的,也就是我们的api 接口请求地址是像这样的: http://www.example.com/api/order/333 或者是这样的需求 http://www.exa

  • Struts2学习笔记(9)-Result配置全局结果集

    Result可以设定全局结果集,如: <struts> <constant name="struts.devMode" value="true" /> <package name="user" namespace="/user" extends="struts-default"> <global-results> <result name="m

  • Linux Centos7.2下安装nodejs&npm配置全局路径的教程

    首先登陆官网:https://nodejs.org/en/ 安装之前检测是否装了 gcc gcc-c++ python 2.7+ 没有的话yum install 下载完成之后上传到Linux: rz 没有rz sz的话安装一下(yum -y install lrzsz) 然后依次执行: cd /usr/local mkdir nodejs6 cd nodejs6 rz ------ 选择nodejs文件 tar xzvf node-v6.11.0.tar.gz cd node-v6.11.0 .

  • Laravel配置全局公共函数的方法步骤

    前言 在laravel项目开发中,经常使用到公共函数,那如何在laravel配置全局公共函数呢??下面话不多说了,来一起看看详细的介绍吧 方法如下 在Laravel项目中我们常常需要定义一些全局的公共函数,通常我们会将这些公共函数定义在一个单独的文件里,如helpers.php中.我们在app目录下创建一个名为helpers.php的文件(app/helpers.php),并编辑其内容如下: /** * 字符串两次md5加密 * @param $str 要加密的字符串 */ function d

  • vue-cli配置全局sass、less变量的方法

    一.全局配置less 1.下载插件 **vue add style-resources-loader** vue add pluginName 是vue-cli3提供的.vue add 是用yarn安装插件的, yarn源的问题有可能导致失败.如果上面安装失败的话,就分别安装 style-resources-loader 和 vue-cli-plugin-style-resources-loader(前提是已经安装过 less less-loader) // 没有出错的话,可以无视这里 npm

  • asp.net core webapi项目配置全局路由的方法示例

    一.前言 在开发项目的过程中,我新创建了一个controller,发现vs会给我们直接在controller头添加前缀,比如[Route("api/[controller]")],即在访问接口的时候会变成http://localhost:8000/api/values,但是如果控制器有很多个,或者要进行版本迭代时,我们会发现痛苦的时刻降临了,要一个一个的修改. 如果在这个时候可以进行全局配置前缀那真是福利呀,修改一处即可.为了能达到此目的我们就来运用一下吧. 二.配置 0.在配置前我们

随机推荐