zoukankan      html  css  js  c++  java
  • SpringBoot全局异常统一处理、反参标准化

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

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

    而另一种异常例如空指针、连接超时、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();
        }
    }

    到此就完成了。

  • 相关阅读:
    手机端调用摄像头拍照
    判断浏览器是否支持css3属性或单位
    浏览器页面加载解析渲染机制(一)
    css默认值汇总
    分享几个高效编写JS 的心得
    说几个JS优化技巧吧
    yahoo的30条优化规则
    Jquery的$命名冲突
    C语言时间头文件
    C语言随机数的生成
  • 原文地址:https://www.cnblogs.com/xxbbtt/p/12878102.html
Copyright © 2011-2022 走看看