• AOP:分页参数统一校验


    需求说明

    为了保证系统的安全性,建议对所有的 查询列表 接口,添加分页参数,并对分页参数进行校验,保证参数的合法性。

    比如, pageSize(每 1 页的数据量),如果不做校验,一旦传递过来一个很大的数值(假设为:十亿),数据库可能会直接卡住,或者应用服务器的内存被挤爆。

    分页参数与校验逻辑

    分页参数参数含义校验逻辑
    pageNumber当前页码应大于等于1
    pageSize每 1 页的数据量取值范围为[1, 100]

    pageNumberpageSize,都应该是整数,如果传入的是小数、超出范围的数字、或者非数字,也应该直接报错;SpringMVC 已经自动支持这部分校验,不需要我们再去额外处理。

    解决方案

    使用 AOP(面向切面编程),在所有接口前,检查分页参数;如果不合法,直接返回接口调用失败,并将错误原因返回。

    返回接口调用失败,采用的方法是抛出业务异常,然后,由异常统一处理模块,将错误原因封装到返回结果中。

    代码

    参数校验切面

    package com.example.core.advice;
    
    import com.example.core.model.BusinessException;
    import com.example.core.model.ErrorEnum;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;
    
    import javax.servlet.http.HttpServletRequest;
    
    /**
     * 分页参数校验
     */
    @Aspect
    @Order(10)
    @Component
    public class PageValidator {
    
        private static final String PAGE_NUMBER = "pageNumber";
        private static final String PAGE_SIZE = "pageSize";
    
    
        // 拦截 com.example.web 包及其子包下的所有类的@RequestMapping注解修饰的方法
        @Pointcut("execution(* com.example.web..*.*(..)) and @annotation(org.springframework.web.bind.annotation.RequestMapping)")
        private void pointcut() {
        }
    
    
        // Before表示 advice() 将在目标方法执行前执行
        @Before("pointcut()")
        public void advice(JoinPoint joinPoint) {
            // 获取请求信息
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (attributes == null) {
                return;
            }
            HttpServletRequest request = attributes.getRequest();
    
    
            // 校验: [当前页码]
            validatePageNumber(request);
    
            // 校验: [每 1 页的数据量]
            validatePageSize(request);
        }
    
    
        /**
         * 校验: [当前页码]
         */
        private void validatePageNumber(HttpServletRequest request) {
            String pageNumberString = request.getParameter(PAGE_NUMBER);
            if (pageNumberString == null) {
                return;
            }
    
            int pageNumber = Integer.parseInt(pageNumberString);
            if (pageNumber >= 1) {
                return;
            }
    
            String userMessage = "当前页码,应大于等于1";
            String errorMessage = String.format("%s:【分页参数校验异常】:【错误字段:[%s],错误值:[%s],错误信息:[%s]】。",
                    ErrorEnum.A0425.getMessage(), PAGE_NUMBER, pageNumber, userMessage);
    
            throw new BusinessException(userMessage, ErrorEnum.A0425.name(), errorMessage);
        }
    
    
        /**
         * 校验: [每 1 页的数据量]
         */
        private void validatePageSize(HttpServletRequest request) {
            String pageSizeString = request.getParameter(PAGE_SIZE);
            if (pageSizeString == null) {
                return;
            }
    
            int pageSize = Integer.parseInt(pageSizeString);
            if (pageSize >= 1 && pageSize <= 100) {
                return;
            }
    
            String userMessage = "每 1 页的数据量,取值范围为[1, 100]";
            String errorMessage = String.format("%s:【分页参数校验异常】:【错误字段:[%s],错误值:[%s],错误信息:[%s]】。",
                    ErrorEnum.A0425.getMessage(), PAGE_SIZE, pageSize, userMessage);
    
            throw new BusinessException(userMessage, ErrorEnum.A0425.name(), errorMessage);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96

    分页参数实体

    分页参数,一般使用封装好的 分页参数实体 接收数据(推荐),但是也可以直接写在接口参数列表中(不推荐)。但是,不论怎样接收,只要是分页参数,就应该被校验。

    下面是 分页参数实体 的代码:

    package com.example.core.model;
    
    import io.swagger.v3.oas.annotations.media.Schema;
    import lombok.Data;
    import org.springdoc.api.annotations.ParameterObject;
    
    @Data
    @ParameterObject
    @Schema(name = "分页参数Query")
    public class PageQuery {
    
        @Schema(description = "当前页码", type = "Integer", defaultValue = "1", example = "1", minimum = "1")
        private Integer pageNumber = 1;
    
        @Schema(description = "每 1 页的数据量", type = "Integer", defaultValue = "10", example = "10", minimum = "1", maximum = "100")
        private Integer pageSize = 10;
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    测试接口

    package com.example.web.page.controller;
    
    import com.example.core.log.annotation.ApiLog;
    import com.example.core.model.PageQuery;
    import io.swagger.v3.oas.annotations.Operation;
    import io.swagger.v3.oas.annotations.tags.Tag;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RestController;
    
    @Slf4j
    @RestController
    @RequestMapping("page")
    @Tag(name = "分页参数校验")
    public class PageController {
    
    
        @ApiLog
        @GetMapping(path = "users/PageQuery")
        @Operation(summary = "查询用户列表 PageQuery", description = "分页参数校验,使用PageQuery接收分页参数。")
        public String listUsers(PageQuery pageQuery) {
            return "查询用户列表 PageQuery:成功";
        }
    
    
        @GetMapping(path = "users/NoPageQuery")
        @Operation(summary = "查询用户列表 NoPageQuery", description = "分页参数校验,分页参数直接写在了方法的参数列表中,未使用PageQuery接收分页参数。")
        public String listUsersWithoutPageQuery(Integer pageNumber, Integer pageSize) {
            return "查询用户列表 NoPageQuery:成功";
        }
    
    
        @RequestMapping(path = "users/RequestMapping", method = RequestMethod.GET)
        @Operation(summary = "查询用户列表 RequestMapping", description = "分页参数校验,使用 @RequestMapping 注解。")
        public String listUsersByRequestMapping(PageQuery pageQuery) {
            return "查询用户列表 RequestMapping:成功";
        }
    
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    测试结果

    能够正确校验出分页参数的错误。

    当前页码

    在这里插入图片描述

    每 1 页的数据量

    在这里插入图片描述

    切面调用排序:@Order(10)

    对切面进行排序,设定切面的触发顺序。数值越小的,优先级越高。

    能够触发的切面可能有好多个,需要排个序,告诉项目先触发哪一个。

    如果没有设定触发顺序,会按照切面在项目中的位置顺序来触发。比如下图中,默认情况,就是PageValidator 先触发,ApiLogAspect 后出发。

    如果 PageValidator 抛出了异常,后面的 ApiLogAspect 就不会被触发了。

    在这里插入图片描述

  • 相关阅读:
    GBase 8s是否支持自动实时跟踪、监控和调优
    RabbitMQ之TTL机制
    【云原生 | 46】高可用的开源键值数据库Etcd的安装与使用
    经典算法-----农夫过河问题(深度优先搜索)
    SpringBoot入门
    【strcmp函数和strncmp函数的对比于模拟实现】
    【LVS】lvs的四种模式的区别是什么?
    Oracle TFA
    记录一次Mac M1 WebStorm 的性能优化记录
    LLVM(6)ORC实例分析:Transform in cpp
  • 原文地址:https://blog.csdn.net/sgx1825192/article/details/133443129