SpringBoot+Maven+国际化文件+拦截器实现全局异常处理(异常处理以请求参数校验举例)

2 篇文章 0 订阅
1 篇文章 0 订阅
本文介绍了如何在SpringBoot应用中实现全局异常处理,包括自定义异常类、异常拦截器,并结合国际化配置进行错误信息展示。详细讲解了配置国际化文件、创建MessageSource工具类以及异常处理器的实现过程。
摘要由CSDN通过智能技术生成

前言:

本文概述如何实现,如果有技术点不懂之处,自行查阅资料证明。SpringParent版本2.3.4.RELEASE

准备工作:

依赖

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--请求参数快捷校验-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
  1. resource目录下创建i18n文件夹,再创建messages_zh_CN.properties文件,做为异常信息配置文件,因为不需要识别国际化语言,所以直接使用中文文件,如图:

在这里插入图片描述

  1. 创建配置类,把国际化文件操作对象设置参数注入到容器
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;

@Configuration
public class MessageConfig {

    @Bean
    public MessageSource messageSource() {
        ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
        /*指定文件路径及名称:
        messages.properties(默认文件,非中、英文时读取)
        messages_en.properties(英文)
        messages_zh.properties(中文)*/
        messageSource.setBasename("classpath:i18n/messages");
        /*设置字符集,IDEA需要在 File | Settings | Editor | File Encodings中设置字符集为 UTF-8*/
        messageSource.setDefaultEncoding("UTF-8");
        /*每小时刷新一次缓存*/
        messageSource.setCacheSeconds(3600);
        return messageSource;
    }


}

在这里插入图片描述

  1. 构建MessageSource操作工具类
@Component
public class MessageSourceUtils {

    /**
     * 指定的消息不存在时返回的消息。
     */
    private static final String MSG_NOT_EXIST = "消息不存在";


    @Resource
    private MessageSource messageSource;

    private MessageSourceAccessor accessor;


    @PostConstruct
    private void init() {
        accessor = new MessageSourceAccessor(messageSource, Locale.CHINA);
    }

    /*根据code获取信息*/
    public String get(String code) {
        return accessor.getMessage(code);
    }

    /*根据code获取动态信息*/
    public String getMsg(String code,String... args) {
        String text = null;
        /*根据CODE获取消息*/
//        String message = messageSource.getMessage(code, args, Locale.CHINA);
        /*取得默认消息内容*/
        try {
            text = get(code);
        } catch (NoSuchMessageException e) {
//            TODO 日志打印
            System.out.println("错误");
        }
        /*参数动静结合,获取最终的参数结果*/
        text = text == null ? MessageFormat.format(MSG_NOT_EXIST, null) : MessageFormat.format(text, args);
        return text;
    }
 }
 
  1. 创建自定义全局异常类
@Getter
public class ManageException extends RuntimeException{

    /*
     * 错误代码
     */
    private String failCode;

    /*
     * 错误信息
     */
    private String[] failMsg;

    public ManageException(String code, String... msg){
        this.failCode = code;
        this.failMsg = msg;
    }

}
  1. 创建自定义拦截器类,@RestControllerAdvice+@ExceptionHandler(要拦截的异常类)组合实现全局异常拦截,不懂注解的自行百度
import com.infinity.honghu.exception.model.ManageException;
import com.infinity.honghu.model.response.RespResult;
import com.infinity.honghu.utils.MessageSourceUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.annotation.Resource;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@RestControllerAdvice
public class GlobalControllerAdvice {

    @Resource
    private MessageSourceUtils msgUtils;


    /**
     * @Author HongHu
     * @Description manage模块的异常
     */
    @ExceptionHandler(ManageException.class)
    public RespResult manageExceptionHandler(ManageException m){
        String msgCode = m.getFailCode();
        String msgInfo = msgUtils.getMsg(msgCode,m.getFailMsg());
        return RespResult.error(msgCode,msgInfo);
    }


    /**
     * @Author HongHu
     * @Description 处理绑定异常,bean中有字段验证,Validated Valid 注解指定要验证这个bean对象,当前端传过来一个表单格式
     * (Content-Type: multipart/form-data)的数据,后台通过需要验证的bean对象接收的时候,加入验证不通过, 则会报此异常
     * 处理form data方式调用接口校验失败抛出的异常
     */
    @ExceptionHandler(BindException.class)
    public RespResult bindExceptionHandler(BindException e) {
        List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
        List<String> collect = fieldErrors.stream()
                .map(o -> o.getDefaultMessage())
                .collect(Collectors.toList());
        return new RespResult().success(collect);
    }

    /**
     * @Author HongHu
     * @Description 请求体绑定异常,与BindException类似,不同的是因为什么触发,当Controller接收的是一个json格式,@RequestBody接收参数时,
     * 验证失败会抛出此异常,处理 json 请求体调用接口校验失败抛出的异常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public RespResult methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
        List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
        List<String> collect = fieldErrors.stream()
                .map(o -> o.getDefaultMessage())
                .collect(Collectors.toList());
        return new RespResult().success(collect);
    }

    /**
     * @Author HongHu
     * @Description Controller中的参数校验失败会抛出此类异常,类头部需要添加@Valited注解,处理单个参数校验失败抛出的异常
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public RespResult constraintViolationExceptionHandler(ConstraintViolationException e) {
        Set<ConstraintViolation<?>> constraintViolations = e.getConstraintViolations();
        List<String> collect = constraintViolations.stream()
                .map(o -> o.getMessage())
                .collect(Collectors.toList());
        return new RespResult().success( collect);
    }

    /**
     * @Author HongHu
     * @Description 处理程序员在程序中未能捕获(遗漏的)异常
     */
    @ExceptionHandler(Exception.class)
    public RespResult exception(Exception e) {
        return RespResult.error("99999",e.getStackTrace().toString());
    }

  1. 创建请求模型
import lombok.Data;

import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;

@Data
public class ReqParam {

    @NotBlank(message = "name不能为空")
    private String name;

    @NotBlank(message = "age不能为空")
    @Min(value=5,message = "age不能小于5")
    private String age;

}
  1. 响应模型

@Data
public class RespResult<T> implements Serializable {

    private String code;
    private String msg;
    private T data;

    /* 成功且返回体有数据 */
    public static RespResult success(Object data) {
        RespResult retSuc = new RespResult();
        retSuc.setCode(RespEnum.SUCCESS.getCode());
        retSuc.setMsg(RespEnum.SUCCESS.getMsg());
        retSuc.setData(data);
        return retSuc;
    }

    /*成功,但返回体没数据*/
    public static  RespResult success(){
        return success(null);
    }

    /*失败返回信息*/
    public static RespResult error(String code,String msg){
        RespResult retEro = new RespResult();
        retEro.setCode(code);
        retEro.setMsg(msg);
        return retEro;
    }


}

使用

@RestController
public class TestController {

    @Resource
    private ApplicationContext applicationContext;


    /*测试国际化文件动静参数结合模式抛出全局异常*/
    @RequestMapping("/globalError")
    public RespResult globalError(){
        throw new ManageException("123456","傻子","天才");
    }
    
    /*Validated校验抛出异常*/
    @RequestMapping("/globalErrorVo")
    public RespResult globalErrorVo(@RequestBody @Validated ReqParam reqParam)  {
        return new RespResult();
    }

{0} 代表第一个参数, {1}代表第二个,可以有N个
在这里插入图片描述

测试国际化文件动静参数结合模式抛出全局异常 测试场景如图:
在这里插入图片描述
Validated校验抛出异常 场景如图
在这里插入图片描述

结束总结

如果你只复制粘贴运气好可能执行成功了,但最靠谱的就是在复制完了,脑袋过一遍代码,哪里不明白研究一下,至少出现一些基础的错误能够看懂,这样会少走很多弯路。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值