SpringMVC 重写、扩展HttpServletRequest请求参数

springboot通过HttpServletRequestWrapper获取所有请求参数 springboot通过拦截器获取参数有两种方式,一种通过request.getParameter获取Get方式传递的参数,另外一种是通过request.getInputStream或reques.getReader获取通过POST/PUT/DELETE/PATCH传递的参数; 1.拦截器获取参数有哪些方式 @PathVariable注解是REST风格url获取参数的方式,只能用在GET请求类型,通过getParameter获取参数 @RequestParam注解支持GET和POST/PUT/DELE. 阅读详情

一、背景说明

     由于在项目进行前后端分离改造时,请求由多种传参方式统一定义为JSON格式传输,在改造过程中需要前后版本兼容。如果能在Controller接收参数之前将JSON格式参数进行解析成原有参数,对Request请求参数进行重写,这样能可以大大减少开发成本。

二、调研

    抱着对Request请求参数目标出发,对@InitBinder和HttpServletRequestWrapper进行了研究,最终使用HttpServletRequestWrapper解决了当前问题。

    1、@InitBinder  初次接触时是用在对Date类型参数进行转换,常用方法如下所示:

@InitBinder
protected void initBinder(WebDataBinder binder) {
	SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
	dateFormat.setLenient(true);
    //根据时间类型进行转换
	binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
    //指定参数字段名称进行转换
	binder.registerCustomEditor(Date.class, "registerDate",new CustomDateEditor(dateFormat, true));
}

注:如果Controller的方法没有一个参数时,@initBinder标注的方法并不会执行(这个也比较好理解)

也可以通过自定义Editor对参数进行解析,详见:https://blog.youkuaiyun.com/axin66ok/article/details/17938095

但是通过@InitBinder并不能满足我的要求,因为registerCustomEditor需要知道将要转换成的参数类型,由于我得Controller参数类型因方法不同而不同。

   2、HttpServletRequestWrapper

       通过重写HttpServletRequest或者继承HttpServletRequestWrapper对HttpServletRequest进行装饰,可以对请求请求参数进行修改。

       重写HttpServletRequest 工作量较大(其中方法较多),继承HttpServletRequestWrapper对HttpServletRequest进行装饰实现起来比较简单,仅需要对不满足你的需求接口进行重写就可以(首选)。

重写HttpServletRequest,代码如下所示:

public class MyHttpServletRequest implements HttpServletRequest {
    @Override
    public String getAuthType()
	@Override
     public Cookie[] getCookies() 
	@Override
    public long getDateHeader(String s) 
	   //.......
}

自定义HttpServletRequest装饰器HttpServletRequestWrapper和Filter过滤器,代码如下所示:

package com.timer.web.interceptor;

import com.alibaba.fastjson.JSONObject;
import com.timer.common.utils.JsonUtil;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Map;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;

public class MyParametersFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        MyParametersWrapper myParametersWrapper = new MyParametersWrapper(httpServletRequest);
        filterChain.doFilter(myParametersWrapper, httpServletResponse);
    }

    /**
     * 继承HttpServletRequestWrapper,创建装饰类,以达到修改HttpServletRequest参数的目的
     */
    private class MyParametersWrapper extends HttpServletRequestWrapper {

        private static final String  OTHER_PARAM = "other";
		
        private Map<String, String[]> parameterMap; // 所有参数的Map集合

        /**
         * other 参数所包含的参数信息
         */
        private Map<String, String[]> otherMap;


        public MyParametersWrapper(HttpServletRequest request) {
            super(request);
            parameterMap = request.getParameterMap();
            otherMap = new ConcurrentHashMap<>();
            /**
             * 判断other参数是否为空
             */
            if(null != parameterMap.get(BODY_PARAM)){
                otherConversion(parameterMap.get(BODY_PARAM));
            }
        }

        /**
         * 将other参数转为map参数
         * @param others
         */
        private void otherConversion(String[] others){
            if(null != others && others.length > 0){
                JSONObject obj = null;
                for(String other : others){
                    try {
                        obj = JsonUtil.parseObject(other);
                        if(null != obj){
                            for(Map.Entry<String, Object> entry : obj.entrySet()){
                                otherMap.put(entry.getKey(),new String []{String.valueOf(entry.getValue())});
                            }
                        }
                    }catch (Throwable e){
                        logger.error("otherConversion.is.system.error",e);
                    }
                }
            }
        }

        // 重写几个HttpServletRequestWrapper中的方法
        /**
         * 获取所有参数名
         *
         * @return 返回所有参数名
         */
        @Override
        public Enumeration<String> getParameterNames() {
            Vector<String> vector = new Vector<String>(parameterMap.keySet());
            vector.addAll(otherMap.keySet());
            return vector.elements();
        }

        /**
         * 获取指定参数名的值,如果有多个参数时默认取第一个
         *
         * @param name 指定参数名
         * @return 指定参数名的值
         */
        @Override
        public String getParameter(String name) {
            String[] values = parameterMap.get(name);
            try {
                if (values == null) {
                    if (null != otherMap) {
                        values = otherMap.get(name);
                    }
                }
            }catch (Throwable e){
                logger.error("getParameter.is.system.error",e);
            }
            if(null == values){
                return null;
            }
            return values.length > 0 ? values[0] :super.getParameter(name);
        }

        /**
         * 获取指定参数名的所有值的数组
         */
        @Override
        public String[] getParameterValues(String name) {
            String[] values = parameterMap.get(name);
            try{
                if (values == null) {
                    if(null != otherMap){
                        values = otherMap.get(name);
                    }
                }
            }catch (Throwable e){
                logger.error("getParameterValues.is.system.error",e);
            }
            return values != null ? values : super.getParameterValues(name);
        }
    }
}

疑问一:为什么要单独定义一个otherMap用于存储解析后的参数

       因为request.getParameterMap() 获取到的继承了ParameterMap类,该类由于防止并发问题单独定义了boolean locked属性,如果贸然向其中进行新增值时会出现parameterMap.locked异常

疑问二:getParameterNames、getParameter、getParameterValues三个方法都在哪里会用到

1)getParameterNames方法:

     getParameterNames会用在Controller的方法参数是自定义实体时使用到,例子如下所示:

@RequestMapping(value = "/index")
@ResponseBody
public String index(MyVo param) {
	//......
}

在进行HttpServletRequest参数转为MyVo实体时会用到 getParameterNames方法,所以在以上代码中需要将OtherMap的keys赋正常返回。

2)getParameter方法:

   getParameter方法会在使用@RequestParam()注解和 request.getParameter("")时用到,间接调用getParameter方法。

3)getParameterValues方法:

   getParameterValues方法会在使用request.getParameterValues("")时用到,间接调用getParameterValues方法。

   

其中JsonUtil如下所示:

import com.alibaba.fastjson.JSON;
public class JsonUtil {
	public static JSONObject parseObject(String jsonText) {
		try {
			return JSON.parseObject(jsonText);
		} catch (Exception e) {
			logger.error("解析字符串:{} json出错:{}", jsonText, e);
		}
		return null;
	}
}

使用过滤器:

<filter>
   <filter-name>myParametersFilter</filter-name>
   <filter-class>com.timer.web.interceptor.MyParametersFilter</filter-class>
</filter>
<filter-mapping>
   <filter-name>myParametersFilter</filter-name>
   <url-pattern>/*</url-pattern>
</filter-mapping>

三、总结

HttpServletRequestWrapper 装饰器可以在请求Controller方法前,对方法参数进行修改,可用于修改参数前缀、添加公参、参数格式重新排版等

 

 

 

深入解析SpringMVC工作流程:从核心原理到实战应用 SpringMVC作为Java Web开发的核心框架,其本质是一个基于前端控制器设计模式的请求处理管道。理解其工作原理,关键在于掌握DispatcherServlet如何协调HandlerMapping、HandlerAdapter等核心组件,完成从请求路由到响应渲染的完整生命周期。这一机制不仅解决了传统Servlet开发中业务逻辑与视图渲染耦合的问题,更通过高度可插拔的组件设计,为开发者提供了强大的扩展能力,例如通过自定义HandlerMethodArgumentResolver实现灵活的参数绑定,或利用 阅读详情

相关推荐

SpringBoot接口参数统一加解密实战:基于RequestBodyAdvice的无侵入方案

在微服务架构和API安全领域,数据传输安全是基础且关键的技术环节。其核心原理在于通过加密算法对敏感信息进行转换,确保数据在传输过程中的机密性和完整性。从技术价值看,统一的加解密方案能有效防止数据泄露、篡改,并满足合规性要求,是构建可信系统的基石。常见的应用场景包括用户隐私信息传输、金融交易数据交互以及企业内部敏感接口调用等。本文聚焦于SpringBoot框架,深入探讨如何利用RequestBodyAdvice与ResponseBodyAdvice这两个扩展点,结合AES等对称加密算法,实现一套对业务代码无侵

weixin_30410119的博客 388

SpringMVC修改请求参数

SpringMVC修改请求参数

qq_32003693的博客 694

手把手教你用STM32CubeMX和HAL库驱动DW3000:从SPI配置到第一个测距Demo

本文详细介绍了如何使用STM32CubeMX和HAL库驱动DW3000 UWB定位通信模块,从SPI配置到实现厘米级测距Demo的全过程。涵盖硬件连接、SPI通信调试、驱动移植及双向测距(TWR)算法实现,帮助开发者快速掌握UWB高精度定位技术,适用于仓储物流、智能家居等场景。

weixin_27038245的博客 485

SpringMVC请求参数的处理

一、默认方式获取请求参数 直接给方法入参写一个和请求参数名相同的变量,这个变量就可以接收请求的参数值 <a href="request1?username=张三">获取请求参数</a> @RequestMapping(value = "/request1") public String request1(String username){ System.out.println(username); return "success";

qq_39794062的博客 659

LVDS信号调试必备:5分钟搞懂Xilinx IDELAYE3的TIME模式校准

本文深入解析了在Xilinx Ultrascale系列FPGA中调试LVDS信号时,如何正确使用IDELAYE3的TIME模式进行精密延迟校准。文章重点阐述了IDELAYCTRL、EN_VTC管脚与校准状态机的协同工作机制,揭秘了内置自校准(BISC)中Align_Delay的影响,并提供了从抽头值计算到实际眼图优化的完整实战方案,帮助工程师快速解决高速接口的时序问题。

joy55的博客 215

02-Cesium-实现天地图标记

首先天地图申请token,然后Cesium-实现天地图标记详细说明

m0_62785037的博客 1171

HttpServletRequest增加自定义参数

事情是这样的,在做用户登录这个模块的时候,前端是前端人员在做,后端是我在做。写好之后准备联调,突然发现传过来的是加密串,这很正常,但我自己写的所有后续的方法里参数走的是HttpServletRequest,获取参数是从request的参数里拿,那么此时就拿不到了。当然可以拿到加密串之后先解密,然后参数直接用解密完设定好的参数即可,但我不想改写好并测试好的代码,那么就要在走进我的方法之前在HttpServletRequest的参数中定义好我要用的字段。 HttpServletRequestWrapper为Ht

daring_xiaowang的博客 7934

Windows 11下JDK 21与JMeter 5.6.3环境配置全攻略

环境变量是操作系统管理软件路径与执行权限的核心机制,它通过预设的键值对,指引系统在调用命令时定位可执行文件。理解其工作原理,是解决软件安装后“命令未找到”等问题的关键。对于Java生态而言,正确配置JAVA_HOME和Path变量,不仅能让java、javac等基础命令全局可用,更是Maven、Gradle乃至JMeter等众多工具链顺畅运行的基石。JMeter作为一款基于Java开发的经典性能测试与接口测试工具,其稳定运行高度依赖健全的Java环境。本文聚焦于在Windows 11系统上,为JDK 21与

weixin_30940783的博客 449

在springmvc的Filter过滤器中更改请求参数,servletRequest.getParameter或者servletRequest.getParameterMap的参数教程

如何更改servletRequest.getParameterMap的参数? 今天接到一个需求,需要对整个项目的请求参数做加密处理,但是项目已经成型,想要更改参数就需要使用MVC的过滤器了。 想要实现的结果是 前端发送一个这样的请求:/chaopin-boot/api/v1/short/add?ec=O9xLAKlugFVUl8NVY-4PHkg-0_KpQZ-IPzc9mYCKY31Rpg2ptsEkkdkEVnsT-6b3JaVY1I6T8Fy97t95AKNP2GfIgyaF5uQ46fo.

qq_32635221的博客 3446

手把手教你用FPGA实现SGMII接口:从IP核配置到板级调试全流程

本文详细介绍了如何在Xilinx 7系列FPGA上实现SGMII(Serial Gigabit Media Independent Interface)接口的全流程,包括IP核配置、约束编写和硬件调试。通过具体的硬件选型建议、软件工具链配置和PCB设计规范,帮助开发者快速构建稳定可靠的千兆通信链路,并提供了常见故障排查和性能优化技巧。

weixin_29194693的博客 296

基于ESP32的智能无线广播系统开发博客

通过本项目的开发,我们成功地实现了基于ESP32的智能无线广播系统。该系统不仅具备传统广播的功能,还实现了通过Wi-Fi无线传输音频、实时广播和智能控制等多种先进功能。未来,随着技术的进一步发展,我们还可以扩展更多功能,如智能推荐广播内容、支持语音识别和控制等。希望本项目能够为嵌入式开发人员提供一些思路和参考,帮助大家在实际开发中更好地应用ESP32平台,开发出更加智能和实用的无线广播系统。

2201_76125393的博客 423

Spring、SpringBoot重写HttpServletRequestWrapper进行parameter处理

前言:   日常开发中,我们常常需要对接口接入的数据参数进行处理,比如解密,关于@RequestBody这类流参数处理,上一章已经有处理方法,链接:RequestBodyAdvice 和 ResponseBodyAdvice增强器使用 这篇主要讲对request的参数进行处理。 实现步骤   众所周知,获取request的参数无非三种方式:   (1)getParameter(String nam...

IM507的博客 1万+

毕设加分项!手把手教你完成基于YOLOv8与PyQt5的多类别目标检测系统——让你的毕设从“完成”到“出色”

从KITTI数据集处理,到YOLOv8模型训练,再到PyQt5界面开发,整个流程其实没有想象中那么难。只要你跟着步骤一步步来,不仅能顺利完成毕设,还能掌握目标检测和GUI开发的核心技能——这些技能在未来的求职、读研中都非常实用。如果过程中遇到问题,别担心!可以去YOLOv8的官方文档、PyQt5论坛找答案,也可以和同学、老师交流。记住,完成毕设的过程也是你技术成长的过程,坚持下去,你会为自己的成果感到自豪。

m0_71589036的博客 178

springMVC开发手册

MVC是一种软件架构的思想,将软件按照模型、视图、控制器来划分M:Model,模型层,指工程中的JavaBean,作用是处理数据JavaBean分为两类:V:View,视图层,指工程中的html或jsp等页面,作用是与用户进行交互,展示数据C:Controller,控制层,指工程中的servlet,作用是接收请求和响应浏览器MVC的工作流程: 用户通过视图层发送请求到服务器,在服务器中请求被Controller接收,Controller调用相应的Model层处理请求,处理完毕将结果返回到Controller

AlbenXie的博客 711

PyRoki 入门教程(二)安装与使用

PyRoki是一个用于机器人运动学优化的Python库,具有模块化设计、可扩展性和跨平台支持等特点。它支持逆运动学、轨迹优化和动作重定位等任务,采用Levenberg-Marquardt优化器提高效率。主要功能包括:基于URDF的可微分正向运动学模型、自动生成碰撞基元、通用成本因子和自定义成本函数等。PyRoki支持CPU、GPU和TPU运行,适用于从单机器人到大规模并行处理场景。目前存在一些限制,如仅支持软约束、静态形状和特定关节类型等。官方提供了多个应用示例,包括基础逆运动学、碰撞检测、轨迹优化和手部/

机器人控制与仿真应用 1251

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

Spring MVC控制器的好用技巧(注解、请求、请求参数、返回模型、表单验证等)

本期目录 1.使用@Controller构造型 2.实现控制器接口 3.扩展AbstractController类 4.为处理程序方法指定URL映射 5.为处理程序方法指定HTTP请求方法 6.将请求参数映射到处理程序方法 7.返回模型和视图 8.将对象放入模型 9.处理程序方法中的重定向 10.处理表格提交...

文武傲群雄,彳亍定江山 333

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

【SpringMVC源码】源码分析

(1)DispatcherServlet是springmvc中的前端控制器(front controller),负责接收request并将request转发给对应的处理组件。(2)HanlerMapping是springmvc中完成url到controller映射的组件。DispatcherServlet接收request,然后从HandlerMapping查找处理request的controller。

weixin_44823875的博客 1198

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

Java:111-SpringMVC的底层原理(中篇)

如果需要补充,自行补充吧 对应的web.xml: index.jsp: springmvc.xml: entity里面的User类: test类: 自行配置tomcat,启动,运行看看结果,那么我们基础操作搭建完毕,现在我们来操作一下拦截: 在com包下创建Interceptor包,然后创建MyInterceptor类: 其中HandlerInterceptor接口如下: 这三个代表了三个地方,前面的三个拦截,比如: preHandle是第一个拦截,postHandle是第二个拦截,after

qq_59609098的博客 1304

SpringMVC拦截器实战:从原理到多场景应用与性能优化

在Web应用开发中,面向切面编程(AOP)是一种将横切关注点(如日志、权限、性能监控)与核心业务逻辑分离的重要设计思想。其核心原理是通过在程序执行的特定连接点插入增强逻辑,实现代码复用和解耦。这一思想在SpringMVC框架中具体体现为拦截器(Interceptor)技术,它允许开发者在请求处理的生命周期关键节点(如Controller方法执行前后)注入自定义行为,从而显著提升代码的可维护性和架构清晰度。拦截器的技术价值在于实现了公共功能的模块化与可配置化,避免了代码重复。其典型应用场景包括用户身份认证、操

weixin_34344403的博客 344

从Servlet到SpringMVC:DispatcherServlet的进化史与技术抉择

本文深入探讨了从Servlet到SpringMVC的技术演进,重点分析了DispatcherServlet作为SpringMVC核心控制器的设计革新。通过对比传统Servlet开发的局限性,详细解析了DispatcherServlet的分层架构、组件协作机制以及现代Web特性支持,为开发者提供了性能优化与最佳实践建议。

weixin_29281941的博客 594

13个 Spring MVC 顶级技巧!

通常,在Spring MVC中,我们编写一个控制器类来处理来自客户端的请求。然后,控制器调用业务类来处理与业务相关的任务,然后将客户端重定向到逻辑视图名称,该名称由Spring的调度程序Servlet解析,以呈现结果或输出。 这样就完成了典型的请求-响应周期的往返。 今天整理了一下编写Spring MVC控制器的14个技巧,你今天get到了吗?(≧▽≦)/ 1.使用@Controller构造...

知识分享 404

springmvc使用requestmapping无法访问控制类_14 个 Spring MVC 顶级技巧!

通常,在Spring MVC中,我们编写一个控制器类来处理来自客户端的请求。然后,控制器调用业务类来处理与业务相关的任务,然后将客户端重定向到逻辑视图名称,该名称由Spring的调度程序Servlet解析,以呈现结果或输出。这样就完成了典型的请求-响应周期的往返。今天整理了一下编写Spring MVC控制器的14个技巧,你今天get到了吗?(≧▽≦)/1、使用@Controller构造型这是创建可...

weixin_42521186的博客 412

必须掌握的 Spring MVC 顶级技巧

一. Spring MVC 顶级技巧 通常,在Spring MVC中,我们编写一个控制器类来处理来自客户端的请求。然后,控制器调用业务类来处理与业务相关的任务,然后将客户端重定向到逻辑视图名称,该名称由Spring的调度程序Servlet解析,以呈现结果或输出。 这样就完成了典型的请求-响应周期的往返。 今天整理了一下编写Spring MVC控制器的14个技巧,你今天get到了吗? 1.使用@Co...

beeworkshop的博客 233

Spring MVC控制器的14个技巧

通常,在Spring MVC中,我们编写一个控制器类来处理来自客户端的请求。然后,控制器调用业务类来处理与业务相关的任务,然后将客户端重定向到逻辑视图名称,该名称由Spring的调度程序Servlet解析,以呈现结果或输出。这样就完成了典型的请求-响应周期的往返。 1.使用@Controller构造型 这是创建可以处理一个或多个请求的控制器类的最简单方法。仅通过用构造型注释一个类@Controll...

weixin_42590334的博客 242

编写Spring MVC控制器的14个技巧

本期目录 1.使用@Controller构造型 2.实现控制器接口 3.扩展AbstractController类 4.为处理程序方法指定URL映射 5.为处理程序方法指定HTTP请求方法 6.将请求参数映射到处理程序方法 7.返回模型和视图 8.将对象放入模型 9.处理程序方法中的重定向 10.处理表格提交和表格验证 11.处理文件上传 12.在控制器中自动装配业务类 13.访问HttpServ...

ooaaa23的博客 318
上一篇: 将博客搬至优快云
下一篇: JVM配置CMS调优实战笔记
TimerBin
博客等级 码龄12年 54粉丝 119原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值