V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
likeme
V2EX  ›  Java

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

  •  
  •   likeme · 350 天前 · 2641 次点击
    这是一个创建于 350 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我的接口

    腾讯签名示例

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

    第 1 条附言  ·  350 天前
    我怀疑破案了,因为我们公司的脚手架有对请求数据做一些过滤,所以导致无法拿到最原始的,我重新创建了一个 spring 脚手架,测试了是用 @RequestBody String body 就可以拿到原始的。
    13 条回复    2023-12-04 13:58:48 +08:00
    likeme
        1
    likeme  
    OP
       350 天前
    chatgpt 已经问烂了,实在是没办法了发到 V2EX 让老哥们给看看。
    NULL2020
        2
    NULL2020  
       350 天前
    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
        3
    chendy  
       350 天前
    随手测试了一下,除非做了什么特殊处理,否则 ReqeustBody String 拿到的就是原始字符串,特殊字符不会被去掉
    至于 /n 和 /t 被去掉,有没有可能是并没有被去掉只是打印看不到呢…
    lisongeee
        4
    lisongeee  
       350 天前
    如果使用 @RequestBody ByteArray body 获取是否可行?
    infoscope
        5
    infoscope  
       350 天前
    直接从 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
        6
    rimwindy  
       350 天前
    不行就再转一次吧,过来的 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
        7
    likeme  
    OP
       350 天前
    @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
        8
    Masoud2023  
       350 天前
    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
        9
    likeme  
    OP
       350 天前   ❤️ 1
    @Masoud2023 我怀疑破案了,因为我们公司的脚手架有对请求数据做一些过滤,所以导致无法拿到最原始的,我重新创建了一个 spring 脚手架,测试了是用 @RequestBody String body 就可以拿到原始的。
    Masoud2023
        10
    Masoud2023  
       350 天前
    HttpServletRequestCopier copier = new HttpServletRequestCopier((HttpServletRequest) servletRequest);
    filterChain.doFilter(copier, servletResponse);
    xiaoxinTOm
        11
    xiaoxinTOm  
       350 天前
    controller 拿的 body ?可能过滤器对请求体做了二次处理,处理过后的才放行到 controller 中,我以前看到的代码有这么做,你去看看过滤器把 filter 把
    likeme
        12
    likeme  
    OP
       347 天前
    @xiaoxinTOm 是的,用的是 ruoyi 的框架,有一个过滤器导致的。
    bill110100
        13
    bill110100  
       347 天前
    可以开 web trace 级 log ,然后打开 spring.mvc.log-request-details =true 就能在日志里看请求参数和载荷信息。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1186 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 59ms · UTC 18:31 · PVG 02:31 · LAX 10:31 · JFK 13:31
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.