springboot入参出参下划线转驼峰

前言

因为历史原因前端入参和出参都为下划线,下划线对有亿点强迫症的我来说是不可接受的。因此就有了下面这篇。

本篇基于之前的一篇springboot封装统一返回 - Scott_pb - 博客园 (cnblogs.com)

引入xml

因为是基于jackson而spring-boot-starter-web已经包含

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

工具类

public class JsonUtils {

    private static final ObjectMapper objectMapper = new ObjectMapper();

    /**
* json字符串转驼峰
*
* @param json String
* @return String
*/
public static String convertToCamelCaseJson(String json) throws JsonProcessingException {
JsonNode jsonNode = objectMapper.readTree(json); jsonNode = convertKeysToCamelCase(jsonNode); return objectMapper.writeValueAsString(jsonNode);
} /**
* JsonNode的key转驼峰
*
* @param jsonNode JsonNode
* @return JsonNode
*/
public static JsonNode convertKeysToCamelCase(JsonNode jsonNode) {
if (jsonNode.isObject()) {
ObjectNode objectNode = objectMapper.createObjectNode();
jsonNode.fields().forEachRemaining(entry -> {
objectNode.set(snakeToCamel(entry.getKey()), convertKeysToCamelCase(entry.getValue()));
});
return objectNode;
} if (jsonNode.isArray()) {
ArrayNode arrayNode = objectMapper.createArrayNode();
jsonNode.elements().forEachRemaining(entry -> {
arrayNode.add(convertKeysToCamelCase(entry));
}); return arrayNode;
} return jsonNode;
} public static JsonNode convertKeysToSnake(JsonNode jsonNode) {
if (jsonNode.isObject()) {
ObjectNode objectNode = objectMapper.createObjectNode();
jsonNode.fields().forEachRemaining(entry -> {
objectNode.set(camelToSnake(entry.getKey()), convertKeysToCamelCase(entry.getValue()));
});
return objectNode;
} if (jsonNode.isArray()) {
ArrayNode arrayNode = objectMapper.createArrayNode();
jsonNode.elements().forEachRemaining(entry -> {
arrayNode.add(convertKeysToSnake(entry));
}); return arrayNode;
} return jsonNode;
} /**
* 下划线转驼峰
*
* @param key String
* @return String
*/
public static String snakeToCamel(String key) {
if (key.indexOf("_") != 0) {
String[] s = key.split("_");
StringBuilder stringBuilder = new StringBuilder(s[0]);
for (int i = 1; i < s.length; i++) {
stringBuilder.append(s[i].substring(0, 1).toUpperCase()).
append(s[i].substring(1));
}
return stringBuilder.toString();
} return key;
} /**
* 驼峰转下划线
* @param key String
* @return String
*/
public static String camelToSnake(String key) {
int length = key.length();
StringBuilder sb = new StringBuilder(length);
for (int i = 0; i < length; i++) {
char current = key.charAt(i);
if (Character.isUpperCase(current)) {
if (i > 0) {
sb.append('_');
}
sb.append(Character.toLowerCase(current));
continue;
}
sb.append(current);
}
return sb.toString();
} }

ServletRequest定义

因为requestBody是流形式,所以转驼峰后,需要将转后的数据封装为流到requestBody中,我采用继承HttpServletRequestWrapper的方法重写HttpServletRequest

public class CamelKeyRequestWrapper extends HttpServletRequestWrapper {
//修改后的字节数组
private final byte[] modifiedContent; //json的key转为驼峰
public CamelKeyRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
this.modifiedContent = camelCaseJson(request);
} //调用getInputStream时,返回转化后的驼峰json数据
public ServletInputStream getInputStream() {
return new ModifiedServletInputStream(new ByteArrayInputStream(modifiedContent));
} @Override
public BufferedReader getReader() {
return new BufferedReader(new InputStreamReader(getInputStream()));
} /**
* 下划线转驼峰
* @param request HttpServletRequest
* @return byte[]
* @throws IOException e
*/
private byte[] camelCaseJson(HttpServletRequest request) throws IOException {
//没有requestBody
if (request.getContentType() != null && !request.getContentType().contains("application/json")) {
return null;
} ServletInputStream inputStream = null;
BufferedReader reader = null;
String originalJsonBody, camelCaseJson = null;
ContentCachingRequestWrapper requestWrapper;
try {
requestWrapper = new ContentCachingRequestWrapper(request);
//原来的InputStream流,因为是流,所以只能使用一次
inputStream = requestWrapper.getInputStream();
//从流中按照UTF-8格式读取
reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
//定义StringBuilder
StringBuilder stringBuilder = new StringBuilder();
String line;
//按行读取
while ((line = reader.readLine()) != null) {
stringBuilder.append(line);
}
//得到下划线的json字符串
originalJsonBody = stringBuilder.toString();
//将下划线的json字符串转为驼峰的json字符串
camelCaseJson = JsonUtils.convertToCamelCaseJson(originalJsonBody); } catch (Exception e) {
e.printStackTrace();
} finally {
//不要忘了关闭链接
assert inputStream != null;
inputStream.close();
assert reader != null;
reader.close();
} assert camelCaseJson != null;
return camelCaseJson.getBytes();
}
}

filter拦截器

@Component
@Order(1)
public class RequestBodyFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { //判断有requestbody传入
if (servletRequest.getContentType() == null || !servletRequest.getContentType().contains("application/json")) {
filterChain.doFilter(servletRequest,servletResponse);
return;
} //下划线转驼峰requestBody
filterChain.doFilter(new CamelKeyRequestWrapper((HttpServletRequest) servletRequest),servletResponse); } }

修改ResponseResultHandler

修改上一篇中ResponseResultHandler

@ControllerAdvice
public class ResponseResultHandler<T> implements ResponseBodyAdvice<Object> {
public static final String RESPONSE_RESULT_ANN = "RESPONSE-RESULT-ANN"; @Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (sra == null) {
return false;
} HttpServletRequest sraRequest = sra.getRequest();
ResponseResult responseResult = (ResponseResult) sraRequest.getAttribute(RESPONSE_RESULT_ANN);
return responseResult != null;
} @SneakyThrows
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response) { if (body instanceof Response) {
return body;
} //List数组
if (body instanceof List) {
body = converSnakeList((List<T>) body); } //PageInfoVo
if (body instanceof PageInfoVo) {
List<? extends T> snakeList = converSnakeList((List<T>) ((PageInfoVo<?>) body).getData());
((PageInfoVo<T>) body).setData(snakeList);
} //...其他类型... if (body instanceof String) {
return Response.success(body);
} return Response.success(body);
} /**
* 数组的key转为下划线
* @param data List
* @return List<? extends T>
* @throws JsonProcessingException JsonProcessingException
*/
private List<? extends T> converSnakeList(List<T> data) throws JsonProcessingException {
int size = data.size(); //list中有数据
if (size > 0) {
ObjectMapper objectMapper = new ObjectMapper();
List<JsonNode> snakeList = new ArrayList<>(size); for (T datum : data) {
//object转JsonNode
JsonNode jsonNode = objectMapper.readTree(objectMapper.writeValueAsString(datum));
//JsonNode的key转为蛇形
JsonNode snakeJsonNode = JsonUtils.convertKeysToSnake(jsonNode); snakeList.add(snakeJsonNode);
} return (List<? extends T>) snakeList;
}
return null;
}
}

springboot入参下划线转驼峰出参驼峰转下划线的更多相关文章

  1. 字节码编程,Javassist篇二《定义属性以及创建方法时多种入参和出参类型的使用》

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 在上一篇 Helloworld 中,我们初步尝试使用了 Javassist字节编程的 ...

  2. 使用filter获取http请求的出参以及入参

    首先 我们的目的是做一个拦截器 能够对http请求做profiler,能够记录本次的调用情况,这里说下如何从http请求中获取到出参的问题. 方案一:参照http://blog.csdn.net/wu ...

  3. 关于用mybatis调用存储过程时的入参和出参的传递方法

    一.问题描述 a)         目前调用读的存储过程的接口定义一般是:void  ReadDatalogs(Map<String,Object> map);,入参和出参都在这个map里 ...

  4. mysql存储过程出参入参,sqlserver很熟悉的一件事到mysql,捣鼓了大半天。记录一下提醒自己。勿看

    create PROCEDURE myTestProcname(in score int ,out result varchar(100))BEGINIF score>60 THENset re ...

  5. Spring AOP 自定义注解获取http接口及WebService接口入参和出参

    注解方法实现过程中可以采用如下获取方式:—以下为例  HttpServletRequest request = ((ServletRequestAttributes) RequestContextHo ...

  6. 先查询再插入,改为存储过程,java部分入参出参、mybatisxml【我】

    先查询再插入,改为存储过程 create or replace procedure PRO_REVENUE_SI(l_p_cd in Varchar2, l_c_cd in Varchar2, l_p ...

  7. python调用c/c++ (入参出参为指针)

    python可以使用ctypes库调用c++编译的so库函数 0x01  c/c++编译为so库文件 编译C文件 gcc -o libpycallfoo.so -shared -fPIC rsa.c  ...

  8. Go语言json编码驼峰转下划线、下划线转驼峰

    目录 一.需求 二.实现 三.使用 JsonSnakeCase统一转下划线json JsonSnakeCase统一转驼峰json 一.需求 golang默认的结构体json转码出来,都是大写驼峰的,并 ...

  9. XmlRpc.net 出参字符串还原为结构体

    上一篇随笔写的是入参结构体转字符串,现在需要把保存到服务器的字符串还原为结构体,这里记录一下操作步骤: 1. 格式化字符串. XmlRpcDeserializer 支持反序列化<struct&g ...

  10. JDBC获取sql server存储过程查询结果集(没有出参)

    对于一些较为复杂的统计条件查询,可以通过存储过程来实现,既可以提高效率,减少网络流量,也可以避免sql语句耦合在代码中.但是存储过程返回的结果集如何获取(类似表数据),却着实让我费劲心力. 如下: C ...

随机推荐

  1. 38. 干货系列从零用Rust编写负载均衡及代理,负载均衡中ip通行与禁止

    wmproxy wmproxy已用Rust实现http/https代理, socks5代理, 反向代理, 静态文件服务器,四层TCP/UDP转发,七层负载均衡,内网穿透,后续将实现websocket代 ...

  2. 去年最火的 JS 开源项目「GitHub 热点速览」

    近日,「Best of JS」发布了过去一年在 GitHub 上 Star 数增速最快的 JavaScript 开源项目(2023 JavaScript Rising Stars),前 10 的开源项 ...

  3. Kafka 具体分析

    前面的相关文件简要地介绍了 Kafka 的基本使用,本文将将要介绍一下关于 Kafka 的集群关系.存储结构以及架构方面的内容进行简要的解析 组件之间的关系 Kafka 中,各个组件之间的关系如下图所 ...

  4. django 定时任务 apscheduler 踩坑

    本想每天定点的去查询一些数据然后用钉钉机器人发出来,前两三天还好好的,后面就执行 ERROR了 看了下错误问题,就跟连不上数据库一样,参考别人的解决方法 scheduler.add_job(every ...

  5. Java中单体应用锁的局限性&分布式锁

    互联网系统架构的演进 在互联网系统发展之初,系统比较简单,消耗资源小,用户访问量也比较少,我们只部署一个Tomcat应用就可以满足需求.系统架构图如下: 一个Tomcat可以看作是一个JVM进程,当大 ...

  6. 2023-09-10:用go语言编写。作为项目经理,你规划了一份需求的技能清单 req_skills, 并打算从备选人员名单 people 中选出些人组成一个「必要团队」 ( 编号为 i 的备选人员

    2023-09-10:用go语言编写.作为项目经理,你规划了一份需求的技能清单 req_skills, 并打算从备选人员名单 people 中选出些人组成一个「必要团队」 ( 编号为 i 的备选人员 ...

  7. Blog Statistics Dec 1, 2021 - Dec 1, 2022

    1. Overview Data Date: Dec 1, 2021 - Dec 1, 2022 Number of articles: 51 All Platform Total Visits: 3 ...

  8. MySQL进阶篇:详解索引使用_最左前缀法则

    MySQL进阶篇:第四章_四.一_ 索引使用_最左前缀法则 最左前缀法则 如果索引了多列(联合索引),要遵守最左前缀法则.最左前缀法则指的是查询从索引的最左列开始,并且不跳过索引中的列.如果跳跃某一列 ...

  9. 理论+实践详解最热的LLM应用框架LangChain

    本文分享自华为云社区<LangChain是什么?LangChain的详细介绍和使用场景>,作者:码上开花_Lancer . 一.概念介绍 1.1 Langchain 是什么? 官方定义是: ...

  10. 一文读懂火山引擎A/B测试的实验类型(1)——编程实验

    一. 概述 编程实验:指的是通过代码编程进行AB实验,广泛使用于前端优化.策略优化和后端算法优化多种实验场景,包含客户端和服务端实验. 前置条件:接入客户端SDK或者服务端SDK,详见:应用接入 二. ...