当前位置: 首页 > news >正文

Feign踩坑源码分析 -- 请求参数分号变逗号

一.案例

  1.1.Post请求:

http://localhost:8250/xx/task/test
json格式参数:
{"string": "a;b;c;d"
}

  1.2.controller代码:

    @AutowiredDataSourceClientService dataSourceClientService;@RequestMapping("/test")@ResponseBodypublic void test(@RequestBody String string) {System.out.println(string);String result = dataSourceClientService.test(string);System.out.println(result);}

  1.3.feign代码:

@FeignClient( value = "zz")
public interface DataSourceClientService {@RequestMapping(value = "/dataSource/test",method = RequestMethod.POST,produces = "text/plain;charset=UTF-8")String test(@RequestParam("str") String str);
}

  1.4服务提供方代码:

    @RequestMapping("/test")@ResponseBodypublic String test(@RequestParam String str) {System.out.println(str);String result = "success;";return result;}

  1.5发起请求后控制台打印结果:

请求方控制台:用户[null]开始调用web接口:http://localhost:8250/xx/task/test
{"string": "a;b;c;d"
}服务提供方控制台:
用户[null]开始调用web接口:http://localhost:8247/zz/dataSource/test
{"string": "a,b,c,d"
}

二.解决办法

  2.1.在请求方对参数进行编码:

        string = UriUtils.encode(string, StandardCharsets.UTF_8);String test = dataSourceClientService.test(string);

   2.2.服务提供方@RequestParam改成@RequestBody;

三.分析

   3.1.需求

  服务提供方需要的字符串是包含“;”而不是“,”,因为实际传递的是一个JSONObject的字符串,导致JSONUtil.parse转换成对象失败,导致业务失败;

   3.2请求方法参数@RequestParam而传参不加密

     3.2.1 请求方源码探究

   通过一步步debug代码调试,发现调用 dataSourceClientService.test(string) 是jdk动态代理,调用链接到

    feign.ReflectiveFeign.FeignInvocationHandler#invoke

    -->feign.SynchronousMethodHandler#invoke

    -->feign.ReflectiveFeign.BuildTemplateByResolvingArgs#create

    -->feign.ReflectiveFeign.BuildTemplateByResolvingArgs#resolve

    -->feign.RequestTemplate#resolve(java.util.Map<java.lang.String,?>)

    -->feign.template.QueryTemplate#expand

    -->feign.template.QueryTemplate#queryString

 static final String COLLECTION_DELIMITER = ";"; private String queryString(String name, String values) {if (this.pure) {return name;}/* covert the comma separated values into a value query string */List<String> resolved = Arrays.stream(values.split(COLLECTION_DELIMITER)).filter(Objects::nonNull).filter(s -> !UNDEF.equalsIgnoreCase(s)).collect(Collectors.toList());if (!resolved.isEmpty()) {return this.collectionFormat.join(name, resolved, this.getCharset()).toString();}/* nothing to return, all values are unresolved */return null;}

   从上面可以看到,一个参数字符串“a;b;c;d”被分割处理成了一个数组,然后重新组合成字符串(Collection)传递给服务提供方,服务提供方接收参数是一个string字符串,会用逗号给拼接成字符串。

  也就是说,feign预设了使用者如果是用传参带了分号过来的,会认为你传的是Collection,而不是String。

    3.2.1 服务提供方源码探究

  下面是服务提供方接收的参数详情:  

org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver#resolveArgument

   从上面可以看出,请求解析出来是一个字符串数组,在

org.springframework.core.convert.support.CollectionToStringConverter#convert 中用逗号拼接起来:

    3.2.1 调用服务提供方方法前参数编码:

    当没有编码前,org.springframework.core.convert.support.GenericConversionService#convert(java.lang.Object, org.springframework.core.convert.TypeDescriptor, org.springframework.core.convert.TypeDescriptor) 调用链进入的是org.springframework.core.convert.support.CollectionToStringConverter#convert 中用逗号拼接起来,因为参数类型是字符串数组

   当编码后,进入的是org.springframework.core.convert.support.GenericConversionService.NoOpConverter#convert,直接返回字符串:

  参数值的获取在org.springframework.web.method.annotation.RequestParamMethodArgumentResolver#resolveName 中

   参数的解码方法在org.apache.tomcat.util.http.Parameters#processParameters(byte[], int, int, java.nio.charset.Charset)中进入

   在org.apache.tomcat.util.http.Parameters#urlDecode进行解码

   前面的是参数进行编码后解码,因为编码成一个字符串,所以解码的也是字符串,当没有进行编码,传递的会认为是一个collection,所以会遍历解码:

   然后加入到数组中:

  3.3请求方法参数改成@RequestBody

   3.3.1服务提供方

  当请求方法是@RequestBody时,获取参数在org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor#resolveArgument:

   直接从httprequest请求中获取body参数值:org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver#readWithMessageConverters(org.springframework.http.HttpInputMessage, org.springframework.core.MethodParameter, java.lang.reflect.Type)

  3.3.2请求方

  参数直接封装到body中:

  feign.ReflectiveFeign.BuildTemplateByResolvingArgs#create

  --> feign.ReflectiveFeign.BuildEncodedTemplateFromArgs#resolve

    对比3.2.1流程会发现,body不会进行字符串的切割,当然拉,也跟参数的请求类型不一致有关,一个是query,一个是body,下面是3.2.1流程

    -->feign.ReflectiveFeign.BuildTemplateByResolvingArgs#create

    -->feign.ReflectiveFeign.BuildTemplateByResolvingArgs#resolve

    -->feign.RequestTemplate#resolve(java.util.Map<java.lang.String,?>)

    -->feign.template.QueryTemplate#expand

    -->feign.template.QueryTemplate#queryString

四.扩展 

  4.1.get请求

  get请求的参数类型是query,所以走的是3.2.1流程,所以string的传参中包含“;”也会被转换成“,”; 

@FeignClient( value = "zz")
public interface DataSourceClientService {@RequestMapping(value = "/dataSource/testGet",method = RequestMethod.GET,produces = "text/plain;charset=UTF-8")String testGet(@RequestParam("str") String str);}@RequestMapping(value = "/testGet")@ResponseBodypublic String testGet(String str) {System.out.println(str);String result = "success";return result;}
服务提供方控制台输出:
用户[null]开始调用web接口:http://localhost:8247/zz/dataSource/testGet
a,b,c,d

  ps:当请求参数字符串存在特殊符号比如“+”时,会被转义为空格

http://localhost:8250/xx/task/test2?string=a+b请求方控制台输出:
用户[null]开始调用web接口:用户[null]开始调用web接口:http://localhost:8250/xx/task/test2
a b

  通过debug调试发现当发起请求时在org.springframework.web.method.support.InvocableHandlerMethod#invokeForRequest中获取参数:

   继续调用链:

  -->org.springframework.web.method.support.HandlerMethodArgumentResolverComposite#resolveArgument

  -->org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver#resolveArgument

  -->org.springframework.web.method.annotation.RequestParamMethodArgumentResolver#resolveName

  -->org.springframework.web.context.request.ServletWebRequest#getParameterValues

  -->org.apache.catalina.connector.RequestFacade#getParameterValues

  -->org.apache.catalina.connector.Request#getParameterValues

  -->org.apache.coyote.Request#getParameters

   从上面可以得知参数在parameters中,并且特殊符号已经被处理过了,那接下来找下什么时候放进来和怎么被处理的;在org.apache.catalina.connector.Request中看见了parseParameters方法,可以看出是对参数的解析方法,在里面打下断点重新跑下:

   从下面可以看出这里是真正进行参数解析处理的:

   org.apache.tomcat.util.http.Parameters#handleQueryParameters:

   -->org.apache.tomcat.util.http.Parameters#processParameters(org.apache.tomcat.util.buf.MessageBytes, java.nio.charset.Charset)

  -->org.apache.tomcat.util.http.Parameters#processParameters(byte[], int, int, java.nio.charset.Charset)

  -->org.apache.tomcat.util.http.Parameters#urlDecode

   从上面可以看到,是在进行解码时将“+”去除掉的;

  继续debug发现在下面的代码中,可以清晰的看到对“+”替换成空格;

  -->org.apache.tomcat.util.buf.UDecoder#convert(org.apache.tomcat.util.buf.ByteChunk, boolean)

  解决办法为:

  1)对参数“a+b”进行编码;

  2)改成post请求;

  4.2.header参数 

  在网上看见一篇文章,在这里收藏下《FeignClient传入的header中带逗号引发的401问题》

相关文章:

Feign踩坑源码分析 -- 请求参数分号变逗号

一.案例 1.1.Post请求&#xff1a; http://localhost:8250/xx/task/test json格式参数&#xff1a; {"string": "a;b;c;d" } 1.2.controller代码&#xff1a; AutowiredDataSourceClientService dataSourceClientService;RequestMapping("/test"…...

nginx通用history模式刷新

注:1.通用配置只支持二段路由,二段及以上依然需要单独进行配置 2.所有location后面的路径,都需要使用通配符进行配置 location ^~ /phdp/ {try_files $uri $uri/ /phdp/index.html;index ruoyi.html index.html index.htm;}location ^~ /phdp-api/ {client_max_body_size 20m;p…...

Linux系统安装:Zookeeper

目录 Zookeeper的安装 1、环境准备 2、上传 3、解压文件到opt/zookeeper目下 4、安装完后进入zookeeper&#xff0c;找到conf目录 5、复制zoo_sample.cfg 6、编辑zoo.cfg 7、复制一份会话&#xff0c;进入zookeeper安装目录&#xff0c;创建一个文件夹zkdata&#xff0…...

cocos2dx+lua学习笔记:UIPageView的使用

前言 本篇在讲什么 本篇简单介绍Lua篇cocos2dx中UIPageView的相关内容 仅介绍简单的应用&#xff0c;仅供参考 本篇适合什么 适合初学Cocos2dX的小白 适合想要在Cocos2dx-lua中使用UIPageView的人 本篇需要什么 对Lua语法有简单认知 对Cocos2dx-Lua有简单认知 Cocos2…...

MyBatis常见面试题汇总(超详细回答)

目录 1.什么是Mybatis&#xff1f; 2.Mybatis的优缺点&#xff1f; 3.#{} 和 ${} 的区别是什么&#xff1f; 4.xml 映射文件中有哪些标签&#xff1f; 5.模糊查询 like 语句该怎么写? 6.Mapper 接口的工作原理是什么&#xff1f;Mapper 接口里的方法&#xff0c;参数不同…...

Jvm调优实战笔记

一、基础命令jps 查看所有java进程jinfo 进程号 查看该线程相关信息3、jstat 统计信息&#xff08;数据跟踪信息&#xff09;jstat -gc 进程号 查看该线程在内存中每一块占用的大小jstat -gc 进程号 时间&#xff08;毫秒&#xff09; 更新频率4、jstack 跟踪线程jstack 进程号…...

JVM 全面了解

JVM包含两个子系统和两个组件&#xff0c;两个子系统为Class loader(类装载器)、Execution engine(执行引擎)&#xff1b;两个组件为Runtime data area(运行时数据区)、Native Interface(本地接口)。 方法区&#xff1a;存储已被虚拟机加载的类元数据信息(元空间) 堆&#xf…...

阿里开源自研高性能核心搜索引擎 Havenask

去年12月&#xff0c;阿里开源了自研的大规模分布式搜索引擎 Havenask&#xff08;内部代号 HA3&#xff09;。 &#xfeff; Havenask 是阿里巴巴内部广泛使用的大规模分布式检索系统&#xff0c;支持了淘宝、天猫、菜鸟、优酷、高德、饿了么等在内整个阿里的搜索业务&#…...

nginx日志服务之敏感信息脱敏

1. 创建实验资源 开始实验之前&#xff0c;您需要先创建实验相关资源。 日志服务之敏感信息脱敏与审计 2. 创建原始数据 本步骤将指导您如何创建NGINX模拟数据。 双击打开虚拟桌面的Firefox ESR浏览器。 在RAM用户登录框中单击下一步&#xff0c;并复制粘贴页面左上角的子…...

【uni-app教程】一、UniAPP 介绍

一、UniAPP 介绍 (1) 什么是 UniAPP? uni-app 是一个使用 Vue.js 开发所有前端应用的框架&#xff0c;开发者编写一套代码&#xff0c;可发布到 iOS&#xff0c;Android&#xff0c;HS&#xff0c;以及各种小程序&#xff08;微信/支付宝/百度/头条/QQ/钉钉》等多个平台&#…...

Splunk Real-time Search 的研究

最近客户想搞清Splunk real-time search 和related search 有啥区别,想两个都试试,看看效果。 为了更好的说明什么是real-time search, 先看一下: With real-time searches and reports, you can search events before they are indexed and preview reports as the event…...

SWM181 串口功能使用介绍

SWM181 串口功能使用介绍&#x1f4cc;SDK固件包&#xff1a;https://www.synwit.cn/kuhanshu_amp_licheng/✨注意新手谨慎选择作为入门单片机学习。&#x1f33c;开发板如下图&#xff1a; &#x1f4cb;SWM181描述上写了有4个串口&#xff0c;在数据手册上&#xff0c;将引脚…...

Stochastic Approximation 随机近似方法的详解之(三)Dvoretzky’s convergence theorem

定理内容 Theorem 6.2 (Dvoretzky’s Theorem). Consider a stochastic process wk1(1−αk)wkβkηkw_{k1}\left(1-\alpha_k\right) w_k\beta_k \eta_kwk1​(1−αk​)wk​βk​ηk​, 其中{αk}k1∞,{βk}k1∞,{ηk}k1∞\{\alpha_k\}^\infty_{k1},\{\beta_k\}^\infty_{k1},\…...

7个ES6解构技巧让代码更简洁

您是否厌倦了编写臃肿且难以阅读的代码&#xff1f;想要提升您的编码技能并使您的代码更具可读性和简洁性&#xff1f; 从解构对象和数组到使用默认值和展开运算符&#xff0c;我们将涵盖所有内容&#xff0c;现在&#xff0c;我们将准备好掌握干净简洁的编码艺术。 1.解构对…...

曾经被人们看成是异想天开的产业互联网,或许终将会实现

一波还未平息&#xff0c;一波又起。元宇宙的热度还未彻底散去&#xff0c;ChatGPT已经成为了名符其实的新风口。如果用一个概念来定义现在这样一个热点和风口频出的时代的话&#xff0c;我想&#xff0c;用产业互联网或许是再合适不过的了。对此&#xff0c;可能有人并不认同。…...

log4j控制台不打印日志的故障解决方案

前言 接管了别的项目组的一个代码&#xff0c;在IDAE调试程序的过程中&#xff0c;发现log4j日志居然没有打印在控制台上&#xff0c;日志相关代码也没有问题。 在网上搜索了一圈&#xff0c;总结了一下个人解决这个问题的流程。 流程 1. 判断用了什么配置文件 不知道是出…...

C# 序列化时“检测到循环引用”错误的彻底解决方案

目录 一&#xff0c;问题表现 二、没有技术含量的解决方案 三、本人彻底的解决方案 简要说明 贴代码 思路解析 思路 一&#xff0c;问题表现 示例代码如下&#xff1a; [Serializable] public class NodeTest {public NodeTest (){new List<NodeTest> ();}p…...

小红书“复刻”微信,微信“内造”小红书

配图来自Canva可画 随着互联网增长红利逐渐见顶&#xff0c;各大互联网平台对流量的争夺变得愈发激烈。而为了寻找新的业务可能性&#xff0c;各家都在不遗余力地拓宽自身边界。在此背景下&#xff0c;目前最为“吸睛”和“吸金”的社交、电商、种草、短视频等领域&#xff0c…...

用arthas轻松排查线上问题

你是否在项目中会碰到以下一些问题&#xff1a; 在代码中打印各种日志来排查&#xff0c;比如方法的入参&#xff0c;出参&#xff0c;及在方法体中打印日志判断走哪行代码还有你觉得代码没问题&#xff0c;可是运行出现却是以前的bug&#xff0c;感觉代码没修改&#xff0c;或…...

mysql一explain结果分析

1. EXPLAIN简介 使用EXPLAIN关键字可以模拟优化器执行SQL查询语句&#xff0c;从而知道MySQL是如何处理你的SQL语句的。分析你的查询语句或是表结构的性能瓶颈。 ➤ 通过EXPLAIN&#xff0c;我们可以分析出以下结果&#xff1a; 表的读取顺序数据读取操作的操作类型哪些索引可…...

原理底层计划--HashMap

HashMap 之前写了“Java集合TreeMap红黑树一生只爱一次”&#xff0c;说到底还是太年轻了&#xff0c;Map其实在排序中应用比较少&#xff0c;一般追求的是速度&#xff0c;通过HashMap来获取速度。hashmap 调用object hashcode方法用于返回对象的哈希码&#xff0c;主要使用在…...

win10 设备管理器中的黄色感叹号(华硕)

目录一、前言二、原因三、方案四、操作一、前言 打开设备管理器&#xff0c;我们可以看到自己设备的信息&#xff0c;但是在重装系统后&#xff0c;你总会在不经意间发现。咦&#xff0c;怎么多了几个感叹号&#xff1f;&#xff1f;&#xff1f; 由于我已经解决该问题&#…...

新产品上市推广不是“铺货”上架

只有不断推出新产品的企业才能走得长远&#xff0c;但现实中往往有很多企业投入了大量人力、物力、财力研发的新产品却在推广的过程中屡屡受挫。那么&#xff0c;为什么适合市场的新产品会在市场营销推广的过程中夭折呢&#xff1f;小马识途营销顾问分析有如下几点&#xff1a;…...

MATLAB训练神经网络小结

MATLAB训练神经网络小结1、一个典型例子1.1 可视化神经网络1.2 指定某一层的激活函数1.3 训练神经网络时使用L1正则化1.4返回训练过程中的参数1.5 查看训练好的权重系数1.6 如何使用早停法来防止过拟合1、一个典型例子 例如输入特征为10维&#xff0c;想训练一个10x20x10x1的三…...

实战:一天开发一款内置游戏直播的国产版Discord应用【附源码】

游戏直播是Discord产品的核心功能之一&#xff0c;本教程教大家如何1天内开发一款内置游戏直播的国产版Discord应用&#xff0c;用户不仅可以通过IM聊天&#xff0c;也可以进行语聊&#xff0c;看游戏直播&#xff0c;甚至自己进行游戏直播&#xff0c;无任何实时音视频底层技术…...

嵌入式学习笔记——基于Cortex-M的单片机介绍

基于Cortex-M的单片机介绍前言生产厂商及其产品线ARM单片机的产品线命名规则留个作业习单片机的资料准备STM32开发所需手册1.芯片的数据手册作业2前言 本文继续接着上一篇中关于Cortex-M的介绍&#xff0c;来记录一些关于ARM系单片机的知识。 生产厂商及其产品线 芯片厂商在…...

Python 虚拟环境的使用

PyCharm 创建的虚拟环境与使用 workon 命令创建的虚拟环境在本质上没有区别&#xff0c;它们都是 Python 的虚拟环境。 使用 PyCharm 创建工程时&#xff0c;使用可以使用曾经工程的虚拟环境&#xff0c;或者新建一个虚拟环境来安装 Python 的库&#xff0c;又或者使用 workon…...

招生咨询|浙江大学MPA项目2023年招生问答与通知

问&#xff1a;报考浙江大学MPA的基本流程是怎么样的&#xff1f; 答&#xff1a;第一阶段为网上报名与确认。MPA考生须参加全国管理类联考&#xff0c;网上报名时间一般为10月初开始、10月下旬截止&#xff0c;错过网上报名时间后不能补报。确认时间一般为11月上旬&#xff0c…...

Qt std :: bad_alloc

文章目录摘要问题出现原因第一种 请求内存多余系统可提供内存第二种 地址空间过于分散&#xff0c;无法满足大块连续内存的请求第三种 堆管理数据结构损坏稍微总结下没想到还能更新参考关键字&#xff1a; std、 bad、 alloc、 OOM、 异常退出摘要 今天又是被BUG统治的一天&a…...

《设计模式》装饰者模式

《设计模式》装饰者模式 装饰者模式&#xff08;Decorator Pattern&#xff09;是一种结构型设计模式&#xff0c;它允许在不改变现有对象结构的情况下&#xff0c;动态地添加行为或责任到对象上。在装饰者模式中&#xff0c;有一个抽象组件&#xff08;Component&#xff09;…...

网站编辑建设/b2b电子商务平台排名

老孟导读&#xff1a;前2天有读者问到是否有带分页功能的表格控件&#xff0c;今天分页功能的表格控件详细解析来来。PaginatedDataTablePaginatedDataTable是一个带分页功能的DataTable&#xff0c;生成一批数据&#xff0c;项目中此一般通过服务器获取&#xff0c;定义model类…...

河北手机网站制作哪家好/网站搭建关键词排名

图书租售管理系统的设计与实现 摘 要 信息化社会内需要与之针对性的信息获取途径&#xff0c;但是途径的扩展基本上为人们所努力的方向&#xff0c;由于站在的角度存在偏差&#xff0c;人们经常能够获得不同类型信息&#xff0c;这也是技术最为难以攻克的课题。针对图书租售管理…...

玄武模板网站制作品牌/网络营销师官网

其实循环生成css都是定义一个数组&#xff0c;然后遍历生成&#xff0c;主要在于各个语言的调用方式不一样. 这里使用的rem作为基准单位&#xff0c;也可以用px或者其他 _variable.scss文件 // 文件名以下划线开头表示被引用文件&#xff0c;引用时不用加下划线和后缀名//定义…...

群晖可以做网站服务器吗/小说百度风云榜

从A表随机取2条记录,用SELECT TOP 10 * FROM ywle order by newid() order by 一般是根据某一字段排序,newid()的返回值 是uniqueidentifier ,order by newid()随机选取记录是如何进行的 newid()在扫描每条记录的时候都生成一个值, 而生成的值是随机的, 没有大小写顺序. 所以…...

公司支付网站服务费怎么做分录/关键词歌曲歌词

我想执行批处理&#xff0c;有些参数等的运算&#xff0c;我就找了 命令行传递给批处理的参数 后来找了python执行系统命令四种方法比较 我认为用python 扩展批处理的功能还是比较理想的。我的情况用简单批处理有点难以实现。 先看看我的代码吧&#xff1a; import os from…...

网上兼职网站怎么做的/竞价推广账户托管服务

Python 3中的File对象不支持next()方法。 Python 3有一个内置函数next()&#xff0c;它通过调用其next ()方法从迭代器中检索下一个项目。 如果给定了默认值&#xff0c;则在迭代器耗尽返回此默认值&#xff0c;否则会引发StopIteration。 该方法可用于从文件对象读取下一个输入…...