开发日记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了,而是正常的错误信息文本

相关推荐
想用offer打牌2 小时前
MCP (Model Context Protocol) 技术理解 - 第二篇
后端·aigc·mcp
曹牧3 小时前
Spring Boot:如何测试Java Controller中的POST请求?
java·开发语言
KYGALYX3 小时前
服务异步通信
开发语言·后端·微服务·ruby
掘了3 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
爬山算法4 小时前
Hibernate(90)如何在故障注入测试中使用Hibernate?
java·后端·hibernate
kfyty7254 小时前
集成 spring-ai 2.x 实践中遇到的一些问题及解决方案
java·人工智能·spring-ai
猫头虎4 小时前
如何排查并解决项目启动时报错Error encountered while processing: java.io.IOException: closed 的问题
java·开发语言·jvm·spring boot·python·开源·maven
李少兄4 小时前
在 IntelliJ IDEA 中修改 Git 远程仓库地址
java·git·intellij-idea
Moment4 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
忆~遂愿4 小时前
ops-cv 算子库深度解析:面向视觉任务的硬件优化与数据布局(NCHW/NHWC)策略
java·大数据·linux·人工智能