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. 文件不落地上线sliver

    接着二开sliver,给他添加一个生成powershell payload上线的功能,生成并复制到剪切板. ‍ 准备 首先,先开启监听,不多说直接mtls默认的就行,sliver可以生成配置文件来实现 ...

  2. Python——第五章:hashlib模块

    hashlib 模块(hash)算法 hashlib 模块是 Python 中用于加密散列(hash)算法的模块.它提供了对常见的哈希算法(如MD5.SHA-1.SHA-256等)的支持,使得开发者可 ...

  3. Redis配置模式及各自优缺点

    大家好,我是闲者,今天简单聊下redis部署模式.Redis支持多种不同的数据结构和模式,以满足不同的使用场景.以下是一些常见的Redis配置和模式示例以及详解.原文地址:Redis配置模式及各自优缺 ...

  4. shared_ptr 自定义 deleter 删除器

    一些结论 如果 shared_ptr 管理的资源不是 new 分配的内存,才考虑自定义删除器,这也是为什么 make_shared 不支持自定义删除器的原因,因为 make_shared 就是通过 n ...

  5. 文心一言 VS 讯飞星火 VS chatgpt (43)-- 算法导论5.4 7题

    七.为使特征序列长度的下界变得更精确,请说明在 n 次硬币的公平抛掷中,不出现比Ign - 2lglgn 更长的连续正面特征序列的概率小于 1/n. 文心一言: 在 n 次硬币的公平抛掷中,每个硬币正 ...

  6. 从部署和运维说说DLI(1)

    DLI是支持多模引擎的Serverless大数据计算服务,其很好的实现了Serverless的特性:   1. 弱化了存储和计算之间的联系: 2. 代码的执行不再需要手动分配资源: 3. 按使用量计费 ...

  7. 华为AppCube通过中国信通院“低代码开发平台通用能力要求”评估!

    摘要:华为AppCube应用魔方顺利通过信通院评估,被认证为具备 "低代码开发平台通用能力"的企业服务平台. 本文分享自华为云社区<华为AppCube通过中国信通院" ...

  8. 协同文档:OT与CRDT实现协同编辑笔记

    讲协同编辑,先回顾下从BBS.邮件,到IM 信息的异步传播 信息的生产和消费异步发生. 典型的场景如论坛,博客,文档库,邮件.我在写这篇文档的时候,你们看不到.你们看的时候,我早已写完.异步场景下,信 ...

  9. storybook添加全局样式与sass全局变量设置

    storybook组件需要全局样式,只需在.storybook/preview.js 增加全局样式即可. import '../src/style/index.scss'; export const  ...

  10. vue2升级vue3:class component的遗憾

    在vue2,class 写法真的非常爽 import { Component as tsc } from 'vue-tsx-support'; import { Component, Watch }  ...