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

springboot接口如何多次獲取request中的body內(nèi)容

 更新時間:2021年06月29日 09:37:47   作者:fragrans  
這篇文章主要介紹了springboot接口多次獲取request中的body內(nèi)容的過程,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

1. 概述

在使用springboot開發(fā)接口時,會將參數(shù)轉(zhuǎn)化為Bean,用來進行參數(shù)的自動校驗。同時也想獲取request中原始body報文進行驗簽(防止報文傳輸過程中被篡改)。

因為通過將bean再轉(zhuǎn)化為字符串后,body里面的報文格式、字段順序會發(fā)生改變,就會導致驗簽失敗。因此只能通過request來獲取body里面的內(nèi)容。

既想接口自動實現(xiàn)參數(shù)校驗,同時又想獲取request中的原始報文,因此我們可以通過在controller中的restful方法中,寫入兩個參數(shù),獲取多次request中的body內(nèi)容。

那么如何實現(xiàn)多次讀取body內(nèi)容了(因為request里的body是以字節(jié)流的方式讀取的,默認情況下讀取一次,字節(jié)流就沒有了),下面就來大致分析一下。

2 接口接收參數(shù)的其他方式

2.1 接收參數(shù)方法一

方法一、

public R list(@RequestBody String rawMsg)

采用上述方式可以直接獲得請求報文中的原始body信息,而且當body是一個json字符串時,rawMsg參數(shù)接口到的body值,不會改變json中key的順序,即與發(fā)送方的body內(nèi)容是保持一致的。這種方式可以用來對報文驗簽,因為被加密的字符串與發(fā)送方是保持一致的。

這種方式可以接受request里面body內(nèi)容的原始格式,保持與發(fā)送方一致。

如下就可以對原始報文進行驗簽操作了

// 用公鑰,對原始報文進行驗簽,在這里如果rawMsg里面是json時,當key的順序改變后,會驗簽失敗,
//如此我們可以通過request來獲取body里面的原始報文
boolean verifyResult = SignVerifyUtils.verifySignature(rawMsg, Constant.NPIS_PUBLIC_KEY);

2.2 接收參數(shù)方法二

方法二、

public R list(@RequestBody @Validated ReqBean<ABCReqBean> abcReqBean)

這種接受參數(shù)的方法,可以將request里的json報文,直接轉(zhuǎn)換成對應的bean對象。并且可以用來校驗參數(shù),例如某個字段是必傳的、某個字段的值最大是多少等等。例如

    @NotNull(message = "日期字段不允許為空")
    @Size(min = 8, max = 8, message = "日期字符串的長度必須為 8")
    private String beginDate;

有沒有一種方法,既能同時利用參數(shù)校驗功能,又能獲取原始body里的內(nèi)容來進行驗簽呢,這時候就可以采用下面的第3中方法。

2.3 接收參數(shù)方法三

@RequestMapping(method = {RequestMethod.POST}, value = "/dataQry") 
public R list(@RequestBody @Validated ReqBean<ABCReqBean> abcReqBean,HttpServletRequest request){
}

在這里就可以通過將報文轉(zhuǎn)換成abcReqBean對象,并實現(xiàn)接口參數(shù)的自動校驗功能;同時可以利用request獲取原始報文來進行驗簽。

注意:由于在接收參數(shù)時,HttpServletRequest只能讀取一次body內(nèi)容(因為是讀的字節(jié)流,讀完就沒了),因此我們需要需要做特殊處理,

下面來看一種基于SpringBoot來解決HttpServletRequest只能讀取一次的問題。

2.3.1 繼承HttpServletRequestWrapper包裝類,每次讀取body后,再將參數(shù)寫會request

為解決上述多次讀取request中的body內(nèi)容的問題,我們只需要將以下兩個類,放到項目中即可,并通過@Component來注測為spring bean即可

繼承HttpServletRequestWrapper ,實現(xiàn)每次讀取request中的body后,在將內(nèi)容寫回request。

package com.abcd.config; 
import org.apache.commons.io.IOUtils; 
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
 
/**
 * @author:
 */
public class RequestWrapper extends HttpServletRequestWrapper {
    //參數(shù)字節(jié)數(shù)組
    private byte[] requestBody;
    //Http請求對象
    private HttpServletRequest request; 
    public RequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        this.request = request;
    }
 
    /**
     * @return
     * @throws IOException
     */
    @Override
    public ServletInputStream getInputStream() throws IOException {
        /**
         * 每次調(diào)用此方法時將數(shù)據(jù)流中的數(shù)據(jù)讀取出來,然后再回填到InputStream之中
         * 解決通過@RequestBody和@RequestParam(POST方式)讀取一次后控制器拿不到參數(shù)問題
         */
        if (null == this.requestBody) {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            IOUtils.copy(request.getInputStream(), baos);
            this.requestBody = baos.toByteArray();
        }
 
        final ByteArrayInputStream bais = new ByteArrayInputStream(requestBody);
        return new ServletInputStream() {
 
            @Override
            public boolean isFinished() {
                return false;
            }
 
            @Override
            public boolean isReady() {
                return false;
            }
 
            @Override
            public void setReadListener(ReadListener listener) {
 
            }
 
            @Override
            public int read() {
                return bais.read();
            }
        };
    }
 
    public byte[] getRequestBody() {
        return requestBody;
    }
 
    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(this.getInputStream()));
    }
}

2.3.2 將包裝類加入過濾器鏈

回寫參數(shù)的包裝類寫好之后接下來就是加入過濾器鏈之中,如下:

package com.abcd.config; 
import org.springframework.stereotype.Component; 
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
 * @author:
 */
@Component
@WebFilter(filterName = "channelFilter", urlPatterns = {"/*"})
public class ChannelFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }
 
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
        try {
            ServletRequest requestWrapper = null;
            if (request instanceof HttpServletRequest) {
                requestWrapper = new RequestWrapper((HttpServletRequest) request);
            }
            if (requestWrapper == null) {
                chain.doFilter(request, response);
            } else {
                chain.doFilter(requestWrapper, response);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ServletException e) {
            e.printStackTrace();
        } 
    }
 
    @Override
    public void destroy() {
    } 
}

解決springboot v2.2以上重復讀取request body內(nèi)容問題

一、需求

項目有兩個場景會用到從Request的Body中讀取內(nèi)容。

1、打印請求日志

2、提供Api接口,在api方法執(zhí)行前,從Request Body中讀取參數(shù)進行驗簽,驗簽通過后在執(zhí)行api方法

二、解決方案

2.1 自定義RequestWrapper

public class MyRequestWrapper extends HttpServletRequestWrapper {
	private final String body;
	public MyRequestWrapper(HttpServletRequest request) throws IOException {
		super(request);
		this.body = RequestReadUtils.read(request);
	}
	public String getBody() {
		return body;
	}
	@Override
	public ServletInputStream getInputStream() throws IOException {
		final ByteArrayInputStream bais = new ByteArrayInputStream(body.getBytes());
		
		return new ServletInputStream() {
			...略
		};
	}
	@Override
	public BufferedReader getReader() throws IOException {
		return new BufferedReader(new InputStreamReader(this.getInputStream()));
	}
}

RequestReadUtils(網(wǎng)上抄的)

private static final int BUFFER_SIZE = 1024 * 8;
	 
    public static String read(HttpServletRequest request) throws IOException {
        BufferedReader bufferedReader = request.getReader();
        for (Enumeration<String> iterator = request.getHeaderNames(); iterator.hasMoreElements();) {
        	String type = iterator.nextElement();
			System.out.println(type+" = "+request.getHeader(type));
		}
        System.out.println();
        StringWriter writer = new StringWriter();
        write(bufferedReader,writer);
        return writer.getBuffer().toString();
    }
 
    public static long write(Reader reader,Writer writer) throws IOException {
        return write(reader, writer, BUFFER_SIZE);
    }
 
    public static long write(Reader reader, Writer writer, int bufferSize) throws IOException
    {
        int read;
        long total = 0;
        char[] buf = new char[bufferSize];
        while( ( read = reader.read(buf) ) != -1 ) {
            writer.write(buf, 0, read);
            total += read;
        }
        return total;
    }

2.2 定義Filter

@WebFilter
public class TestFilter implements Filter{
 @Override
 public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain){
  HttpServletRequest request = (HttpServletRequest) servletRequest;
  HttpServletResponse response = (HttpServletResponse) servletResponse;
  
  MyRequestWrapper wrapper = WebUtils.getNativeRequest(request, MyRequestWrapper.class);
  chain.doFilter(wrapper == null ? new MyRequestWrapper(request) :wrapper,servletRequest);
 }
}

三、遇到問題

使用的SpringBoot v2.1.x版本

1、Form提交無問題

2、獲取RequestBody無問題

使用SpringBoot v2.2.0以上版本(包括v2.3.x)

1、Form提交無法獲取參數(shù)

2、獲取RequestBody無問題

四、問題排查

經(jīng)過排查,v2.2.x對比v2.1.x的不同在于一下代碼差異:

BufferedReader bufferedReader = request.getReader();
-----------------
char[] buf = new char[bufferSize];
while( ( read = reader.read(buf) ) != -1 ) {
    writer.write(buf, 0, read);
    total += read;
}

當表單提交時

1、v2.1.x無法read到內(nèi)容,讀取結(jié)果為-1

2、v2.2.x、v2.3.x能夠讀取到內(nèi)容

當表單提交時(x-www-form-urlencoded),inputStream讀取一次后后續(xù)不會觸發(fā)wrapper的getInputStream操作,所以Controller無法獲取到參數(shù)。

解決方案

MyRequestWrapper改造

public class MyRequestWrapper extends HttpServletRequestWrapper {
	private final String body;
	public MyRequestWrapper(HttpServletRequest request) throws IOException {
		super(request);
		this.body = getBodyString(request);
	}
	public String getBody() {
		return body;
	}
	
	public String getBodyString(final HttpServletRequest request) throws IOException {
	    String contentType = request.getContentType();
	    String bodyString = "";
	    StringBuilder sb = new StringBuilder();
	    if (StringUtils.isNotBlank(contentType) && (contentType.contains("multipart/form-data") || contentType.contains("x-www-form-urlencoded"))) {
	        Map<String, String[]> parameterMap = request.getParameterMap();
	        for (Map.Entry<String, String[]> next : parameterMap.entrySet()) {
	            String[] values = next.getValue();
	            String value = null;
	            if (values != null) {
	                if (values.length == 1) {
	                    value = values[0];
	                } else {
	                    value = Arrays.toString(values);
	                }
	            }
	            sb.append(next.getKey()).append("=").append(value).append("&");
	        }
	        if (sb.length() > 0) {
	            bodyString = sb.toString().substring(0, sb.toString().length() - 1);
	        }
	        return bodyString;
	    } else {
	        return IOUtils.toString(request.getInputStream());
	    }
	}
	@Override
	public ServletInputStream getInputStream() throws IOException {
		final ByteArrayInputStream bais = new ByteArrayInputStream(body.getBytes());
		
		return new ServletInputStream() {
			@Override
			public boolean isFinished() {
				return false;
			}
			@Override
			public boolean isReady() {
				return false;
			}
			@Override
			public int read() {
				return bais.read();
			}
			@Override
			public void setReadListener(ReadListener readListener) {
			}
		};
	}
	@Override
	public BufferedReader getReader() throws IOException {
		return new BufferedReader(new InputStreamReader(this.getInputStream()));
	}
}

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

相關(guān)文章

  • Java連接ftp服務器實例代碼

    Java連接ftp服務器實例代碼

    這篇文章主要介紹了Java連接ftp服務器實例代碼 的相關(guān)資料,需要的朋友可以參考下
    2015-12-12
  • Java高性能序列化工具Kryo詳情

    Java高性能序列化工具Kryo詳情

    這篇文章主要介紹了Java高性能序列化工具Kryo詳情,Kryo?是一個快速序列化/反序列化工具,依賴于字節(jié)碼生成機制,更多相關(guān)內(nèi)容感興趣的朋友可以參考一下下面文章內(nèi)容
    2022-06-06
  • jsp中EL表達式獲取數(shù)據(jù)

    jsp中EL表達式獲取數(shù)據(jù)

    EL 全名為Expression Language。本文給大家介紹的是在jsp中EL表達式獲取數(shù)據(jù)的幾種方式,希望大家能夠喜歡
    2016-07-07
  • Redis打開rdb文件常用方法詳解

    Redis打開rdb文件常用方法詳解

    這篇文章主要介紹了Redis打開rdb文件常用方法詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-09-09
  • Java輕松實現(xiàn)批量插入或刪除Excel行列操作

    Java輕松實現(xiàn)批量插入或刪除Excel行列操作

    在職場生活中,對Excel工作表的行和列進行操作是非常普遍的需求,下面小編就來和大家介紹一下如何在Java中完成批量插入、刪除行和列的操作吧
    2023-10-10
  • Java Spring-IOC容器與Bean管理之基于注解的方式案例詳解

    Java Spring-IOC容器與Bean管理之基于注解的方式案例詳解

    這篇文章主要介紹了Java Spring-IOC容器與Bean管理之基于注解的方式案例詳解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • java 異常之手動拋出與自動拋出的實例講解

    java 異常之手動拋出與自動拋出的實例講解

    這篇文章主要介紹了java 異常之手動拋出與自動拋出的實例講解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-02-02
  • Java?C++題解leetcode672燈泡開關(guān)示例

    Java?C++題解leetcode672燈泡開關(guān)示例

    這篇文章主要為大家介紹了Java?C++題解leetcode672燈泡開關(guān)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-09-09
  • Windows下apache ant安裝、環(huán)境變量配置教程

    Windows下apache ant安裝、環(huán)境變量配置教程

    這篇文章主要介紹了Windows下apache ant安裝、環(huán)境變量配置教程,ANT的安裝很簡單,本文同時講解了驗證安裝是否成功的方法和使用方法實例,需要的朋友可以參考下
    2015-06-06
  • Spring StopWatch使用實例詳解

    Spring StopWatch使用實例詳解

    這篇文章主要介紹了Spring StopWatch使用實例詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-01-01

最新評論