Tomcat get请求传数组集合参数

news2024/11/16 17:46:26

前言

最近做项目,需要通过GET传参,来实现查询的能力,本来是RPC调用,直接参数序列化即可。但是服务最近修改为HTTP,本来Spring Cloud的feign也可以直接传参数,但是当使用Nginx访问时参数到底传啥呢,笔者传入?list=['xxx']直接就报错了,错误类型

Invalid character found in the request target [/haha?list=[%27haha%27] ]. The valid characters are defined in RFC 7230 and RFC 3986

准备demo

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.7.18</version>
        </dependency>

demo随意写,写个springboot项目,实际上跟servlet容器有关,Boot默认Tomcat,其他servlet容器可能实现标准不一样而不同。

    @GetMapping("/haha")
    public String sayHello(List<String> list){
        return list.toString();
    }

如果我们自己实现http get,实际上就是针对url的参数解析,说不定?list=['xxx']就不会报错,追根溯源--标准的实现区别。

日志如下

源码分析

查询资料:RFC 7230: Hypertext Transfer Protocol (HTTP/1.1): Message Syntax and Routingicon-default.png?t=N7T8https://www.rfc-editor.org/rfc/rfc7230

    这个标准定义了400状态码http 1.1协议的一些规则

RFC 3986: Uniform Resource Identifier (URI): Generic Syntax icon-default.png?t=N7T8https://www.rfc-editor.org/rfc/rfc3986     定义了保留字符

   

很不幸[]属于保留字符。

从Tomcat的源码看org.apache.coyote.http11.Http11InputBuffer的parseRequestLine方法。

} else if (parsingRequestLineQPos != -1 && !httpParser.isQueryRelaxed(chr)) {
                    // Avoid unknown protocol triggering an additional error
                    request.protocol().setString(Constants.HTTP_11);
                    // %nn decoding will be checked at the point of decoding
                    String invalidRequestTarget = parseInvalid(parsingRequestLineStart, byteBuffer);
                    throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget", invalidRequestTarget));
}

逐个字符读取。发现是保留字符则根据标准抛出异常,提示根据什么标准。

 在org.apache.tomcat.util.http.parser.HttpParser的静态代码块中,定义了通常不允许的字符。


    static {
        for (int i = 0; i < ARRAY_SIZE; i++) {
            // Control> 0-31, 127
            if (i < 32 || i == 127) {
                IS_CONTROL[i] = true;
            }

            // Separator
            if (i == '(' || i == ')' || i == '<' || i == '>' || i == '@' || i == ',' || i == ';' || i == ':' ||
                    i == '\\' || i == '\"' || i == '/' || i == '[' || i == ']' || i == '?' || i == '=' || i == '{' ||
                    i == '}' || i == ' ' || i == '\t') {
                IS_SEPARATOR[i] = true;
            }

            // Token: Anything 0-127 that is not a control and not a separator
            if (!IS_CONTROL[i] && !IS_SEPARATOR[i] && i < 128) {
                IS_TOKEN[i] = true;
            }

            // Hex: 0-9, a-f, A-F
            if ((i >= '0' && i <= '9') || (i >= 'a' && i <= 'f') || (i >= 'A' && i <= 'F')) {
                IS_HEX[i] = true;
            }

            // Not valid for HTTP protocol
            // "HTTP/" DIGIT "." DIGIT
            if (i == 'H' || i == 'T' || i == 'P' || i == '/' || i == '.' || (i >= '0' && i <= '9')) {
                IS_HTTP_PROTOCOL[i] = true;
            }

            if (i >= '0' && i <= '9') {
                IS_NUMERIC[i] = true;
            }

            if (i >= 'a' && i <= 'z' || i >= 'A' && i <= 'Z') {
                IS_ALPHA[i] = true;
            }

            if (IS_ALPHA[i] || IS_NUMERIC[i] || i == '+' || i == '-' || i == '.') {
                IS_SCHEME[i] = true;
            }

            if (IS_ALPHA[i] || IS_NUMERIC[i] || i == '-' || i == '.' || i == '_' || i == '~') {
                IS_UNRESERVED[i] = true;
            }

            if (i == '!' || i == '$' || i == '&' || i == '\'' || i == '(' || i == ')' || i == '*' || i == '+' ||
                    i == ',' || i == ';' || i == '=') {
                IS_SUBDELIM[i] = true;
            }

            // userinfo = *( unreserved / pct-encoded / sub-delims / ":" )
            if (IS_UNRESERVED[i] || i == '%' || IS_SUBDELIM[i] || i == ':') {
                IS_USERINFO[i] = true;
            }

            // The characters that are normally not permitted for which the
            // restrictions may be relaxed when used in the path and/or query
            // string
            // 明确定义通常不允许使用的字符,当然也可以放开限制,当为路径或者查询参数
            if (i == '\"' || i == '<' || i == '>' || i == '[' || i == '\\' || i == ']' || i == '^' || i == '`' ||
                    i == '{' || i == '|' || i == '}') {
                IS_RELAXABLE[i] = true;
            }
        }

        DEFAULT = new HttpParser(null, null);
    }

至此我们知道了数组和集合使用get传参报错的原因:明确定义通常不允许使用的字符,当然也可以放开限制,当为路径或者查询参数。 

解决办法

解决办法也简单了,解析参数规避'[' ']'这样的字符即可,比如string默认tolist,使用string,string传list或者数组对象等,解析规则实际上我们甚至可以自定义,参考Tomcat或者springboot的规则。

比如使用字符串:实际上springboot就是这么做的,tomcat毕竟GET仅传递String字段,各种参数类型都是后面Springboot转换的

 如果使用List直接注入,那么因为没有构造函数,报错,毕竟接口类型,反射无法初始化对象。

因为Springboot会反射解析对象,所以即使使用ArrayList也不能解析参数,因为默认情况仅能解析String

 只有@RequestParam绑定参数key才行

因为解析器不一样,具体就不分析了,涉及Springmvc的设计。

 

数组或者集合使用,分割。

 

实际上还有其他办法:我们可以放开限制,只需要注入HttpParser即可

在tomcat协议定义里面org.apache.coyote.http11.AbstractHttp11Protocol

定义了

那么我们只要注入这2个值即可,第一个是路径字符,第2个是查询字符,根据最小修改原则,此处注入查询参数。根据Springboot自动装配org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration的属性注入即可

 application.properties文件

server.tomcat.relaxedQueryChars=<,>,[,\,],^,`,{,|,}

即可实现放开限制

 

但是tomcat认为'[' ']'是字符串的字符,以,逗号分割。

 

并不符合我们的直观感受,所以还是一个原则,只不过是允许'[' ']'这样的字符了。  

总结

通过tomcat GET传参数,尤其是数组或者集合,发现tomcat实现了很多开源标准,预估其他servlet容器也差不多,如果是我们自己实现servlet容器解析http协议包,那么这些标准估计是不会去实现的,开源的能力定义了一系列标准并且基本上都实现了。而Springboot在tomcat标准的基础上转换了各种类型,实现了方便快速的开发。

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

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

相关文章

微软将停用 App Center:React Native 开发人员应该使用以下工具

再见,应用中心!你好,EAS + Sentry + GitHub Actions! React Native 开发人员大遗憾:微软宣布将于 2025 年 3 月 31 日关闭Visual Studio App Center。 多年来, Infinite Red一直将 App Center 作为许多 React Native 咨询客户的 CI/CD 流程和开发工作流程的关键部分。它有…

零基础MySQL完整学习笔记

零基础MySQL完整学习笔记 1. 基础操作(必须会!)1.1 修改密码(4种方法)1.2 创建新用户1.3 导入数据库 2. SQL四种语言介绍2.1 DDL(数据库定义语言)2.2 DML(数据操纵语言)2.3 DCL(数据库控制语言)2.4 TCL(事务控制语言) 3. 数据库操作3.1 创建数据库3.2 查询数据库3.3 删除数据库…

SpringCloud分布式微服务链路追踪方案:Zipkin

创作博客的目的是希望将自己掌握的知识系统地整理一下&#xff0c;并以博客的形式记录下来。这不仅是为了帮助其他有需要的人查阅相关内容&#xff0c;也是为了自己能够更好地巩固和加深对这些知识的理解。创作的时候也是对自己所学的一次复盘和总结&#xff0c;在创作的过程中…

python爬虫需要什么HTTP代理?

用来爬虫的话&#xff0c;还是建议用高匿名代理&#xff0c;但显然题主用了高匿名代理还是出现了一部分问题&#xff0c;我们可以先找到问题关键再解决它&#xff0c;一般爬虫用了高匿名代理出现被封会有以下几种原因&#xff1a; 1.代理IP的质量不过关 一般来说每个网站都有…

AI写文章生成器,这些工具都可以一键智能生成文章

在AI技术快速发展的今天&#xff0c;AI写作生成器成为我们创作内容的重要工具&#xff0c;它可以提高我们的写作效率&#xff0c;节省时间和精力。下面小编就来和大家分享几款优秀的AI写作生成器&#xff0c;帮助你快速生成高质量的文章。 1.专业AI写作工具-文章在线生成器 专…

ChatGPT对那些带有残疾迹象的简历有偏见——但它可以改善

ChatGPT对那些带有残疾迹象的简历有偏见——但它可以改善 去年&#xff0c;华盛顿大学(University of Washington)研究生凯特•格拉兹科(Kate Glazko)在寻找研究实习机会时注意到&#xff0c;招聘人员在网上发布消息称&#xff0c;他们使用OpenAI的ChatGPT和其他人工智能工具来…

如何模拟一个具有网络管理功能的被测件的一些思路

不知道大家有没有遇到过这个问题&#xff1f; 当我们在学习如何测试网络管理时&#xff0c;难题不在于如何编写测试脚本&#xff0c;而是编写完测试脚本后&#xff0c;没有真实被测件来让我们执行测试脚本&#xff0c;进而调试脚本。这也是我在给大家讲CANoe工具和CAPL编程语言…

Android Media Framework(八)OMXNodeInstance - Ⅰ

OpenMAX框架的学习有两大难点&#xff0c;一是组件的状态切换与buffer的流转过程&#xff0c;这部分内容我们已经在IL Spec中学习过了&#xff1b;二是OMX组件使用的buffer类型与buffer分配过程&#xff0c;这一节我们来重点剖析OMX组件使用的buffer类型。 1、引言 在实际应用…

我原以为政务类网站不追求漂亮,打脸啦,漂亮得颠覆你认知。

我原本以为政务类网站一定时沉稳、工整、信息量大的&#xff0c;这些和漂流都关联不上&#xff0c;直到最近看了一些网站&#xff0c;发现我的认识狭隘了。 政务类网站的设计风格通常需要注重以下几个方面&#xff1a; 稳重和专业感&#xff1a; 政务类网站需要给人以稳重、正…

c++分隔字符串

可以使用getline函数。 有两个版本&#xff1a; 至于为什么可以使用getline函数返回值作为while的判断条件&#xff0c;cprimer中表述如下&#xff1a;

51-60 CVPR 2024 最佳论文 | Generative Image Dynamics

在2023年11月&#xff0c;谷歌研究院发布了一项令人瞩目的研究成果——Generative Image Dynamics&#xff08;生成图像动力学&#xff09;。这项技术的核心是将静态的图片转化为动态的、无缝循环的视频&#xff0c;而且更令人兴奋的是&#xff0c;这些生成的视频还具有交互性。…

【Web APIs】JavaScript 事件基础 ② ( “ 事件 “ 开发步骤 | 常见鼠标 “ 事件 “ )

文章目录 一、" 事件 " 开发步骤1、" 事件 " 开发步骤2、完整代码示例 二、常见鼠标 " 事件 "1、常见鼠标 " 事件 "2、鼠标 " 事件 " 代码示例 Web APIs 博客相关参考文档 : WebAPIs 参考文档 : https://developer.mozilla…

计算机组成原理 | CPU子系统(1)基本概述

基本结构模型 运算与缓存部件 数据寄存部件 PSW不是很清楚 存储器是什么&#xff1f;属于那个结构里&#xff1f; 时序处理部件 cpu是大脑&#xff0c;控制器是神经元 ①通过硬件产生控制信号 ②通过软件产生控制信号 外频&#xff08;系统时钟信号&#xff09;&#xff0c;…

Springboot整合cxf进行WebService发布和WebService调用

import javax.jws.WebMethod; import javax.jws.WebParam; import javax.jws.WebResult; import javax.jws.WebService; /** 测试接口 author Μr.ηobοdy date 2019-12-29 */ WebService(name “UserService”, // 暴露服务名称 targetNamespace “http://servic…

后端数据null前端统一显示成空

handleNullValues方法在封装请求接口返回数据时统一处理 // null 转 function handleNullValues(data) {// 使用递归处理多层嵌套的对象或数组function processItem(item) {if (Array.isArray(item)) {return item.map(processItem);} else if (typeof item object &&…

深入解析 Python dataclass:类属性与类方法解释

文章目录 dataclass实例属性和类属性自动设置属性 实例方法静态方法&#xff08;staticmethod&#xff09;和 类方法&#xff08;classmethod&#xff09;静态方法类方法 dataclass dataclass 是 Python 3.7 引入的一个装饰器&#xff0c;用于简化类的定义。 使用 dataclass …

Unity的ScrollView滚动视图复用

发现问题 在游戏开发中有一个常见的需求&#xff0c;就是需要在屏幕显示多个&#xff08;多达上百&#xff09;显示item&#xff0c;然后用户用手指滚动视图可以选择需要查看的item。 现在的情况是在100个data的时候&#xff0c;Unity引擎是直接创建出对应的100个显示item。 …

0801功率放大问题

3个学时讲一个电路&#xff08;两个共集共集并联&#xff09;&#xff0c;4个问题&#xff0c;发展线索 丙类放大电路用在高频通讯行业&#xff0c;低频功放是甲类&#xff0c;乙类&#xff0c;甲乙类 PT三极管的损耗 Pv电源提供的功率 现代模电通常使用方法b 只有交流…

聊聊 golang 中 channel

1、引言 Do not communicate by sharing memory; instead, share memory by communicating Golang 的并发哲学是“不要通过共享内存进行通信&#xff0c;而要通过通信来共享内存”&#xff0c;提倡通过 channel 进行 goroutine 之间的数据传递和同步&#xff0c;而不是通过共享…

YashanDB为新质生产力赋能 灌注合肥区域转型源动力

当前&#xff0c;数据要素已成为我国数字经济的“核心引擎”与“关键生产要素”&#xff0c;为全面激发数据要素的价值&#xff0c;各地区正积极探索数据要素交易平台的可行模式&#xff0c;加快在数据要素领域的布局。近日&#xff0c;深圳计算科学研究院崖山数据库系列产品受…