• 前后端分离异常统一处理


    作为一个程序员,虽然技术不厉害,但是都有一个向上的心,原来一直负责业务开发,梳理业务、设计流程、开发代码,最近开始接触一些架构类的设计和思路

    全国医改在即,项目组接了一个医疗改革相关的供应商项目,主要是针对物流方向的。

    技术架构:领导要求用springcloud但是新来的成员都没有相关开发经验,项目负责人直接在网上扒了一套微服务的代码(个人感觉不太成熟),好多东西不太全,

    所以需要重新整理,由于项目负责人比较忙,自己有迫切的想要了解一些东西,所以针对架构层次的代码学习了一下,

    自己原来只做业务,从不关心架构的设计是否合理优美,甚至在业务方向已经做到管理层次,后来跳槽离职后才发现自己技术的短板,所以一直在补充学习

    其实只需要架构师搭建微服务架构即可,项目组成员还是针对不同的功能模块进行业务开发,和原来的SSM没有本质区别,只是增加了一些新的注解,和组件的使用

    这里就不对微服务的组件进行介绍了,只是针对自己部署的异常处理进行记录——只是个人理解,如有好的设计欢迎指正

    问题描述

    技术架构是前后端分离,想着统一返处理回值(包括正常结果和异常结果),但是架构代码只是做了一个自己的返回类Map类型,如下

    package com.bootdo.clouddocommon.utils;
    
    import java.util.HashMap;
    import java.util.Map;
    
    public class R extends HashMap<String, Object> {
        private static final long serialVersionUID = 1L;
        public R() {
            put("code", 0);
            put("msg", "操作成功");
        }
        public static R error() {
            return error(500, "操作失败");
        }
        public static R operate(boolean b){
            if(b){
                return R.ok();
            }
            return R.error();
        }
        public static R error(String msg) {
            return error(500, msg);
        }
        public static R error(int code, String msg) {
            R r = new R();
            r.put("code", code);
            r.put("msg", msg);
            return r;
        }
        public static R ok(String msg) {
            R r = new R();
            r.put("msg", msg);
            return r;
        }
        public static R ok(Map<String, Object> map) {
            R r = new R();
            r.putAll(map);
            return r;
        }
        public static R ok() {
            return new R();
        }
        public static R error401() {
            return error(401, "你还没有登录");
        }
        public static R error403() {
            return error(403, "你没有访问权限");
        }
        public static R data(Object data){
            return R.ok().put("data",data);
        }
        public static R page(Object page){
            return R.ok().put("page",page);
        }
        @Override
        public R put(String key, Object value) {
            super.put(key, value);
            return this;
        }
    }

    这种这种类封装方式很好的处理的所有Controller类的返回结果 例如;

        @Log("停用记录")
        @ApiOperation(value = "停用信息记录")
        @PutMapping("/stop")
        R stop(@RequestBody List<Long> idList) {
    
            return R.operate(consumeMaterialService.stop(idList,getUser()) > 0);
        }
      @Log("获取耗材信息列表(无分页)")
        @ApiOperation(value = "获取耗材信息列表(无分页)")
        @ApiImplicitParam(name = "params", value = "map参数集合(基础的参数格式——可在结构内增加其他过滤条件字段)", example = "{consumeName:10}")
        @GetMapping("/allList")
        R list(@RequestParam Map<String, Object> params) {
            List<ConsumeMaterialDO> list = consumeMaterialService.list(params);
            return R.data(list);
    
        }

    这种方式特点

    1、很好的统一了Controller层的返回结果

    2、没有一套错误编码和对应的信息,需要硬编码的形式在代码提现,例如:(该问题可以通过增加一个错误信息的枚举类来解决——下文中会说明)

    R findMaterialBySupplierIdNotAddList(@RequestParam Map<String, Object> params){
            if(params.isEmpty()){
                return R.error("30032",”"医院ID和供应商ID不能为空");//可以只写错误信息,也可以加上编码
            }else{
                Long id=new Long(params.get("hospitalId").toString());
                if(id==null||id==0){
                    return R.error("医院ID不能为空");
                }
                Long sid=new Long(params.get("supplierId").toString());
                if(sid==null||sid==0){
                    return R.error("供应商ID不能为空");
                }
            }
            //查询列表数据
            Query query = new Query(params);
            List<ConsumeMaterialDO> materialList = supplierService.findMaterialBySupplierIdNotAddList(query);
            int total = supplierService.findMaterialBySupplierIdNotAddCount(query);
            PageUtils pageUtils = new PageUtils(materialList, total);
            //返回状态字典
            List<SysDictDO> statusList = initDataService.getSysDictByType("status");
            //返回耗材状态
            List<SysDictDO> measurementStatusList = initDataService.getSysDictByType("unit");
            return R.ok().put("page",pageUtils).put("suppliermaterialstatusoptions",statusList).put("measurementstatusoptions",measurementStatusList);
        }

    3、没有办法处理service层的异常返回,因为service 层返回的都是具体的实体或者整数

    因为原来做过的项目中有过相关的异常处理——记得是定义一个统一的异常接收类对返回值结构进行处理,其他地方直接抛出异常即可,当然需要定义一套错误信息枚举类,用于统一返回信息内容

    随意根据自己的理解和经验,查找相关代码做了一套异常处理的代码,代码和原理很简单

    参考链接:https://segmentfault.com/a/1190000016576329

    1、首先在原来的基础上增加一个错误信息的枚举类——代码比较多删减了一部分,可以根据自己的需求添加

    package com.bootdo.clouddocommon.constants;
    
    /**
     * 异常处理状态码
     */
    public enum ResultCode {
    
        SUCCESS(0, "请求成功"),
        Unknown_Exception(-1, "未知异常"),
    
        USER_NOT_FOUND(10001, "没有找到此用户"),
        USERNAME_NOT_BLANK(10002, "用户名不能为空"),
        USERNAME_EXIST(10003, "用户名已经存在"),
        USERTYPE_ERROR(100031, "用户类型不正确"),   
        
        DEVICE_ID_EMPTY(10052,"设备ID:deviceId不能为空"),
        DELETE_CONNECT_ERROR(10053,"删除connect出错");
    
        private int code;
        private String message;
    
        ResultCode(int code, String message) {
            this.code = code;
            this.message = message;
        }
    
        public int getCode() {
            return code;
        }
    
        public String getMessage() {
            return message;
        }
    }

    2、添加一个自定义的异常类用于抛出:主要是将自己的错误编码和信息整合进去——原生的异常类也是可以的,只是好像没有自定的编码返回(自定义的异常类也需要集成原生的异常类——红色部分——有兴趣的同学可以看一下源码)

    package com.bootdo.clouddoadmin.config;
    
    import com.bootdo.clouddocommon.constants.ResultCode;
    /**
     * 功能描述: <br>
     * 〈异常类封装——定义抛出的异常类〉
    
     * @return:
     * @since: 1.0.0
     * @Author:
     * @Date:
     */
    public class DomainException extends RuntimeException {
        private int errCode = ResultCode.Unknown_Exception.getCode();
    
        public DomainException() {
            super(ResultCode.Unknown_Exception.getMessage());
        }
    
        public DomainException(ResultCode resultCode) {
            super(resultCode.getMessage());
            this.errCode = resultCode.getCode();
        }
    
        public int getErrCode() {
            return errCode;
        }
    
        public void setErrCode(int errCode) {
            this.errCode = errCode;
        }
    }

    3、添加统一的异常返回处理——RestControllerAdvice(ControllerAdvice)拦截异常并统一处理

    package com.bootdo.clouddoadmin.config;
    
    import com.bootdo.clouddocommon.utils.R;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.bind.annotation.RestControllerAdvice;
    
    import javax.servlet.http.HttpServletRequest;
    /**
     * 功能描述: <br>
     * 〈统一异常处理返回〉
    
     * @return:
     * @since: 1.0.0
     * @Author:
     * @Date:
     */
    @RestControllerAdvice
    public class GlobalExceptionHandler {
    
        @ExceptionHandler(value = DomainException.class)
        public R domainExceptionHandler(HttpServletRequest req,
                                        DomainException e) throws Exception {
            e.printStackTrace();
            return   R.error(e.getErrCode(), e.getMessage());
        }
    }

    这样整体就设计完了,无论是在service或者controller中都可以抛出异常,然后经过处理成为统一格式的返回值

    例如:

    throw new DomainException(ResultCode.CODE_EMPTY);

     经测试是有效的——结果截图就不再展示了

  • 相关阅读:
    TensorboardX的使用【有手就⭐系列】
    Python学习记录
    es 之 自定义 mapping(五)
    es 索引和文档 API (四)
    布尔查询(三)
    term 和 match 查询(二)
    使用 Python 批量将数据插入到 ES中
    【flask + vue 前后端分离博客】设计 User 用户(三)
    【flask + vue 前后端分离博客】使用 axios 访问接口(二)
    【flask + vue 前后端分离博客】创建第一个 Flask RESTFul(一)
  • 原文地址:https://www.cnblogs.com/yutf/p/11493454.html
Copyright © 2020-2023  润新知