后端自动去除字符串前后空格

文章介绍了在处理Post请求查询时遇到的前端传参带有空格导致查询错误的问题。作者提出使用自定义注解`Trim`和SpringAOP切面来自动去除String字段的前后空格,以优化代码并减少手动修改。这种方法可以在类或属性级别应用,提高代码的可维护性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

关于Post请求查询,前端传来的字符串前后有空格的处理

1. 问题起因:

最近办公室在对项目系统进行统一的测试,测试出了一些查询的问题,我们这写的index页面的分页查询接口,如果是index页面展示的字段单表可以满足,就直接用mybatis plus的分页查询,但是呢,凡是都有例外,因为做的是公司用的oa系统,关联的地方很多,比如组织机构啊,项目啊,合同啊,单位啊,之类的……总之有很是单表查询满足不了的,因此,就产生了今天的问题,前端的入参,不标准怎么办?冷不丁的给你冒出个前后空格,导致查询结果不对,用户就反馈,为什么这个查不出来啊,我都是复制的,想想就头疼。

2. 初步想法

因为最开始对这个系统功能不熟悉,加之比较急,就没有考虑这个问题,具体是前端做,还是后端做,其实:我感觉,必须是前端自己做,哈哈哈!一开始呢,第一时间肯定就是想到trim方法嘛。

就是用下面这种改法:

这是控制器方法,一个例子而已。

	@PostMapping("/getIndexPageData")
    public R getIndexPageData(@RequestBody SubProblemIndexReqDTO reqDTO,
                              @RequestHeader String token) {

        log.info(LogConstant.CALL_INTERFACE,"/security/statistical/getIndexPageData");
        return securityCheckService.getIndexPageData(reqDTO,token);

    }

这是入参类的结构

/**
 * index页请求DTO
 * @author: 一位大帅哥
 * @date: 2022/10/12 10:32
 * @description: TODO
 */
@Data
@EqualsAndHashCode(callSuper = true)
@Trim // 就是我们今天要说的注解
public class SubProblemIndexReqDTO extends PageReqDTO {

    /**
     * 区域
     */
    private String area;

    /**
     * 流程编号
     */
    private String flowCode;


    /**
     * 项目名称
     */
    private String projectName;


    /**
     * 合同名称
     */
    private String contractName;


    /**
     * 监理单位名称
     */
    private String companyNameJl;

    /**
     * 施工单位名称
     */
    private String companyNameSg;

    /**
     * 检查类型
     */
    private String checkType;

    /**
     * 回复率起始值
     */
    private Double responseRateBegin;

    /**
     * 回复率结束值
     */
    private Double responseRateEnd;

    /**
     * 整改率起始值
     */
    private Double rectificationRateBegin;

    /**
     * 整改率结束值
     */
    private Double rectificationRateEnd;

    /**
     * 闭合率起始值
     */
    private Double closingRateBegin;

    /**
     * 闭合率结束值
     */
    private Double closingRateEnd;

    /**
     * 检查日期 --> 起始值
     */
    private Date checkDateBegin;

    /**
     * 检查日期 --> 结束值
     */
    private Date checkDateEnd;

}

顺便给大家说一下利用Post请求做分页,把查询参数放到类中的,定义一个抽象的ReqDTO,把查询的公共参数放到里面,直接继承着用,感觉大家应该都能想到。下面是我用的ReqDTO

/**
 * <p>请求公共属性抽象类</p>
 *
 * @author gaoming
 * @Date 2022/9/6 11:43
 */
@Data
public abstract class PageReqDTO {

    /**
     * 页大小 , 默认值 10
     */
    private Integer pageSize = 10;

    /**
     * 当前页 , 默认值 1
     */
    private Integer pageNum = 1;

    /**
     * 流程状态
     */
    private Integer processStatus;

    /**
     * 项目ids
     */
    private List<String> projectIds;

    /**
     * 项目组织结构id
     */
    private String organizationId;

}

一开始想的就是在service方法中,把要用到的String属性判断不为空,就进行trim操作

if (StrUtil.isNotBlank(reqDTO.getArea())) {
   reqDTO.setArea(reqDTO.getArea().trim());
}

但是这样做就太多了,有的入参类String属性有一二十个,那要是这样改,人不得改崩了啊!所以果断放弃,因此,就要从别的方面入手,第一时间想到用注解形式,只要定义一个注解,放到类或者类的属性上面,不就可以了吗?

3. 正文开始

看了这么久,终于等到正文了!

  1. 定义一个自定义注解Trim

    /**
     * @author GmDsg
     * @date 2023/5/15 9:29
     * @description 去除String字段前后空格
     */
    @Target({ElementType.FIELD, ElementType.TYPE}) // 注解可作用在类或者类属性
    @Retention(RetentionPolicy.RUNTIME) // 运行时生效
    public @interface Trim {
    }
    
  2. 定义一个切面类

    /**
     * @author GmDsg
     * @date 2023/5/15 9:30
     * @description Trim切面
     */
    @Aspect
    @Component
    @Slf4j
    public class TrimAop {
    
        /**
         * 切入点:任意包路径下controller包下的任意Controller类的公共方法
         * <p切入点根据自己的需求,做不同的操作,不规定死/p>
         */
        @Pointcut("execution(public * *..controller.*Controller.*(..))")
        public void pointCut() {
    
        }
    
        /**
         * 前置操作
         * @param joinPoint 连接点
         * @throws Throwable 抛出异常
         */
        @Before("pointCut()")
        public void before(JoinPoint joinPoint) throws Throwable {
            // 获取入参
            Object[] args = joinPoint.getArgs();
            for (int i = 0; i < args.length; i++) {
                Object arg = args[i];
                if (arg == null) {
                    continue;
                }
                // 获取该入参类是否有Trim注解
                Class<?> argClass = arg.getClass();
                Trim annotation = argClass.getAnnotation(Trim.class);
    
                // 获取该入参类所有属性
                Field[] fields = argClass.getDeclaredFields();
                if (annotation == null) {
                    // 类上没有Trim注解
                    for (Field field : fields) {
                        // 获取类中属性是否有Trim注解
                        Trim fieldAnnotation = field.getAnnotation(Trim.class);
                        if (fieldAnnotation == null || !field.getType().equals(String.class)) {
                            continue;
                        }
                        // 该属性有注解并且也是String类型
                        // 设置私有属性可访问
                        field.setAccessible(true);
                        // 获取属性值
                        Object value = field.get(arg);
                        if (value != null) {
                            // 执行trim操作,并重新为该属性赋值
                            field.set(arg, ((String) value).trim());
                        }
                    }
                } else {
                    // 入参类上存在Trim注解
                    for (Field field : fields) {
                        // 遍历所有属性值并筛选是String类型的字段
                        if (!field.getType().equals(String.class)) {
                            continue;
                        }
                        // 设置私有属性可访问
                        field.setAccessible(true);
                        // 获取属性值
                        Object value = field.get(arg);
                        if (value != null) {
                            // 执行trim操作,并重新为该属性赋值
                            field.set(arg, ((String) value).trim());
                        }
                    }
                }
                // 为当前类赋值处理完的类,实现去除空格效果
                args[i] = arg;
            }
        }
    }
    
  3. 浅浅的解释一下

    使用@Before注解获取前置操作的所有入参,也就是这一句

            // 获取入参
            Object[] args = joinPoint.getArgs();
    

    会获取控制器方法的所有入参:就会获取reqDTO,token

    @PostMapping("/getIndexPageData")
    public R getIndexPageData(@RequestBody SubProblemIndexReqDTO reqDTO,
                              @RequestHeader String token) {
    
        log.info(LogConstant.CALL_INTERFACE,"/security/statistical/getIndexPageData");
        return securityCheckService.getIndexPageData(reqDTO,token);
    
    }
    

    遍历获取到的Object,反射获取Object的class,调用class的getAnnotation(Trim.class)方法

    // 获取类中属性是否有Trim注解
    Trim annotation = argClass.getAnnotation(Trim.class);
    

    如果这个annotation不为空,则说明该类上有Trim注解,对于类上面有Trim注解的,会对所有String类型并且不为null的属性进行去除

    前后空格操作。

    所以就要获取类中所有的属性

    // 获取该入参类所有属性
    Field[] fields = argClass.getDeclaredFields();
    

    遍历fields,挨个判断Field是否是String类型并且不为空,如果满足,则为该Field设置去除前后空格的值,具体操作如下。

    // 入参类上存在Trim注解
    for (Field field : fields) {
       // 遍历所有属性值并筛选是String类型的字段
       if (!field.getType().equals(String.class)) {
            continue;
       }
       // 设置私有属性可访问
       field.setAccessible(true);
       // 获取属性值
       Object value = field.get(arg);
       if (value != null) {
           // 执行trim操作,并重新为该属性赋值
           field.set(arg, ((String) value).trim());
       }
    }
    

    然后类上面没有Trim注解的呢,我们还要再对fields进行遍历,因为我们的注解当时定义是作用在类上或者属性上的,因此类上面没有还要对属性进行判断,这个属性有没有Trim注解,有这个注解它是不是String类型的,话不多说,上代码。

    // 类上没有Trim注解
    for (Field field : fields) {
        // 获取类中属性是否有Trim注解
        Trim fieldAnnotation = field.getAnnotation(Trim.class);
        if (fieldAnnotation == null || !field.getType().equals(String.class)) {
            continue;
        }
        // 该属性有注解并且也是String类型
        // 设置私有属性可访问
        field.setAccessible(true);
        // 获取属性值
        Object value = field.get(arg);
        if (value != null) {
            // 执行trim操作,并重新为该属性赋值
            field.set(arg, ((String) value).trim());
        }
    }
    

    其实这个没啥讲的,取属性上的注解,看属性上有没有这个注解,如果有,还要判断这个属性是不是String类型的,如果都满足,则对这个Field做trim操作

4. 测试

我们找到一个接口,随便测试一下

  • 先在入参类上面加Trim注解

    1

  • 然后我们故意给String属性写空格,然后查询

    image-20230516091812238

  • 后台打印日志,已经把前后空格去掉了

    image-20230516091842106

  • 然后我们再试试不在类上写注解,只在指定字段写注解

    4

    给指定属性加注解:待会测试中,只有projectName 和 companyNameSg有值,都带空格

    5

​ 这是入参,然后直接点查询

6

​ 后台日志:

​ 可以看到,projectName 和 companyNameSg都加了空格,因为projectName加了Trim注解,所以去掉了空格,反之,companyNameSg没加注解,没有去掉空格,导致查询为空!

7

​ 结果:

8

5. 最后

最后还是要说一下,利用切面类,是很好用,但是,启动项目时,spring也会加载切面,如果切面写的范围过大,会导致启动速度变慢,也可能导致方法查询不准确,所以大家用的时候,要写好这个切入点。以上就是我这次给大家分享的‘干货’,因为我也是小菜鸟一枚,写博客也只是记录一下,大家有什么好的建议或者文章中有一些写的不对不准确的地方,欢迎大家指正。最后祝在看文章的您,能在这卷到飞起的java大环境中,成长为一个超级大大大大大……神!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值