spring 到底怎么才能拿到原始的 body 啊?

2023-12-01 11:57:01 +08:00
 likeme

我的接口

腾讯签名示例

尝试与腾讯实时音视频进行对接。从 request 中提取数据或通过 @RequestBody String body 获取,发现格式不符合原始格式,且缺少了\n 或\t 符号,导致签名不一致。确认使用的是示例中的密钥和签名值,并通过 Postman 进行了测试。

2706 次点击
所在节点    Java
13 条回复
likeme
2023-12-01 11:58:07 +08:00
chatgpt 已经问烂了,实在是没办法了发到 V2EX 让老哥们给看看。
NULL2020
2023-12-01 12:08:00 +08:00
public static String getRequestBodyString(HttpServletRequest request) {
final int contentLength = request.getContentLength();
if (contentLength <= 0) {
return null;
}

StringBuilder data = new StringBuilder();
String line;
BufferedReader reader;
try {
reader = request.getReader();
while (null != (line = reader.readLine()))
data.append(line);
} catch (Exception e) {
log.error("HttpServletRequest read line error.");
}

return data.toString();
}
chendy
2023-12-01 12:11:55 +08:00
随手测试了一下,除非做了什么特殊处理,否则 ReqeustBody String 拿到的就是原始字符串,特殊字符不会被去掉
至于 /n 和 /t 被去掉,有没有可能是并没有被去掉只是打印看不到呢…
lisongeee
2023-12-01 12:19:09 +08:00
如果使用 @RequestBody ByteArray body 获取是否可行?
infoscope
2023-12-01 12:44:25 +08:00
直接从 HttpServletRequest 中拿
用 @RequestBody 映射的,SpringMCV 根据 Content-Type 选择对应的转换器进行转换,如:
org.springframework.http.converter.StringHttpMessageConverter 只对 text/plain 生效
org.springframework.http.converter.json.MappingJackson2HttpMessageConverter 对 application/json 生效

如果对方请求不是 text/plain, 你拿到的 String 就不是原始的了


建议的验签方式应该是在 Filter 层,对业务代码无侵入
ContentCachingRequestWrapper

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 用 ContentCachingRequestWrapper 的目的时把请求体读出来的内容缓存起来,后续 SpringMVC 还可以再读一遍,原始的 InputStream 是不能重置再读的
ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper((HttpServletRequest) request);
String requestContent = new String(requestWrapper.getContentAsByteArray());
checkSign(requestContent);
chain.doFilter(requestWrapper, response);
}
rimwindy
2023-12-01 13:33:24 +08:00
不行就再转一次吧,过来的 body 可能就是普通 JSON ,不是演示里那种压缩为一行的情况。

Demo:
```java
public class Main {
public static void main(String[] args) throws JsonProcessingException {
String body = """
{
"status": "OK",
"data": [
{
"id": 1,
"name": "data1"
},
{
"id": 2,
"name": "data2"
}
]
}
""";

// Jackson-databind -> pom.xml
ObjectMapper mapper = new ObjectMapper();
String body2 = mapper.writeValueAsString(body);
System.out.println("JSON to one line: " + body2);
}
}
```

Output:
```bash
JSON to one line: "{\n\t\"status\": \"OK\",\n\t\"data\": [\n\t\t{\n\t\t\t\"id\": 1,\n\t\t\t\"name\": \"data1\"\n\t\t},\n\t\t{\n\t\t\t\"id\": 2,\n\t\t\t\"name\": \"data2\"\n\t\t}\n\t]\n}\n"
```
likeme
2023-12-01 13:41:07 +08:00
@chendy 打印 body ,然后再将打印的 json 发送请求,controller 就拿不到原来的格式了,直接变成了一行,丢失了换行符了。

String body = "{\n" + "\t\"EventGroupId\":\t2,\n" + "\t\"EventType\":\t204,\n" + "\t\"CallbackTs\":\t1664209748188,\n" + "\t\"EventInfo\":\t{\n" + "\t\t\"RoomId\":\t8489,\n" + "\t\t\"EventTs\":\t1664209748,\n" + "\t\t\"EventMsTs\":\t1664209748180,\n" + "\t\t\"UserId\":\t\"user_85034614\",\n" + "\t\t\"Reason\":\t0\n" + "\t}\n" + "}";

System.out.println(body);
Masoud2023
2023-12-01 13:48:16 +08:00
https://github.com/yuhuachang/java-spring-boot-samples/blob/master/spring-rest-logging/src/main/java/com/example/restlogging/logging/HttpServletRequestCopier.java

你想要的应该是这个。

把这个放进 filter 链,然后在 controller 拿到 httpservletrequest ,强制转换到这个 HttpServletRequestCopier ,调用 getContentAsByteArray ,然后转成 utf-8 string 就行了。

靠 @RequestBody 我记得我之前的实践是怎么拿都拿不到最原始的 body ,他始终都会走一遍 Spring 的那套类型转换,这个往上好像有很多资料都提到过这个问题。
likeme
2023-12-01 13:53:22 +08:00
@Masoud2023 我怀疑破案了,因为我们公司的脚手架有对请求数据做一些过滤,所以导致无法拿到最原始的,我重新创建了一个 spring 脚手架,测试了是用 @RequestBody String body 就可以拿到原始的。
Masoud2023
2023-12-01 13:53:37 +08:00
HttpServletRequestCopier copier = new HttpServletRequestCopier((HttpServletRequest) servletRequest);
filterChain.doFilter(copier, servletResponse);
xiaoxinTOm
2023-12-01 16:21:31 +08:00
controller 拿的 body ?可能过滤器对请求体做了二次处理,处理过后的才放行到 controller 中,我以前看到的代码有这么做,你去看看过滤器把 filter 把
likeme
2023-12-04 08:59:26 +08:00
@xiaoxinTOm 是的,用的是 ruoyi 的框架,有一个过滤器导致的。
bill110100
2023-12-04 13:58:48 +08:00
可以开 web trace 级 log ,然后打开 spring.mvc.log-request-details =true 就能在日志里看请求参数和载荷信息。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/996794

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX