扩展Spring Boot中的GET方法,支持复杂参数

现在restful风格的webapi正大行其道,但是因为这并不是一个标准,而只是一种风格,所以每家的实现都各有特色,实践中也会因为各种各样的原因,灵活的突破restful的定义。
这些问题当中,比较突出的一个就是经常会有把传递复杂参数的GET请求转换成POST请求的情况出现。这是因为HTTP的GET请求对body的发送没有明确定义,所以不好使用GET来进行复杂参数的发送,而POST则没有这些限制。比较典型的一个场景就是进行复杂条件的数据查询,按照restful风格的定义来说,这是一个典型的使用GET请求的场景,而实践中却经常会被灵活的POST化。
为了解决这个问题,给大家总结下一个比较常用的解决方案:url中接受一个被编码(如base64)的复杂对象,并通过自定义注解,在接口调用前对这个被编码的对象进行解码,并转换成bean对象进行使用。
主要代码展示如下,请大家按需修改使用:

  • controller: restful api的定义

    1
    2
    3
    4
    5
    6
    7
    // @Data 注解来自lombok,避免我们手写get、set方法
    @Data
    public class PageQuery{
    private Integer size;
    private Integer current;
    private Map<String,String> condition;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    @Slf4j
    @RestController
    @RequestMapping("/app")
    public class AppController {
    /**
    * 分页查询
    * 可传递查询参数,按照某些字段分页查询
    * GET host/app/page?{复杂对象经过base64编码后的字符串}
    * @param pageQuery 分页条件,查询参数
    * @return 分页查询结果
    */
    @GetMapping("/page")
    public Object page( @RequestJson PageQuery pageQuery) {
    // pageQuery为get方法传入的复杂对象
    // 此处处理查询逻辑
    return null;
    }

    }
  • 上一步中会法发现注解RequestJson找不到,所以需要进行一下定义

    1
    2
    3
    4
    5
    6
    7
    8
    /**
    * get 请求参数使用该注解就会使用自定义的参数解析器来解析复杂的参数
    */
    @Target(ElementType.PARAMETER)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface RequestJson {
    }
  • 自定义注解的解析器

    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
    /**
    * 自定义参数解析器,请求参数使用该注解 {@link RequestJson} 就会使用以下解析方式。
    * 说明:前端将参数序列化成 json,再经过 Base64 编码之后放在 url 上传输。
    * 后端 Base64 解码之后,再通过 json 工具将参数反序列化为对象。
    * 例如:http://localhost:8080/app/page?eyJjdXJyZW50IjoxLCJzaXplIjoxMCwidG90YWwiaG9uZSI6IiJ9fQ==
    */
    @Slf4j
    public class HandlerRequestJsonArgumentResolver implements HandlerMethodArgumentResolver {

    /**
    * 该方法判断是否要使用该参数解析器。这里我们就判断 有没有使用 @RequestJson 注解来判断是否使用该解析器
    * 如果返回 true 就运行 resolveArgument 方法来解析参数
    * 如果返回 false 使用其他参数解析器来解析参数,springframework 有很多参数解析器
    */
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
    return parameter.hasParameterAnnotation(RequestJson.class);
    }

    /**
    * 该方法真正用来解析参数。
    * 先将 json 字符串解析出来
    * 再将 json 转成 Bean 对象
    */
    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
    HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
    try {
    String json = new String( Base64.getDecoder().decode(request.getQueryString()), "UTF-8");
    ObjectMapper objectMapper=new ObjectMapper();
    return objectMapper.readValue( json, parameter.getParameterType());
    } catch (Exception e) {
    log.error("请求参数解析失败,请检查!" + e.getMessage());
    throw new RuntimeException(e.getMessage());
    }
    }
    }
  • 注册自定义参数解析器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    @Configuration
    public class WebConfig implements WebMvcConfigurer {
    /**
    * 注册自定义参数解析器
    */
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
    resolvers.add(new HandlerRequestJsonArgumentResolver());
    }
    }

PS. 处理方法及示例代码基本参考(COPY)网络资源


扩展Spring Boot中的GET方法,支持复杂参数
http://prebug.cn/2022/08/08/enhance-get-method/
作者
任志伟
发布于
2022年8月8日
许可协议