欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringBoot如何配置獲取request中body的json格式參數(shù)

 更新時間:2022年06月30日 09:41:26   作者:Duktig丶  
這篇文章主要介紹了SpringBoot如何配置獲取request中body的json格式參數(shù),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

背景

最近開發(fā)項目,因為有第三方調(diào)用我們的接口,我們使用SpringBoot以JavaBean的方式接收了我們預期的參數(shù),參數(shù)接收也沒有什么異常。但是有一些需求問題需要溝通,需要拿到合作第三方傳入的所有參數(shù),來進行參數(shù)核驗。

如何拿到請求的所有參數(shù)呢?正常的思路肯定是從request中獲取,如果是GET請求,參數(shù)在請求路徑中拼接;如果是POST請求,參數(shù)在request的請求體(body)中。

一番檢索,很容易拿到相關(guān)代碼。但是經(jīng)過實操,發(fā)現(xiàn)并不能如期獲取到參數(shù)。經(jīng)過思考,我的接口是POST請求,參數(shù)形式是json格式(使用了@RequestBody來修飾參數(shù))。

具體過程參看如下分析

獲取請求中的參數(shù)(非json格式參數(shù))

獲取方法

方法一

Enumeration<String> parameterNames = request.getParameterNames();
while (parameterNames.hasMoreElements()) {
    String paraName = parameterNames.nextElement();
    System.out.println(paraName + ": " + request.getParameter(paraName));
}

方法二

Map<String, String[]> map = request.getParameterMap();
Set<Map.Entry<String, String[]>> keSet = map.entrySet();
for (Iterator<Map.Entry<String, String[]>> itr = keSet.iterator(); itr.hasNext(); ) {
    Map.Entry<String, String[]> me = itr.next();
    Object ok = me.getKey();
    Object ov = me.getValue();
    String[] value = new String[1];
    if (ov != null) {
        value = (String[]) ov;
    } else {
        value[0] = ov.toString();
    }
    for (int k = 0; k < value.length; k++) {
        System.out.println(ok + "=" + value[k]);
    }
}

結(jié)論

經(jīng)過測試以上兩個方法可以獲取GET請求的參數(shù),以及參數(shù)格式為form-data、x-www-form-urlencoded的POST請求,但是json格式參數(shù)(postman中為raw)的參數(shù)不能獲得。

根據(jù)代碼的簡介程度,選擇方法一,明顯更舒服一些。

以上結(jié)論經(jīng)過postman實測.

獲取POST請求json格式的參數(shù)

以上方法已經(jīng)可以獲取大多數(shù)情況下的請求的參數(shù),但是明顯還不能滿足需求,需要獲取傳入json格式的參數(shù)。 

經(jīng)過檢索推薦方法(參看后邊完整方法)

經(jīng)過一番檢索,網(wǎng)上推薦的方法一般都是使用流來進行參數(shù)讀取,即使用getInputStream()和getReader():

Map<String, Object> params = new HashMap<>();
BufferedReader br = null;
try {
    try {
        br = request.getReader();
    } catch (IOException e) {
        e.printStackTrace();
    }
    String str;
    StringBuilder wholeStr = new StringBuilder();
    while ((str = Objects.requireNonNull(br).readLine()) != null) {
        wholeStr.append(str);
    }
    if (StrUtil.isNotEmpty(wholeStr.toString())) {
        params = JSON.parseObject(wholeStr.toString(), Map.class);
    }
} catch (IOException e) {
    e.printStackTrace();
}
System.out.println(params);

遇到的問題及解決思路

問題1 流不能多次被調(diào)用

但是又會遇到如下問題:

ERROR m.e.handler.GlobalExceptionHandler - getInputStream() has already been called for this request
java.lang.IllegalStateException: getInputStream() has already been called for this request
    at org.apache.catalina.connector.Request.getReader(Request.java:1212)
    at org.apache.catalina.connector.RequestFacade.getReader(RequestFacade.java:504)

根據(jù)報錯信息分析簡單來說,就是getInputStream()已經(jīng)被調(diào)用了,不能再次調(diào)用??墒俏铱创a上,我也沒調(diào)用。經(jīng)過一番檢索,原來@RequestBody注解配置后,默認會使用流來讀取數(shù)據(jù)。

具體原因:

  • 默認配置時,getInputStream()和getReader()一起使用會報錯,使用兩遍getInputStream(),第二遍會為空。
  • 當存在@RequestBody等注解時,springMVC已讀取過一遍流,默認單獨使用getInputStream()或getReader()都為空。

實測,不加@RequestBody注解,可以如期獲得請求中的json參數(shù),但是又不得不加@RequestBody注解。這樣就需要新的思路

解決思路

寫filter繼承HttpServletRequestWrapper,緩存InputStream,覆蓋getInputStream()和getReader()方法,使用ByteArrayInputStream is = new ByteArrayInputStream(body.getBytes());讀取InputStream。

實現(xiàn)方法

1.定義增強類,繼承繼承HttpServletRequestWrapper

將請求體中的流copy一份,覆寫getInputStream()和getReader()方法供外部使用。每次調(diào)用覆寫后的getInputStream()方法都是從復制出來的二進制數(shù)組中進行獲取,這個二進制數(shù)組在對象存在期間一直存在,這樣就實現(xiàn)了流的重復讀取。

public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {
    private byte[] body;
    public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        body = HttpHelper.getBodyString(request).getBytes(StandardCharsets.UTF_8);
    }
    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }
    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream bais = new ByteArrayInputStream(body);
        return new ServletInputStream() {
            @Override
            public int read() throws IOException {
                return bais.read();
            }
            @Override
            public boolean isFinished() {
                return false;
            }
            @Override
            public boolean isReady() {
                return false;
            }
            @Override
            public void setReadListener(ReadListener readListener) {
            }
        };
    }
    public void setInputStream(byte[] body) {
        this.body = body;
    }
}

2.構(gòu)建過濾器

@Slf4j
@WebFilter(filterName = "RequestWrapperFilter", urlPatterns = "/api/test/test2")
public class RequestWrapperFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException
            , IOException {
        ServletRequest requestWrapper = null;
        if (request instanceof HttpServletRequest) {
            requestWrapper = new BodyReaderHttpServletRequestWrapper((HttpServletRequest) request);
        }
        if (null == requestWrapper) {
            log.error("過濾器包裝request失敗!將返回原來的request");
            chain.doFilter(request, response);
        } else {
            log.info("過濾器包裝request成功");
            chain.doFilter(requestWrapper, response);
        }
    }
    @Override
    public void destroy() {
    }
}

注意事項:

  • 使用@WebFilter注解的urlPatterns屬性,可指定過濾哪一個接口方法。
  • 過濾器類最好不要交由Spring管理,否則每一個接口都會進行過濾(實測)。例如:不要添加@Component注解. 3.編寫工具類方便調(diào)用
/**
 * description:http輔助工具類
 *
 * @author RenShiWei
 * Date: 2021/5/7 22:11
 **/
public class HttpHelper {
    /**
     * description:從request獲取body的json數(shù)據(jù)
     *
     * @param request /
     * @return /
     * @author RenShiWei
     * Date: 2021/5/7 22:44
     */
    public static String getBodyString(ServletRequest request) {
        StringBuilder sb = new StringBuilder();
        ServletInputStream inputStream = null;
        BufferedReader reader = null;
        try {
            inputStream = request.getInputStream();
            reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
            String line = "";
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return sb.toString();
    }
    /**
     * description:從request獲取body的json數(shù)據(jù),并格式化成map形式
     *
     * @param request /
     * @return /
     * @author RenShiWei
     * Date: 2021/5/7 22:44
     */
    @SuppressWarnings("all")
    public static Map<String, Object> getBodyMap(ServletRequest request) {
        Map<String, Object> params = new HashMap<>();
        String bodyString = getBodyString(request);
        if (StrUtil.isNotEmpty(bodyString)) {
            params = JSON.parseObject(bodyString, Map.class);
        }
        return params;
    }
}

4.在SpringBoot啟動類上添加@ServletComponentScan注解

使用

@RestController
@RequestMapping("/api/test")
public class TestController {
    @GetMapping("/test1")
    @AnonymousAccess
    public ResponseEntity<String> test1(HttpServletRequest request) {
        System.out.println("---GET請求 getParameterNames 入?yún)?--");
        try {
            request.setCharacterEncoding("utf-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        Enumeration<String> parameterNames = request.getParameterNames();
        while (parameterNames.hasMoreElements()) {
            String paraName = parameterNames.nextElement();
            System.out.println(paraName + ": " + request.getParameter(paraName));
        }
        System.out.println("---GET請求 getParameterMap 入?yún)?--");
        Map<String, String[]> map = request.getParameterMap();
        Set<Map.Entry<String, String[]>> keSet = map.entrySet();
        for (Iterator<Map.Entry<String, String[]>> itr = keSet.iterator(); itr.hasNext(); ) {
            Map.Entry<String, String[]> me = itr.next();
            Object ok = me.getKey();
            Object ov = me.getValue();
            String[] value = new String[1];
            if (ov != null) {
                value = (String[]) ov;
            } else {
                value[0] = ov.toString();
            }
            for (int k = 0; k < value.length; k++) {
                System.out.println(ok + "=" + value[k]);
            }
        }
        return ResponseEntity.ok(null);
    }
    @PostMapping("/test2")
    @AnonymousAccess
    public ResponseEntity<String> test2(@RequestBody TestParam testParam, HttpServletRequest request) {
        String body = HttpHelper.getBodyString(request);
        Map<String, Object> bodyMap = HttpHelper.getBodyMap(request);
        System.out.println("body: " + body);
        System.out.println("bodyMap: " + bodyMap);
        return ResponseEntity.ok(null);
    }
}

postman測試

GET請求

POST的JSON格式參數(shù)(其他方式結(jié)果與GET請求結(jié)果一致)

結(jié)果

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java解釋器的運行過程介紹

    Java解釋器的運行過程介紹

    今天小編就為大家分享一篇關(guān)于Java解釋器的運行過程介紹,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-04-04
  • SpringBoot如何配置Controller實現(xiàn)Web請求處理

    SpringBoot如何配置Controller實現(xiàn)Web請求處理

    這篇文章主要介紹了SpringBoot如何配置Controller實現(xiàn)Web請求處理,文中通過圖解示例介紹的很詳細,具有有一定的參考價值,需要的小伙伴可以參考一下
    2023-05-05
  • java 如何實現(xiàn)日志追蹤MDC

    java 如何實現(xiàn)日志追蹤MDC

    這篇文章主要介紹了java 實現(xiàn)日志追蹤MDC方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • 詳解Java包裝類及自動裝箱拆箱

    詳解Java包裝類及自動裝箱拆箱

    這篇文章主要介紹了Java包裝類及自動裝箱拆箱,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-03-03
  • Spring依賴注入與第三方Bean管理基礎詳解

    Spring依賴注入與第三方Bean管理基礎詳解

    依賴注入(Dependency Injection)和控制反轉(zhuǎn)(Inversion of Control)是同一個概念。具體含義是:當某個角色(可能是一個Java實例,調(diào)用者)需要另一個角色(另一個Java實例,被調(diào)用者)的協(xié)助時,在 傳統(tǒng)的程序設計過程中,通常由調(diào)用者來創(chuàng)建被調(diào)用者的實例
    2022-12-12
  • Mybatis 查詢語句條件為枚舉類型時報錯的解決

    Mybatis 查詢語句條件為枚舉類型時報錯的解決

    這篇文章主要介紹了Mybatis 查詢語句條件為枚舉類型時報錯的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • Java并發(fā)容器相關(guān)知識總結(jié)

    Java并發(fā)容器相關(guān)知識總結(jié)

    今天給大家?guī)淼奈恼率荍ava并發(fā)容器的相關(guān)知識,文中有非常詳細的介紹,對正在學習Java并發(fā)容器的小伙伴們很有幫助,需要的朋友可以參考下
    2021-06-06
  • Java定位問題線程解析

    Java定位問題線程解析

    這篇文章主要介紹了Java定位問題線程,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • Java中實現(xiàn)二叉樹的遍歷與重構(gòu)

    Java中實現(xiàn)二叉樹的遍歷與重構(gòu)

    這篇文章主要介紹了Java中實現(xiàn)二叉樹的遍歷與重構(gòu),樹是一種非線性的數(shù)據(jù)結(jié)構(gòu),它是由n(n>=0)個有限結(jié)點組成一個具有層次關(guān)系的集合,把它叫做樹是因為它看起來像一棵倒掛的樹,也就是說它是根朝上,而葉朝下的,需要的朋友可以參考下
    2023-10-10
  • java中的session對象如何獲取

    java中的session對象如何獲取

    這篇文章主要介紹了java中的session對象如何獲取,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11

最新評論