springboot全局异常统一处理反参标准化

xxbbtt      2022-04-03     786

关键词:

对于日常的开发过程中出现的异常,我把它分为两种,

一种是需要给前端返回的异常,这种异常通常有入参格式、字段缺少、以及相关的业务异常,需要明确的告诉前端出现了什么问题,前端才好处理,

而另一种异常例如空指针、连接超时、io异常,这类型的异常不需要前端知晓,统一返回服务器异常即可。

所以我们需要捕获异常,对异常进行分类,然后再将封装成固定的格式返回给前端。

首先第一步个自定义一个ExceptionMap,这里其实啥也没实现就是改了个名字,这样在代码的可读性上能增加不少(我觉得)。

其实不做这一步直接用HashMap也行。我们都知道异常的抛出是冒泡的形式抛出的,现在要做的就是捕获,获取异常的内容,ExceptionMap就是用来异常被捕获后将异常的信息转化成一个map,后续再进行格式化和返回

/**
 * 自定义HashMap的ExceptionMap controller抛出的异常被处理成ExceptionMap
 * @author xuwang
 * @date 2019年5月29日 15:04:49
 */
public class XcCuisineExceptionMap extends HashMap {
}

 

第二步需要继承Exception实现一个新的业务Exception类,

这样做有两个用处,一个是可以自定义异常的内容,一个是可以和其他异常区分出来

/**
 * @ClassName: XcCuisineBusinessException
 * @ClassNameExplain:
 * @Description:
 * @author xuwang
 * @date 2019年05月31日 10:34:00
 *
 */
public class XcCuisineBusinessException extends Exception {
    private static final long serialVersionUID = 1;
    private int code;
    private String errorMsg;

    public XcCuisineBusinessException(int code, String errorMsg) {
        super(errorMsg);
        this.code = code;
        this.errorMsg = errorMsg;
    }


    public XcCuisineBusinessException(int code, String errorMsg, Throwable throwable) {
        super(errorMsg, throwable);
        this.code = code;
        this.errorMsg = errorMsg;
    }

    public int getCode() {
        return code;
    }

    public String getErrorMsg() {
        return errorMsg;
    }
}

 

第三步实现一个ControllerAdvice,这一步的目的主要就是捕获全局异常,所有从Controller抛出的异常都能在这捕获到,

在ExceptionHandler中,根据getClass().getName()区分出业务异常,和服务器异常,将异常变成一个拥有code和message的XcCuisineExceptionMap

这里还额外判断了一个MethodArgumentNotValidException ,MethodArgumentNotValidException是使用@Valid对入参里字段进行限制后,字段不符合规则出现的异常,这种也属于业务异常,但是没法抛出变成XcCuisineBusinessException,就在这判断了一下。

/**
 * @ClassName: XcCuisineControllerAdvice
 * @ClassNameExplain: 
 * @Description: 
 * @author xuwang
 * @date 2019年05月31日 10:34:00
 *
 */
@ControllerAdvice
public class XcCuisineControllerAdvice {
    static final Logger logger = LoggerFactory.getLogger(XcCuisineControllerAdvice.class);

    /**
     * 全局异常捕捉处理
     * @param ex
     * @return
     */
    @ResponseBody
    @ExceptionHandler(value = Exception.class)
    public Map errorHandler(Exception ex) {
        Map map = new XcCuisineExceptionMap();
        if(ex.getClass().getName().equals(XcCuisineBusinessException.class.getName())){
            XcCuisineBusinessException bex = (XcCuisineBusinessException) ex;
            map.put("code", bex.getCode());
            map.put("msg", bex.getErrorMsg());
        }else if(ex.getClass().getName().equals(MethodArgumentNotValidException.class.getName())){
            MethodArgumentNotValidException mex = (MethodArgumentNotValidException) ex;
            StringBuffer sb = new StringBuffer();
            List<FieldError> errorList = mex.getBindingResult().getFieldErrors();
            for (FieldError error : errorList) {
                sb.append(error.getObjectName());
                sb.append("对象的");
                sb.append(error.getField());
                sb.append("字段");
                sb.append(translationString(error.getDefaultMessage()));
            }
            map.put("code",ExceptionConstants.PARAM_INVALID_CODE);
            map.put("msg", sb.toString());
        }else {
            map.put("code", ExceptionConstants.SERVER_EXCEPTION_CODE);
            map.put("msg", ExceptionConstants.SERVER_EXCEPTION_MSG);
        }
        return map;
    }

    /**
     * @Title: translationString
     * @TitleExplain:
     * @Description: 对字符串进行转译 解决报错中出现json关键字符  导致json序列化失败的问题
     * @param
     * @return java.lang.String
     * @version
     * @author 
     */
    private String translationString(String string){
        String temp = GsonUtil.toJson(string);
        return temp.substring(1, temp.length()-1);
    }


}

 第四步是在创建转化器,我的这个转换器其实对入参和反参都进行了转换,因为在我的项目里入参也是有标准的,不过这里没写太多,

在writeInternal中,反参如果是XcCuisineExceptionMap,直接就转换成JSON字符串返回,这里还在正常返回的内容中添加了code和message,做到了反参的标准化{code:"",msg:"",data:{}}

/**
 * @author xuwang
 * @ClassName: GsonHttpMessageConverter
 * @ClassNameExplain: Gson转换器
 * @Description: 
 * @date 2019年05月30日 20:13:04
 */
public class GsonHttpMessageConverter extends AbstractHttpMessageConverter {

    private final static String CODE_KEY = "code";

    private final static String MSG_KEY = "msg";

    private final static String DATA_KEY = "data";

    private final static String CONTENT_TYPE_KEY = "Content-Type";

    public GsonHttpMessageConverter() {
        super(new MediaType("application", "json", Charset.forName("utf-8")));
    }

    @Override
    protected boolean supports(Class aClass) {
        //返回true表示支持所有的类
        return true;
    }

    /**
     * 处理请求内容
     * @param aClass
     * @param httpInputMessage
     * @return
     * @throws IOException
     * @throws HttpMessageNotReadableException
     */
    @Override
    protected Object readInternal(Class aClass, HttpInputMessage httpInputMessage) throws IOException, HttpMessageNotReadableException {
            //取出requestBody中内容
            String json = StreamUtils.copyToString(
                    httpInputMessage.getBody(), httpInputMessage.getHeaders().getContentType().getCharset());

            //打印入参
            logger.debug(aClass+" request json : 
" + json);

            if(StringUtils.isNotEmpty(json) && json.trim().startsWith("{") && json.trim().endsWith("}")){
                return GsonUtil.json2Bean(json, aClass);
            }else{
                //TODO  throw new RequestFormatException("请求格式不正确");
                return json;
            }
    }

    /**
     * 处理响应内容
     * @param o
     * @param httpOutputMessage
     * @throws IOException
     * @throws HttpMessageNotWritableException
     */
    @Override
    protected void writeInternal(Object o, HttpOutputMessage httpOutputMessage) throws IOException, HttpMessageNotWritableException {
        Class oClass = o.getClass();
        String json = "";
        if(oClass.getName().equals(XcCuisineExceptionMap.class.getName())){
            //判断如果是抛出来的异常直接转换为json字符串
            json = GsonUtil.toJson(o);
        }else {
            Map<String, Object> result = new LinkedHashMap<>();
            //设置code
            result.put(CODE_KEY, Constant.CORRECT_CODE);
            //设置msg
            result.put(MSG_KEY, Constant.CORRECT_MSG);
            //设置data
            result.put(DATA_KEY, o == null ? "" : o);
            json = GsonUtil.toJson(result);
        };

        logger.debug("response json : 
" + json);

        httpOutputMessage.getHeaders().add(CONTENT_TYPE_KEY, "application/json");
        httpOutputMessage.getBody().write(json.getBytes("UTF-8"));

    }
}

最后是注册这个转换器

@EnableWebMvc
@Configuration
public class MvcConfig implements WebMvcConfigurer {

    @Override
    public void configureMessageConverters(
            List<HttpMessageConverter<?>> converters) {
        //清理其他转换器,添加自定义转换器
        converters.clear();
        converters.add(createGsonHttpMessageConverter());
    }

    @Bean
    public GsonHttpMessageConverter createGsonHttpMessageConverter() {
        //注入自定义转换器
        return new GsonHttpMessageConverter();
    }
}

到此就完成了。

 

springboot2全局统一返回restful风格数据统一异常处理

...vice拦截异常并统一处理。开发环境:IntelliJIDEA2019.2.2jdk1.8SpringBoot2.2.21、创建一个SpringBoot项目,pom.xml引用的依赖包如下<dependency><gr 查看详情

springboot2系列教程(十四)|统一异常处理

如题,今天介绍SpringBoot是如何统一处理全局异常的。SpringBoot中的全局异常处理主要起作用的两个注解是@ControllerAdvice和@ExceptionHandler,其中@ControllerAdvice是组件注解,添加了这个注解的类能够拦截Controller的请求,而ExceptionHandler... 查看详情

重学springboot系列之统一全局异常处理(代码片段)

重学SpringBoot系列之统一全局异常处理设计一个优秀的异常处理机制异常处理的乱象例举该如何设计异常处理开发规范自定义异常和相关数据结构该如何设计数据结构枚举异常的类型自定义异常请求接口统一响应数据结构使用示... 查看详情

springmvc,springboot统一校验自定义异常全局异常处理(代码片段)

引入jar包<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency>写一个DTO用来接收客户端传送的参数@Datapubliccl 查看详情

springmvc,springboot统一校验自定义异常全局异常处理(代码片段)

引入jar包<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency>写一个DTO用来接收客户端传送的参数@Datapubliccl 查看详情

springboot全局异常统一处理(代码片段)

...ice&@ExceptionHandlerhttp异常ErrorControllerServlet异常Filter1.SpringBoot默认错误统一处理机制在基于SpringBoot的Web应用中,对于Http请求处理过程中发生的各种错误,如常见的400、404和500等错误,SpringBoot默认提供了一种映射... 查看详情

springboot中全局异常处理器

  SpringBoot中全局异常处理器,就是把错误异常统一处理的方法。等价于Springmvc中的异常处理器。步骤一:基于前面的springBoot入门小demo修改步骤二:修改HelloController类  修改HelloController,使得访问/hello一定会产生异常:someexce... 查看详情

springboot学习笔记:全局异常处理(代码片段)

SpringBoot可以使用@ControllerAdvice注解进行全局异常的处理,这样可以方便统一对异常的处理返回1.处理常见异常首先我们需要确定一个异常处理统一返回的格式@DatapublicclassBaseResult/***返回code,成功为0,失败为-1*/privateSt... 查看详情

springboot定义统一的返回异常提示数据格式(代码片段)

一描述1.1没有加全局异常处理1.这里设置一个字符串为空指针异常,然后看看返回给前端的信息。 2.返回结果 3.效果看起来不友好的提示1.2 添加全局异常处理1.代码:添加一个全局异常处理类@ControllerAdvicepublicclassGlob... 查看详情

springboot返回统一的json标准格式(代码片段)

自定义状态码枚举类封装返回结果全局异常捕获处理,使用@RestControllerAdvice注解拦截Controller方法的返回值,统一处理返回值/响应体创建Controller,准备测试请求接口,查看响应结果近年来,随着移动互联网... 查看详情

springboot返回统一的json标准格式(代码片段)

自定义状态码枚举类封装返回结果全局异常捕获处理,使用@RestControllerAdvice注解拦截Controller方法的返回值,统一处理返回值/响应体创建Controller,准备测试请求接口,查看响应结果近年来,随着移动互联网... 查看详情

springboot2.x:全局异常处理

前言在JavaWeb系统开发中,不管是Controller层、Service层还是Dao层,都有可能抛出异常。如果在每个方法中加上各种trycatch的异常处理代码,那样会使代码非常繁琐。在SpringMVC中,我们可以将所有类型的异常处理从各个单独的方法中... 查看详情

spring--springboot配置全局异常处理器

一、为什么要使用全局异常处理器?什么是全局异常处理器?就是把错误异常统一处理的方法。应用场景:1、当你使用jsr303参数校验器,如果参数校验不通过会抛异常,而且无法使用try-catch语句直接捕获,这时可以使用全局异... 查看详情

springboot中web应用的统一异常处理

...用的时候,请求处理过程中发生错误是非常常见的情况。SpringBoot提供了一个默认的映射:/error,当处理中抛出异常之后,会转到该请求中处理,并且该请求有一个全局的错误页面用来展示异常内容。选择一个之前实现过的Web应... 查看详情

springboot中添加全局异常捕获类

前几天工作中遇到一个项目,前后端分离的,作为后端开发专注开发接口。对于程序中出现的异常如果不进行处理,将报错信息直接返回到前端十=时不优雅的,因此需要对异常进行捕获和处理,但是每个接口都单独处理异常则... 查看详情

springboot全局异常可以获取方法名吗

参考技术Aspringboot全局异常可以获取方法名。方法名可以任意定,方法内是对于该异常的统一处理方法。如果想处理其它异常,重新定义一个value就行。value的值可以为一个,也可以为多个异常类型,可以指的很细,也可以指的很宽,比如... 查看详情

springcloudgateway全局通用异常处理

为什么需要全局异常处理在传统SpringBoot应用中,我们@ControllerAdvice来处理全局的异常,进行统一包装返回@ControllerAdvicepublicclassConsoleExceptionHandler{@ExceptionHandler(AccessException.class)privateResponseEntity<String>handleAcc 查看详情

springcloudgateway全局通用异常处理

为什么需要全局异常处理在传统SpringBoot应用中,我们@ControllerAdvice来处理全局的异常,进行统一包装返回@ControllerAdvicepublicclassConsoleExceptionHandler{@ExceptionHandler(AccessException.class)privateResponseEntity<String>handleAcc 查看详情