问题

前几天帮忙其他部门的多个祖先级项目改造开发,服务间使用Feign方式调用,发现接口提供方接收到的请求,没有请求参数,经过排查发现服务调用方的FastJsonHttpMessageConverter配置方式存在问题,导致请求中RequestBody的序列化出现问题。

排查步骤

  1. 服务提供方排查

经debug DispatcherServlet发现request中各请求参数名发生了变化—命名规则由camel变成了snake_case(蛇形命名规则),接口的请求参数名的命名规则是camel,所以导致接口中各个参数没有值,判定很有可能是请求调用方的HttpMessageConverter的配置有问题;

  1. 服务调用方排查

debug查发出请求时请求的所有的messageConverter,发现项目里添加了FastJsonHttpMessageConverter,在经过FastJsonHttpMessageConverter 对请求参数的处理后,HttpOutputMessage输出的请求参数名发生了变化—把原来camel规则的请求参数统一进行了snake_case转换;

  1. 至此基本定位问题原因-FastJsonHttpMessageConverter的配置导致参数命名发生改变,进而导致服务提供方接收不到原有的参数。

解决方法

深入阅读FastJsonHttpMessageConverter源码,在writeInternal 和 readInternal时,会使用FastJsonConfig-> SerializeConfig,来进行请求体的序列化和反序列化,如图-1

图-1

方法一:

  1. 自定义FeignClient的configuration覆盖默认的FeignClientsConfiguration,在自定义的FeignConfiguration中去重新定义SpringEncoder和SpringDecoder,在Encoder和Decoder中去重新配置FastJsonHttpMessageConverter;
  2. 针对此FeignClient的configuration定义一个此feign实例的FastJsonConfig->SerializeConfig,而非使用全局的SerializeConfig(之所以不用全局,因为项目比较庞杂,怕影响其他三方服务调用),设置其propertyNameStrategy = CamelCase
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import feign.Logger.Level;
import feign.codec.Decoder;
import feign.codec.Encoder;
import feign.codec.ErrorDecoder;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.boot.autoconfigure.web.HttpMessageConverters;
import org.springframework.cloud.netflix.feign.support.ResponseEntityDecoder;
import org.springframework.cloud.netflix.feign.support.SpringDecoder;
import org.springframework.cloud.netflix.feign.support.SpringEncoder;
import org.springframework.context.annotation.Bean;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter; public class FeignConfiguration {
HttpMessageConverters converters;
ObjectFactory<HttpMessageConverters> httpMessageConverters; public FeignConfiguration() {
} @PostConstruct
public void init() {
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
List<MediaType> supportedMediaTypes = new ArrayList();
supportedMediaTypes.add(MediaType.APPLICATION_JSON);
supportedMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
supportedMediaTypes.add(MediaType.APPLICATION_ATOM_XML);
supportedMediaTypes.add(MediaType.APPLICATION_FORM_URLENCODED);
supportedMediaTypes.add(MediaType.APPLICATION_OCTET_STREAM);
supportedMediaTypes.add(MediaType.APPLICATION_PDF);
supportedMediaTypes.add(MediaType.APPLICATION_RSS_XML);
supportedMediaTypes.add(MediaType.APPLICATION_XHTML_XML);
supportedMediaTypes.add(MediaType.APPLICATION_XML);
supportedMediaTypes.add(MediaType.IMAGE_GIF);
supportedMediaTypes.add(MediaType.IMAGE_JPEG);
supportedMediaTypes.add(MediaType.IMAGE_PNG);
supportedMediaTypes.add(MediaType.TEXT_EVENT_STREAM);
supportedMediaTypes.add(MediaType.TEXT_HTML);
supportedMediaTypes.add(MediaType.TEXT_MARKDOWN);
supportedMediaTypes.add(MediaType.TEXT_PLAIN);
supportedMediaTypes.add(MediaType.TEXT_XML);
converter.setSupportedMediaTypes(supportedMediaTypes);
this.converters = new HttpMessageConverters(new HttpMessageConverter[]{converter});
this.httpMessageConverters = () -> {
return this.converters;
};
} @Bean
public Level feignLoggerLevel() {
return Level.FULL;
} @Bean
public ErrorDecoder businessErrorDecoder() {
return new FeignErrorDecoder();
} @Bean
public Decoder feignDecoder() {
return new ResponseEntityDecoder(new SpringDecoder(this.httpMessageConverters));
} @Bean
public Encoder feignEncoder() {
return new SpringEncoder(this.httpMessageConverters);
} }

方法二:

服务提供方,feign接口的@RequestBody 对应的请求实体类添加@JSONType(naming=PropertyNamingStrategy.CamelCase)

SerializeConfig在getWriter时会读取这个注解来决定序列化/反序列化时的请求体中参数的命名规则。

 1 import com.alibaba.fastjson.PropertyNamingStrategy;
2 import com.alibaba.fastjson.annotation.JSONType;
3
4 import java.io.Serializable;
5
6 /**
7 * @author zhaoxinbo
8 * @name: UserRequest
9 * @description: 请求参数-user RequestBody
10 * @date 2021/9/24 20:22
11 */
12 @JSONType(naming = PropertyNamingStrategy.CamelCase)
13 public class UserRequest implements Serializable {
14 private Long userId;
15
16 private String userName;
17
18 public Long getUserId() {
19 return userId;
20 }
21
22 public void setUserId(Long userId) {
23 this.userId = userId;
24 }
25
26 public String getUserName() {
27 return userName;
28 }
29
30 public void setUserName(String userName) {
31 this.userName = userName;
32 }
33 }

建议使用第二种方式,服务提供方明确告知请求参数的命名规则,当然第一种方法中如果有服务提供方来提供FeignConfiguration的话更方便。

FastJsonHttpMessageConverter请求中参数序列化问题排查的更多相关文章

  1. axio 请求中参数是数组

    前言 最近在做 Vue 项目中,Get 请求中有的参数是数组,传 JSON 字符串是没有问题的,但是直接传数组就一直报错,有问题. 参数后面无故加了 [],例如:UserIds 变成 UserIds[ ...

  2. ajax 之POST请求,参数序列化

    比如,,我们在没有使用jquery的时候,没有$.post来让我们使用,那我们像下面这样直接发送: var params1 = { username: username, passwrod: pass ...

  3. 解决backbone url请求中参数有中文,存入数据库是乱码

    最近项目用到了backbone 做前后端的分离方案,遇见了中文乱码问题,解决方案总结如下: 假设需要存一条课程记录到后台   model定义如下: var AddCourse= Backbone.Mo ...

  4. PHP中get请求中参数的key不能是para

    &para会被转化成¶,然后就无法进行下去了. 仅作记录.

  5. Spring MVC如何获取请求中的参数

    目录 一.获取URL中路径参数 1.1 @PathVariable 注解 1.2 @PathParam 注解 二.获取请求参数: 2.1 GET请求 2.1.1 获取请求中的单个参数:@Request ...

  6. http请求中的中文乱码问题

    通过浏览器访问服务器页面和资源时,不可避免地要传送中文字串,如果客户机与服务器不能用同一码表解析字串,肯定会出现各种各样的乱码问题.我总结了几个乱码场景及解决办法,如下 1.服务器上的中文字串被客户端 ...

  7. AJAX请求 $.ajaxSetup方法的使用:设置AJAX请求的默认参数选项,当程序中需要发起多个AJAX请求时,则不用再为每一个请求配置请求的参数

    定义和用法ajaxSetup() 方法为将来的 AJAX 请求设置默认值.语法$.ajaxSetup({name:value, name:value, ... }) 该参数为带有一个或多个名称/值对的 ...

  8. (三)Asp.net web api中的坑-【http post请求中的参数】

    接上篇, HttpPost 请求 1.post请求,单参数 前端 var url = 'api/EnterOrExit/GetData2';var para = {};para["Phone ...

  9. post请求中的参数形式和form-data提交数据时取不到的问题

    @Controller页面form表单请求时不会丢数据返回json数据时需要加 注解@ResponseBody请求格式如下 @ResponseBody public Object login(Sign ...

随机推荐

  1. 联盛德 HLK-W806 (一): Ubuntu20.04下的开发环境配置, 编译和烧录说明

    目录 联盛德 HLK-W806 (一): Ubuntu20.04下的开发环境配置, 编译和烧录说明 联盛德 HLK-W806 (二): Win10下的开发环境配置, 编译和烧录说明 联盛德 HLK-W ...

  2. 【POJ1845】Sumdiv【算数基本定理 + 逆元】

    描述 Consider two natural numbers A and B. Let S be the sum of all natural divisors of A^B. Determine ...

  3. 【k8s】使用Terraform一键部署EKS集群

    本文适用范文 使用AWS海外账号 对aws.terraform.k8s有一定的了解 新建一个独立的VPC Terraform简介 terraform是一个云端的资源编排工具,官方对自己的定位:Terr ...

  4. DAS,NAS,SAN,简介

    根据服务器类型分为:封闭系统的存储和开放系统的存储,封闭系统主要指大型机,开放系统指基于Windows.UNIX.Linux等操作系统的服务器;开放系统的存储分为:内置存储和外挂存储;外挂存储根据连接 ...

  5. Golang使用validator进行数据校验及自定义翻译器

    Golang使用validator进行数据校验及自定义翻译器 包下载:go get github.com/go-playground/validator/v10 一.概述 在接口开发经常会遇到一个问题 ...

  6. 【模板】Splay(伸展树)普通平衡树(数据加强版)/洛谷P6136

    题目链接 https://www.luogu.com.cn/problem/P6136 题目大意 需要写一种数据结构,来维护一些非负整数( \(int\) 范围内)的升序序列,其中需要提供以下操作: ...

  7. (数据科学学习手札132)Python+Fabric实现远程服务器连接

    本文示例代码及文件已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 日常工作中经常需要通过SSH连接到多台远程 ...

  8. FTP 文件传输服务

    昨晚心血来潮,尝试用python写了一个ftp文件传输服务,可以接收指令,从远程ftp服务器同步指定目录数据,最后没用上,开源出来. https://github.com/jadepeng/ftp_t ...

  9. 图形学3D渲染管线学习

    图形学3D渲染管线 DX和OpenGL左右手坐标系不同,会有一些差距,得出的矩阵会不一样; OpenGL的投影平面不是视景体的近截面: 顶点(vertexs) 顶点坐标,颜色,法线,纹理坐标(UV), ...

  10. map和forEach的区别

    总结 forEach()可以做到的东西,map()也同样可以.反过来也是如此. map()会分配内存空间存储新数组并返回,forEach()不会返回数据. forEach()允许callback更改原 ...