开发日记8-优化接口使其更规范

痛点1

query接口有时候需要传入多个参数,是不是意味着query就一定要在传参增加很多个呢?

不是,你只需传入对象

痛点1解决方案

1 定义对象

java 复制代码
package com.wz.nls.business.req;

import lombok.Data;

@Data
public class DemoQueryReq {
    private String mobile;
}

2 修改接口

java 复制代码
    public List<Demo> query(DemoQueryReq req) {
        String mobile = req.getMobile();
        DemoExample example = new DemoExample();
        example.setOrderByClause("id desc");
        DemoExample.Criteria criteria = example.createCriteria();
        if (mobile != null) {
            criteria.andMobileNameEqualTo(mobile);
        }
        return demoMapper.selectByExample(example);
    }

3 测试

java 复制代码
GET http://localhost:18000/nls/query?mobile=Huawei
Accept: application/json

依然可以查到结果。

痛点2

返回的结果,类型五花八门,也不好统一维护

痛点2解决方案

在/data/wz/JavaProject/im-nls/business/src/main/java/com/wz/nls/business/resp/CommonResp.java下

java 复制代码
package com.wz.nls.business.resp;

import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
public class CommonResp<T> {
    /**
     * 业务成功或失败
     */
    private boolean success=true;
    /**
     * 返回信息
     */
    private String message;
    /**
     * 返回泛型数据
     */
    private T cotent;
    public CommonResp(T cotent) {
        this.cotent = cotent;
    }
}

注意这里是使用泛型,而不是object

  • 泛型解决了 Object 的核心痛点 :编译期类型安全校验,杜绝ClassCastException运行时异常;
  • 泛型让代码更简洁:无需手动强制类型转换,减少冗余代码;
  • 泛型让代码更易读:类型语义清晰,团队协作更高效;
  • 泛型不丢失复用性:和 Object 一样适配任意类型,同时兼顾安全。

那么现在,接口查询获得的结果就要用它来包装

java 复制代码
            <dependency>
                <groupId>cn.hutool</groupId>
                <artifactId>hutool-all</artifactId>
                <version>5.8.26</version>
            </dependency>

或者,更简单的

java 复制代码
    @GetMapping("/query")
    public CommonResp<List<Demo>> query(DemoQueryReq req) {
        return new CommonResp<>(demoService.query(req));
    }

测试看看,现在很接近前端获取的数据了

痛点3

现在query查到的实体就是表结构,这样其实不太安全,也不具备拓展性,我们有时候需要增减属性

痛点3解决方案

首先在pom中引入依赖,最外层父pom,添加

java 复制代码
            <dependency>
                <groupId>cn.hutool</groupId>
                <artifactId>hutool-all</artifactId>
                <version>5.8.26</version>
            </dependency>

然后在business的子pom,添加

java 复制代码
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
        </dependency>

然后更新maven

然后在/data/wz/JavaProject/im-nls/business/src/main/java/com/wz/nls/business/resp/DemoQueryResp.java中引入

java 复制代码
package com.wz.nls.business.resp;

import lombok.Data;

@Data
public class DemoQueryResp {
    private Long id;
    private String mobileName;
}

这里可以直接屏蔽掉password

同步需改接口的类型,这里只有引入了hutool才能用上BeanUtil.copyToList

java 复制代码
    public List<DemoQueryResp> query(DemoQueryReq req) {
        String mobile = req.getMobile();
        DemoExample example = new DemoExample();
        example.setOrderByClause("id desc");
        DemoExample.Criteria criteria = example.createCriteria();
        if (mobile != null) {
            criteria.andMobileNameEqualTo(mobile);
        }
        return BeanUtil.copyToList(demoMapper.selectByExample(example),  DemoQueryResp.class);
    }
}
java 复制代码
@RestController
public class TestController {
    @Resource
    private DemoService demoService;

    @GetMapping("/count")
    public CommonResp<Long> count() {
        return new CommonResp<>(demoService.count());
    }

    @GetMapping("/query")
    public CommonResp<List<DemoQueryResp>> query(DemoQueryReq req) {
        return new CommonResp<>(demoService.query(req));
    }
}

痛点4

异常太多如何管理

痛点4解决方案

增加统一异常处理

在/data/wz/JavaProject/im-nls/business/src/main/java/com/wz/nls/business/controller/ControllerExceptionHandler.java下

java 复制代码
package com.wz.nls.business.controller;

import com.wz.nls.business.resp.CommonResp;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * 统一异常处理、数据预处理等
 */
@Slf4j
@ControllerAdvice
public class ControllerExceptionHandler {

    /**
     * 所有异常统一处理
     * @param e
     * @return
     */
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public CommonResp<Object> exceptionHandler(Exception e) {
        CommonResp<Object> commonResp = new CommonResp<>();
        log.error("系统异常:", e);
        commonResp.setSuccess(false);
        commonResp.setMessage("系统出现异常,请联系管理员");
        return commonResp;
    }

}

它相当于一个拦截器,所有的异常都会统一在这里收束。

但是这里有个缺点,所有的异常都是统一的回复,即便我们在函数中抛出异常

java 复制代码
    public List<DemoQueryResp> query(DemoQueryReq req) {
        String mobile = req.getMobile();
        DemoExample example = new DemoExample();
        example.setOrderByClause("id desc");
        DemoExample.Criteria criteria = example.createCriteria();
//        if (mobile != null) {
//            criteria.andMobileNameEqualTo(mobile);
//        }
        if (StringUtils.isBlank(mobile)) {
            throw new RuntimeException("手机不能为空");
        }
        criteria.andMobileNameEqualTo(mobile);
        return BeanUtil.copyToList(demoMapper.selectByExample(example),  DemoQueryResp.class);
    }

也无济于事,前端还是

而后端可以看到

这时候,我们可以针对性的根据异常来输出文本,在/data/wz/JavaProject/im-nls/business/src/main/java/com/wz/nls/business/controller/ControllerExceptionHandler.java下

java 复制代码
package com.wz.nls.business.controller;

import com.wz.nls.business.resp.CommonResp;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * 统一异常处理、数据预处理等
 */
@Slf4j
@ControllerAdvice
public class ControllerExceptionHandler {

    /**
     * 所有异常统一处理
     * @param e
     * @return
     */
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public CommonResp<Object> exceptionHandler(Exception e) {
        CommonResp<Object> commonResp = new CommonResp<>();
        log.error("系统异常:", e);
        commonResp.setSuccess(false);
        commonResp.setMessage("系统出现异常,请联系管理员");
        return commonResp;
    }
    /**
     * RuntimeException异常处理
     * @param e
     * @return
     */
    @ExceptionHandler(value = RuntimeException.class)
    @ResponseBody
    public CommonResp<Object> exceptionHandler(RuntimeException e) {
        CommonResp<Object> commonResp = new CommonResp<>();
        log.error("系统异常:", e);
        commonResp.setSuccess(false);
        commonResp.setMessage(e.getMessage());
        return commonResp;
    }
}

通过RuntimeException的handler捕获异常,通过e.getMessage()来获取异常文本信息,并在最上层的接口抛出。

但是以上并不是最优解,以为异常太多了,我们最好自定义一个异常类,并且通过枚举来管理异常的细分种类

首先在/data/wz/JavaProject/im-nls/business/src/main/java/com/wz/nls/business/exception/BusinessExceptionEnum.java中

java 复制代码
package com.wz.nls.business.exception;

import lombok.AllArgsConstructor;
import lombok.Getter;


@AllArgsConstructor
public enum BusinessExceptionEnum {
    DEMO_MOBILE_NOT_NULL("手机号不能为空!");

    @Getter
    private String desc;
}

然后在自定义类中使用它

java 复制代码
package com.wz.nls.business.exception;

import lombok.AllArgsConstructor;
import lombok.Data;


@Data
@AllArgsConstructor
public class BusinessException extends RuntimeException {
    private BusinessExceptionEnum e;
}

在拦截器/data/wz/JavaProject/im-nls/business/src/main/java/com/wz/nls/business/controller/ControllerExceptionHandler.java中这样定义

java 复制代码
    /**
     * BusinessException异常处理
     * @param e
     * @return
     */
    @ExceptionHandler(value = BusinessException.class)
    @ResponseBody
    public CommonResp<Object> exceptionHandler(BusinessException e) {
        CommonResp<Object> commonResp = new CommonResp<>();
        log.error("系统异常:", e);
        commonResp.setSuccess(false);
        commonResp.setMessage(e.getE().getDesc());
        return commonResp;
    }

使用时,我们直接抛出枚举类的desc

java 复制代码
    public List<DemoQueryResp> query(DemoQueryReq req) {
        String mobile = req.getMobile();
        DemoExample example = new DemoExample();
        example.setOrderByClause("id desc");
        DemoExample.Criteria criteria = example.createCriteria();
//        if (mobile != null) {
//            criteria.andMobileNameEqualTo(mobile);
//        }
        if (StringUtils.isBlank(mobile)) {
            throw new BusinessException(BusinessExceptionEnum.DEMO_MOBILE_NOT_NULL);
        }
        criteria.andMobileNameEqualTo(mobile);
        return BeanUtil.copyToList(demoMapper.selectByExample(example),  DemoQueryResp.class);
    }

痛点5

后台运维,看堆栈报错太复杂,如何简约化?

痛点5解决方案

/data/wz/JavaProject/im-nls/business/src/main/java/com/wz/nls/business/exception/BusinessException.java

重写fillInStackTrace方法防止递归,并且重写构造函数,原本使用父类可以传值进去,然后抛出异常的,

java 复制代码
throw new RuntimeException("手机不能为空");

现在在构造函数使用super,然后狸猫换太子~

java 复制代码
package com.wz.nls.business.exception;

import lombok.Data;


@Data
public class BusinessException extends RuntimeException {
    private BusinessExceptionEnum e;
    
    public BusinessException(BusinessExceptionEnum e) {
        super(e.getDesc());
        this.e = e;
    }
    
    @Override
    public Throwable fillInStackTrace() {
        return this;
    }
}

此时不显示null了,而是正常的错误信息文本

相关推荐
葫芦和十三5 小时前
图解 MongoDB 21|选举与 failover:Primary 是怎么选出来的
后端·mongodb·agent
GetcharZp6 小时前
26k Star 开源内网穿透神器 NetBird,一分钟实现全球设备互联!
后端
考虑考虑6 小时前
Mybatis实现批量插入
java·后端·mybatis
咖啡八杯7 小时前
GoF设计模式——中介者模式
java·后端·spring·设计模式
lizhongxuan9 小时前
多Agent之间的区别
后端
青石路11 小时前
记一次多JDK版本问题的排查,一坑套一坑,差点没爬上来
java
杨充11 小时前
1.面向对象设计思想
后端
IT_陈寒12 小时前
Java的Date类又坑了我一次,改用时间戳真香
前端·人工智能·后端
systemPro12 小时前
2.6亿条设备数据,历史查询从超时到50ms,我做了什么
后端
要阿尔卑斯吗12 小时前
提示词优化启示:为什么“按顺序输出“比“关键度评分“更有效
后端