SpringBoot系列之Web如何支持下划线驼峰互转的传参与返回

接下来先容一个非常现实的运用处景,有些时候后端接口对外定义的传参/返回都是下划线命名风格,但是Java本身是推举驼峰命名办法的,那么一定就存在一个传参下换线,转换成驼峰的场景;以及在返回时,将驼峰命名的转换成下划线

那么如何支持上面这种运用处景呢?

php驼峰转下划线SpringBoot系列之Web若何支撑下划线驼峰互转的传介入返回 PHP

本文先容几种常见的手段

I. 项目搭建1. 项目依赖

本项目借助SpringBoot 2.2.1.RELEASE + maven 3.5.3 + IDEA进行开拓

开一个web做事用于测试

<dependencies> <!-- 邮件发送的核心依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency></dependencies>

配置文件application.yml

server: port: 80802. 需求拆分

接下来为了更方便的理解我们要做的事情,对上面的运用处景进行一些拆分,方便理解

2.1 要求参数解析

对付要求参数,外部通报是下划线命名格式的办法,须要与项目中驼峰命名的工具进行映射,以是这里的问题点便是无法走默认的绑定规则,须要我们进行兼容处理

比如传参是 user_name = 一灰灰,但是我们吸收的参数是 userName

2.2 返回结果处理

返回结果的处理,这里单指返回json工具的场景,一个普通的POJO工具,正常序列化为json字符串时,key实际上与工具的成员名是同等的,而现在则希望将key统一成下划线风格的办法

如,返回一个大略的实体工具

public class ViewDo { private Integer userId; private string userName;}

对应期待返回的json串为

{ "user_name" : "一灰灰", "user_id" : 110}II. 支持办法

为了简化后续的流程,我们这里的传参都确定两个userName + userId,对应项目中的实体类如

@Data@NoArgsConstructor@AllArgsConstructorpublic static class ViewDo { private Integer userId; private String userName;}1. 要求参数解析1.1 @RequestParam表明办法

最大略也是最随意马虎想到的办法自然是直策应用RequestParam表明,将所有的要求参数都通过它来重命名

@GetMapping(path = "getV3")public ViewDo getV3(@RequestParam("user_id") Integer userId, @RequestParam("user_name") String userName) { String str = "userId: " + userId + " userName: " + userName; System.out.println(str); return new ViewDo(userId, userName);}

利用上面直接来写参数映射关系的办法属于比较常见的方法了,但是存在一个问题

•通用性差(每个接口的每个参数都要这么整,如果人为是按照代码来付费的话,那还是可以吸收的;否则这个写法,就真的有点难熬痛苦了)•若接口参数定义的是Map、Java bean实体(POJO),这个映射关联就不太好处理了

除了上面这个问题之外,有个不是问题的问题(为什么这么说,且看下面的说法)

•如果我的接口传参,希望同时吸收驼峰和下划线命名的传参(现实中还真有这种精力病似的场景,别问我怎么知道的),上面这个是弗成的

1.2 Json传参指定命名策略

上面的case,适用于常见的get要求,post表单传参,然后在接口处一一定义参数;对付post json传参时,我们可以考虑通过定义json序列化的命名策略,来支持下划线与驼峰的互转

比如SpringMVC默认利用的jackson来实现json序列化,那么我们可以直接通过指定jackson的PropertyNamingStrategy来完成

配置文件中 application.yml,添加下面这行

spring: jackson: # 利用jackson进行json序列化时,可以将下划线的传参设置给驼峰的非大略工具成员上;并返回下划线格式的json串 # 特殊把稳。
利用这种办法的时候,哀求不能有自定义的WebMvcConfigurationSupport,由于会覆盖默认的处理办法 # 办理办法便是 拿到ObjectMapper的bean工具,手动塞入进去 property-naming-strategy: SNAKE_CASE

对应的接口定义如下

/ post json串 curl 'http://127.0.0.1:8080/postV2' -X POST -H 'content-type:application/json' -d '{"user_id": 123, "user_name": "一灰灰"}' @param viewDo @return /@PostMapping(path = "postV2")public ViewDo postV2(@RequestBody ViewDo viewDo) { System.out.println(viewDo); return viewDo;}

实际要求之后,看一下效果

把稳

•利用上面这种配置的办法,须要特比把稳的,如果在项目中自己定义了WebMvcConfigurationSupport,那么上面的配置将不会生效(至于详细的缘故原由,后面有机会单独解释)

当我们实际的项目中,无法直策应用上面这种配置时,可以考虑利用下面的办法

@SpringBootApplicationpublic class Application extends WebMvcConfigurationSupport { / 下面这个设置,可以实现json参数解析/返回时,传入的下划线转驼峰;输出的驼峰转下划线 @param converters / @Override protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) { MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); ObjectMapper objectMapper = converter.getObjectMapper(); // 设置驼峰标志转下划线 objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); // 设置格式化内容 converter.setObjectMapper(objectMapper); converters.add(0, converter); super.extendMessageConverters(converters); }}

利用jackson的命名策略来支持驼峰下划线的转换虽好,但是存在一个非常明显的毛病

•它只适用于json传参

1.3 自定义DataBinder

对付非json的传承,比如普通的get要求,post表单传参,然后在接口处通过定义一个POJO参数类来吸收,此时又该当怎么处理呢?

比如接口定义如下

/ POJO 对应Spring中的参数转换是 ServletModelAttributeMethodProcessor | RequestParamMethodArgumentResolver @param viewDo @return /@GetMapping(path = "getV5")public ViewDo getV5(ViewDo viewDo) { System.out.println("v5: " + viewDo); return viewDo;}/ curl 'http://127.0.0.1:8080/postV1' -X POST -d 'user_id=123&user_name=一灰灰' 把稳:非json传参,jackson的配置将不会生效,即上面这个要求是不会实现下划线转驼峰的; 但是返回结果会是下划线的 @param viewDo @return /@PostMapping(path = "postV1")public ViewDo post(ViewDo viewDo) { System.out.println(viewDo); return viewDo;}

对付上面这种场景,一个想法便是是否可以在ViewDo的成员上,添加一个表明,指定参数名,一如RequestParam,不过Spring貌似并没有供应这种支持能力

因此我们可以考虑自己来实现数据绑定,下面供应一个根本的实现, 来演示这种办法改怎么玩(相对完全的基于表明的映射办法,下篇博文先容)

public class SimpleDataBinder extends ExtendedServletRequestDataBinder { public SimpleDataBinder(Object target, String objectName) { super(target, objectName); } @Override protected void addBindValues(MutablePropertyValues mpvs, ServletRequest request) { super.addBindValues(mpvs, request); if (!mpvs.contains("userName")) { mpvs.add("userName", getVal(mpvs, "user_name")); } if (!mpvs.contains("userId")) { mpvs.add("userId", getVal(mpvs, "user_id")); } } private Object getVal(MutablePropertyValues mpvs, String key) { PropertyValue pv = mpvs.getPropertyValue(key); return pv != null ? pv.getValue() : null; }}

然后在参数解析中,利用这个DataBinder

public class SimpleArgumentProcessor extends ServletModelAttributeMethodProcessor { public SimpleArgumentProcessor(boolean annotationNotRequired) { super(annotationNotRequired); } @Override protected void bindRequestParameters(WebDataBinder binder, NativeWebRequest nativeWebRequest) { Object target = binder.getTarget(); SimpleDataBinder dataBinder = new SimpleDataBinder(target, binder.getObjectName()); super.bindRequestParameters(dataBinder, nativeWebRequest); }}

接着便是注册这个参数解析

@SpringBootApplicationpublic class Application extends WebMvcConfigurationSupport { @Override protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) { argumentResolvers.add(new SimpleArgumentProcessor(true)); }}

再次要求时,可以创造下划线的传参也可以映射到ViewDo工具上(无论是get要求还是post要求,都可以精确映射)

2.返回结果

对付返回结果,希望返回下划线格式的json串,除了上面先容到的设置json序列化的命名策略之外,还有下面几种配置办法

2.1 属性表明 @JsonProperty

直接在POJO工具的成员上,指定希望输出的name

public static class ViewDo { @JsonProperty("user_id") private Integer userId; @JsonProperty("user_name") private String userName;}2.2 实体类表明 @JsonNaming

直接在类上添加表明,指定驼峰策略

@JsonNaming(value = PropertyNamingStrategy.SnakeCaseStrategy.class)public static class ViewDo { private Integer userId; private String userName;}2.3 全局配置

上面两种缺陷比较明显,不太通用;更通用的选择和前面传参的json序列化配置办法一样,两种姿势

配置文件指定

spring: jackson: # 利用jackson进行json序列化时,可以将下划线的传参设置给驼峰的非大略工具成员上;并返回下划线格式的json串 # 特殊把稳。
利用这种办法的时候,哀求不能有自定义的WebMvcConfigurationSupport,由于会覆盖默认的处理办法 # 办理办法便是 拿到ObjectMapper的bean工具,手动塞入进去 property-naming-strategy: SNAKE_CASE

前面也说到,上面这种配置可能会失落效(比如你设置了自己的WebMvcConfig),推举利用下面的办法

public class Application extends WebMvcConfigurationSupport { / 下面这个设置,可以实现json参数解析/返回时,传入的下划线转驼峰;输出的驼峰转下划线 @param converters / @Override protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) { MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); ObjectMapper objectMapper = converter.getObjectMapper(); // 设置驼峰标志转下划线 objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); // 设置格式化内容 converter.setObjectMapper(objectMapper); converters.add(0, converter); super.extendMessageConverters(converters); }}3. 小结

本文紧张先容了几种实例case,用于实现传参/返回的驼峰与下划线的互转,核心策略,有下面几种

•传参:@RequestParam 指定真正的传参name•Json传参、返回:通过定义json序列化框架的PropertyNamingStrategy,来实现•普通表单传参/get传参,映射POJO时:通过自定义的DataBinder,来实现映射

虽然上面几种姿势,可以知足我们的基本诉求,但是如果我希望实现一个通用的下划线/驼峰互转策略,即不管传参是下划线还是驼峰,都可以精确无误的绑定到接口的参数变量上,可以怎么实现呢?

末了再抛出一个问题,如果吸收参数是Map,上面的几种实现姿势会生效么?又可以如何怎么处理map这种场景呢?

III. 不能错过的源码和干系知识点0. 项目

•工程:https://github.com/liuyueyi/spring-boot-demo•源码:https://github.com/liuyueyi/spring-boot-demo/tree/master/spring-boot/202-web-params-camel

1. 微信"大众年夜众号: 一灰灰Blog

尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和缺点之处,如创造bug或者有更好的建议,欢迎批评示正,不吝感激

下面一灰灰的个人博客,记录所有学习和事情中的博文,欢迎大家前去走走

•一灰灰Blog个人博客 https://blog.hhui.top

•一灰灰Blog-Spring专题博客 http://spring.hhui.top