Tomcat get请求传数组集合参数

前言

最近做项目,需要通过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 errorrequest.protocol().setString(Constants.HTTP_11);// %nn decoding will be checked at the point of decodingString 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, 127if (i < 32 || i == 127) {IS_CONTROL[i] = true;}// Separatorif (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 separatorif (!IS_CONTROL[i] && !IS_SEPARATOR[i] && i < 128) {IS_TOKEN[i] = true;}// Hex: 0-9, a-f, A-Fif ((i >= '0' && i <= '9') || (i >= 'a' && i <= 'f') || (i >= 'A' && i <= 'F')) {IS_HEX[i] = true;}// Not valid for HTTP protocol// "HTTP/" DIGIT "." DIGITif (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.mzph.cn/pingmian/34501.shtml

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

相关文章

iOS Swift5 视频播放

文章目录 1.截图2.代码3.导入本地文件 1.截图 2.代码 import UIKit import AVKit import AVFoundationclass ViewController: UIViewController {override func viewDidLoad() {super.viewDidLoad()view.backgroundColor .white// 创建按钮let button UIButton(type: .syste…

【学习】程序员资源网站

1 书栈网 简介&#xff1a;书栈网是程序员互联网IT开源编程书籍、资源免费阅读的网站&#xff0c;在书栈网你可以找到很多书籍、笔记资源。在这里&#xff0c;你可以根据热门收藏和阅读查看大家都在看什么&#xff0c;也可以根据技术栈分类找到对应模块的编程资源&#xff0c;…

通过InoDriverShop伺服调试软件连接汇川SV660F系列伺服的具体方法(以太网)

通过InoDriverShop伺服调试软件连接汇川SV660F系列伺服的具体方法(以太网) 具体连接伺服驱动器的步骤可参考以下内容: 启动InoDriverShop, 新建或打开工程 如下图所示,选择在线,选中SV660F图标,右侧通信类型选择“TCP—DCP”,点击下一步,同时要选择自己当前使用的网卡…

【扩散模型(一)】Stable Diffusion中的重建分支(reconstruction branch)和条件分支(condition branch)

Stable Diffusion 是一种基于扩散模型的生成模型&#xff0c;用于生成图像等数据。在解释 Stable Diffusion 的过程中&#xff0c;经常会提到两个主要的分支&#xff1a;重建分支&#xff08;reconstruction branch&#xff09;和条件分支&#xff08;condition branch&#xf…

C++多线程异步日志实现

使用C11标准&#xff0c;构建了一个方便使用的、轻量化的日志系统。封装线程安全的lockQueue&#xff0c;实现对每条日志添加信息、push到lockQueue中的LogTmp类&#xff0c;实现一个多线程异步的日志系统Logger。 lockqueue.h #pragma once #include <queue> #include…

Go微服务: redis分布式锁保证数据原子操作的一致性

概述 随着云计算和大数据技术的飞速发展&#xff0c;分布式系统已经成为现代IT架构的重要组成部分在分布式系统中&#xff0c;数据的一致性是一个至关重要的挑战&#xff0c;特别是在并发访问和修改共享资源的场景下分布式锁是一种跨进程、跨机器节点的互斥锁&#xff0c;用于…

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

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

08.QT控件:QWidget

一、Widget 简介 Widget 是 Qt 中的核⼼概念.。英⽂原意是 "小部件"&#xff0c;我们此处也把它翻译为 "控件"。控件是构成⼀个图形化界⾯的基本要素。 Qt 作为⼀个成熟的 GUI 开发框架, 内置了⼤量的常⽤控件。并且 Qt 也提供了 "⾃定义控件" 的…

《第一行代码 第3版》学习笔记——第十一章 网络技术

1 webview用法 class MainActivity : ComponentActivity() {SuppressLint("SetJavaScriptEnabled")override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {NetWorkDemoTheme {// A surface container using the bac…

主流MQ对比和选型

在以下几个我们比较关心的维度进行对比 ActiveMQ RabbitMQ RocketMQkafka官网https://activemq.apache.org/https://www.rabbitmq.com/https://rocketmq.apache.org/https://kafka.apache.org/githubhttps://github.com/apache/activemqhttps://github.com/rabbitmqhttps://g…

AI如何让办公更智能?WPS AI海外版给出答案

导读&#xff1a;从语义检查到一键生成PPT&#xff0c;WPS Office海外版如何面向2亿月活用户快速推出AI功能&#xff1f; 近日&#xff0c;WPS Office海外版应用亚马逊云科技Amazon Bedrock等生成式AI技术与服务&#xff0c;在海外正式推出人工智能应用WPS AI海外版&#xff0c…

Postman测试,如何保持用户登录状态?

为了在Postman中保持用户登录状态&#xff0c;我们可以使用以下步骤&#xff1a; 1. 下载和安装Postman 首先&#xff0c;我们需要下载和安装Postman。Postman是一个流行的API开发和测试工具&#xff0c;可以帮助我们发送HTTP请求并测试API的功能。 2. 创建一个新的Postman …

【Vue】vue-router路由使用

前言 Vue Router是Vue框架中非常重要的一个功能。 目标 1 单页面应用与多页面应用的区别; 2 vue-router的具体实现方法; 3 路由模式有哪几种,有什么区别; 4 如何进行路由守卫与路由缓存; 一 路由的概念 概念 Vue Router是Vue提供的路由管理器。将组件与路由一一对应起来,…

6-2 归并排序

6-2 归并排序 分数 10 全屏浏览 切换布局 作者 软件工程DS&A课程组 单位 燕山大学 以下代码采用分而治之算法实现归并排序。请补充函数mergesort&#xff08;&#xff09;的代码。提示&#xff1a;mergesort&#xff08;&#xff09;函数可用递归实现&#xff0c;其中参…

Conda创建与激活虚拟环境(指定虚拟环境创建位置)

1.Conda优势 Conda是一个开源的软件包管理系统和环境管理系统&#xff0c;主要用于在不同的计算环境中安装和管理软件包和其依赖项。它最初是为Python而设计的&#xff0c;但现在也可以用于管理其他语言的软件包。 Conda提供了对虚拟环境的支持&#xff0c;这使得用户可以在同…

如何在Java中处理UnsupportedOperationException异常?

如何在Java中处理UnsupportedOperationException异常&#xff1f; 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01; 在Java编程中&#xff0c;我们经常会遇到各…

swiper实例

大家好&#xff0c;我是燐子&#xff0c;今天给大家带来swiper实例 微信小程序中的 swiper 组件是一种用于创建滑动视图的容器组件&#xff0c;常用于实现图片轮播、广告展示等效果。它通过一系列的子组件 swiper-item 来定义滑动视图的每一个页面。 基本用法 以下是一个简单的…

ESAPI.setAttribute设置值前端取不到

我在后端使用java设置email request.setAttribute("email",ESAPI.encoder().encodeForHTML("123456qq.com"))前端jsp页面获取不到&#xff0c; var email"<%ESAPI.encoder().encodeForHTML(request.getParameter("email"))%>"…

web前端——HTML

目录 一、HTML概述 1.HTML是什么&#xff1f; 2.HTML具体化解释 二、HTML基本语法 1.声明 2. Head头标签 3.body身体标签 4.一个html的基本结构 5.标签 6.标签属性 ①属性的格式 ②属性的位置 ③添加多个属性 三、基本常用标签 1.超链接 2.图像标签 ①图像标…

springboot集成JPA并配置hikariCP连接池问题解决

一、引入需要的依赖 springboot版本 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-parent</artifactId><version>2.3.2.RELEASE</version><relativePath/></parent> jpa依赖 <!--…