手写一个类似@RequestParam的注解(用来接收请求体的参数)

news2024/12/24 10:25:37

一、本文解决的痛点

按照大众认为的开发规范,一般post类型的请求参数应该传在请求body里面。但是我们有些post接口只需要传入一个字段,我们接受这种参数就得像下面这样单独创建一个类,类中再添加要传入的基本类型字段,配合@RequestBody来实现这种功能多少有点繁琐:

@Data
public class TextHolder {
    private String text;
}

@PostMapping("test")
public ApiResponse test(@RequestBody TextHolder textHolder){
	....
}

那么我们能不能省略类的创建,实现一个类似@RequestParam的注解来实现请求体参数的直接接收呢?本文就是来解决这个问题的!

二、实现步骤

2.1定义我们这个增强版的请求体注解

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestBodyPlus {
    String value() default "";
}

2.2手写一个方法参数解析器

@Slf4j
public class RequestBodyPlusMethodHandler implements HandlerMethodArgumentResolver {

    public static final ThreadLocal<Map<String,Object>> requestBodyMap = new ThreadLocal<>();

    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        return methodParameter.hasParameterAnnotation(RequestBodyPlus.class);
    }

    @Override
    public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
        HttpServletRequest request  = nativeWebRequest.getNativeRequest(HttpServletRequest.class);
        RequestBodyPlus parameterAnnotation = methodParameter.getParameterAnnotation(RequestBodyPlus.class);
        String paramName = parameterAnnotation.value();
        if (StringUtils.isEmpty(paramName)) {
            paramName = methodParameter.getParameterName();
        }

        Map<String, Object> paramsMap = new HashMap<>();
        if (requestBodyMap.get() == null) {
            String requestBodyString = getRequestBodyString(request);
            paramsMap = JSON.parseObject(requestBodyString);
            // 需要把请求体Map放入ThreadLocal中,因为request中的inputStream读完一次,下次就读不了了,这也是原生的@RequestBody只能在方法参数中出现一次的原因!
            requestBodyMap.set(paramsMap);
        }else {
            paramsMap = requestBodyMap.get();
        }

        Object paramValue = paramsMap.get(paramName);
        // 有的参数需要databinder处理
        if (paramValue != null && webDataBinderFactory != null) {
            WebDataBinder binder = webDataBinderFactory.createBinder(nativeWebRequest, paramValue, paramName);
            paramValue = binder.convertIfNecessary(paramValue, methodParameter.getParameterType(), methodParameter);
        }

        return paramValue;
    }



    private String getRequestBodyString(final ServletRequest request){
        StringBuilder stringBuilder = new StringBuilder();
        try(InputStream inputStream = request.getInputStream();
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));){
            String line = "";
            while((line = bufferedReader.readLine()) != null){
                stringBuilder.append(line);
            }
        }catch (IOException e){
            log.error("request的ServletInputStream转换失败",e);
        }finally {
            return stringBuilder.toString();
        }
    }

}

2.3需要写一个拦截器,用来remove上面的threadLocal避免内存泄漏

public class RequestBodyPlusInterceptor implements HandlerInterceptor {

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        RequestBodyPlusMethodHandler.requestBodyMap.remove();
    }

}

2.4需要把拦截器和参数解析器配置好才能生效

@Configuration
public class WebConfigurer extends WebMvcConfigurerAdapter {

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(new RequestBodyPlusMethodHandler());
    }


    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new RequestBodyPlusInterceptor());
        super.addInterceptors(registry);
    }
}

三、使用案例

3.1测试代码:

@RestController
@RequestMapping("plus")
public class TestRequestBodyPlus {
    @PostMapping("one")
    public String test01(@RequestBodyPlus("dx") Integer dx, @RequestBodyPlus("ls") String ls, @RequestBodyPlus("jk") Date jk, @RequestBodyPlus("el") List<Long> el) {
        System.out.println(el);
        String format = "%d_______%s_______%s_________%d";
        return String.format(format, dx, ls, jk, el.size());
    }

    //有了下面这个方法,上面的接口的入参数就能传`2023-11-24`这种字符串
    @InitBinder //该注解底层的源码:RequestMappingHandlerAdapter#invokeHandlerMethod
    public void initBinder(WebDataBinder binder){
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        dateFormat.setLenient(false);
        binder.registerCustomEditor(Date.class,new CustomDateEditor(dateFormat,false));
    }//该方法的作用域仅在当前的controller,如果想全局生效,需要写在@ControllerAdvice所在的类中
}

3.2测试请求

POST localhost:8088/plus/one
{
    "dx" : 3,
    "ls" : "bbb",
    "jk" : "2023-11-24",
    "el" : [1,2,3,4,5]
}

3.3测试结果

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1887628.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

在卷积神经网络(CNN)中为什么可以使用多个较小的卷积核替代一个较大的卷积核,以达到相同的感受野

在卷积神经网络&#xff08;CNN&#xff09;中为什么可以使用多个较小的卷积核替代一个较大的卷积核&#xff0c;以达到相同的感受野 flyfish 在卷积神经网络&#xff08;CNN&#xff09;中&#xff0c;可以使用多个较小的卷积核替代一个较大的卷积核&#xff0c;以达到相同的…

git命令含有中文,终端输出中文乱码的问题

目录 1、[当前代码页] 的936 (ANSI/OEM - 简体中文 GBK) 是导致中文乱码的原因 2、这样会导致什么问题呢&#xff1f; (1) 问题一: 【属性】选项的【字体】无法识别自定义文字样式&#xff0c;【默认值】选项可选自定义字体样式&#xff0c;却无法覆盖【属性】选项 (2) 问题…

还用老气的Excel做报表?试试这款“免费”可视化工具,快速制作3D智慧社区!

随着科技的飞速发展&#xff0c;智慧社区已经成为现代城市管理的重要组成部分。作为这一领域的核心工具&#xff0c;3D智慧社区可视化大屏凭借其先进的技术和强大的功能&#xff0c;正在逐步改变我们的生活方式。今天给大家分享一个 基于山海鲸可视化开发的3D可视化大屏的案例—…

【Flutter】列表流畅性优化

前言 在日常APP的开发中&#xff0c;列表是使用频率最高的&#xff0c;这里讲述在Flutter中优化列表的滑动速度与流畅度&#xff0c;以来提高用户的体验。 方案 1、使用ListView.builder代替ListView ListView.builder在创建列表的时候要比ListView更高效&#xff0c;因为L…

Python题解Leetcode Hot100之二叉树

1. 二叉树的中序遍历 题目描述 给定一个二叉树&#xff0c;返回它的中序遍历。解题思路 使用递归的方法对左子树进行中序遍历&#xff0c;然后访问根节点&#xff0c;最后对右子树进行中序遍历。也可以使用栈来模拟递归的过程&#xff0c;迭代地进行中序遍历。代码class Solut…

机器人控制系列教程之Delta机器人运动学分析(1)

并联机构运动学 对于并联机构的位置正解、位置逆解和对应于位置解的速度、加速度进行分析是并联机构运动学分析主要内容。与串联机构不同&#xff0c;一般并联机构的位置逆解相对要简单&#xff0c;而位置正解则求解比较复杂些。并联机构的位置正解的求解过程中&#xff0c;大…

云徙电商OMS如何赋能品牌电商业务精细化运营

「6.18」、「双十一」、「双十二「、「年货节」等等&#xff0c;如火如荼的「造节」&#xff0c;将电商不断推向高地。 关于电商业务&#xff0c;一个业内共识是&#xff0c;2003 年是线上线下业务切换的关键节点。而消费者需求的变迁是引发这场业务模式革新的核心推手。 盘点…

Crossover和PD虚拟机谁更强大?pd虚拟机一年多少钱

在当前的虚拟化技术和应用程序兼容性解决方案中&#xff0c;Crossover和PD虚拟机&#xff08;Parallels Desktop&#xff09;都是备受用户喜爱的选择。对于需要在非原生系统上运行应用程序的用户而言&#xff0c;选择合适的工具尤为重要。那么&#xff0c;Crossover和PD虚拟机谁…

d3dx9_43.dll丢失怎么解决?d3dx9_43.dll怎么安装详细教程

在使用计算机中&#xff0c;如果遇到d3dx9_43.dll丢失或许找不到d3dx9_43.dll无法运行打开软件怎么办&#xff1f;这个是非常常见问题&#xff0c;下面我详细介绍一下d3dx9_43.dll是什么文件与d3dx9_43.dll的各种问题以及d3dx9_43.dll丢失的多个解决方法&#xff01; 一、d3dx9…

vue3+ts 写echarts 中国地图

需要引入二次封装的echarts和在ts文件写的option <template><div class"contentPage"><myEcharts :options"chartOptions" class"myEcharts" id"myEchartsMapId" ref"mapEcharts" /></di…

vscode语言模式

1.背景 写vue3ts项目的时候&#xff0c;用到了volar插件&#xff0c;在单文件使用的时候&#xff0c;鼠标悬浮在代码上面会有智能提示&#xff1b; 但是最近volar插件提示被弃用了&#xff0c;然后我按照它的官方提示&#xff0c;安装了Vue-official扩展插件&#xff0c;但是…

【开发篇】明明配置跨域声明,为什么却仍可以发送HTTP请求

一、问题 在SpringBoot项目中&#xff0c;明确指定仅允许指定网站跨域访问&#xff1a; 为什么开发人员却仍旧可以通过HTTP工具调用接口&#xff1f; 二、为什么 在回答这个问题之前&#xff0c;我们首先要了解一下什么是CORS&#xff01; 1、什么是CORS CORS的全称为跨域资源…

20240702在飞凌OK3588-C开发板上通过HDMI OUT输出USB3.0接口的热像仪的预览图像

20240702在飞凌OK3588-C开发板上通过HDMI OUT输出USB3.0接口的热像仪的预览图像 2024/7/2 18:19 rootok3588:/# rootok3588:/# rootok3588:/# lsusb Bus 005 Device 001: ID 1d6b:0002 Bus 003 Device 001: ID 1d6b:0001 Bus 001 Device 001: ID 1d6b:0002 Bus 006 Device 00…

普发PfeifferTC400真空泵驱动操作手侧引脚定义通讯定义

普发PfeifferTC400真空泵驱动操作手侧引脚定义通讯定义

shellhub 部署

1、环境介绍 操作系统&#xff1a;龙蜥os 7.9 2、安装docker yum install -y yum-utils device-mapper-persistent-data lvm2 yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo sed -i sdownload.docker.commirrors.aliyun.c…

美的、希亦、苏泊尔超声波清洗机值不值得买?对比测评甄选性能王!

超声波清洗机作为一种高效、便捷的清洁设备&#xff0c;不仅可以用于清洗眼镜&#xff0c;还可以用于清洗化妆刷、珠宝、手表等多种物品&#xff0c;极大的方便了日常生活。其高频振动的特点可以帮助去除物品表面的污垢和细菌&#xff0c;让物品焕然一新。因此&#xff0c;选择…

YOLOv8改进 | 卷积模块 | 减少冗余计算和内存访问的PConv【CVPR2023】

秋招面试专栏推荐 &#xff1a;深度学习算法工程师面试问题总结【百面算法工程师】——点击即可跳转 &#x1f4a1;&#x1f4a1;&#x1f4a1;本专栏所有程序均经过测试&#xff0c;可成功执行&#x1f4a1;&#x1f4a1;&#x1f4a1; 专栏目录 &#xff1a;《YOLOv8改进有效…

最小步数模型——AcWing 1107. 魔板

最小步数模型 定义 最小步数模型通常是指在某种约束条件下&#xff0c;寻找从初始状态到目标状态所需的最少操作或移动次数的问题。这类问题广泛存在于算法、图论、动态规划、组合优化等领域。具体来说&#xff0c;它涉及确定一个序列或路径&#xff0c;使得按照特定规则执行…

【JS】纯web端使用ffmpeg实现的视频编辑器-视频合并

纯前端实现的视频合并 接上篇ffmpeg文章 【JS】纯web端使用ffmpeg实现的视频编辑器 这次主要添加了一个函数&#xff0c;实现了视频合并的操作。 static mergeArgs(timelineList) {const cmd []console.log(时间轴数据,timelineList)console.log("文件1",this.readD…

BAS(入侵与攻击模拟)正在替代红队测试?

之前经常会被用户问到&#xff0c;漏扫、渗透和红队红的区别是啥&#xff1f; 传统的漏扫、渗透和红蓝对抗&#xff0c;可以看到工具化的漏洞不可靠&#xff0c;人工的成本就高。怎么找到一个漏洞可信度又高&#xff0c;成本又低的&#xff0c;就诞生了BAS。 抛开漏扫&#xf…