精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

如何在 Spring Boot 應用程序中記錄POST請求的body信息?

開發 前端
我們可以自己定義一個類CustomHttpRequestWrapper?,繼承自HttpServletRequestWrapper?,定義一個成員變量bodyInStringFormat?,存儲body中獲取到的數據,其實字符串底層是字節數組,然后重寫getInputStream?方法,構造一個ByteArrayInputStream?輸入流,而ByteArrayInputStream?實現了ma

前言

最近收到一個需求,出于審計的目的,希望可以通過日志記錄下對應用程序發起的post、put請求的body內容,面對這樣的一個需求,大家是不是覺得很簡單,但是我在開發過程中還是遇到了問題,在本文中做一個分享。

輸入流只能讀取一次

既然要記錄所有的請求,我們可以創建一個過濾器LogRequestFilter, 統一攔截所有的請求,讀取里面的輸入流InputStream,我想大家都能想到把,具體代碼如下:

@Component
public class LogRequestFilter implements Filter {

private final Logger logger = LoggerFactory.getLogger(LogRequestFilter.class);

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
// 記錄post和put請求體內容
logPostOrPutRequestBody((HttpServletRequest) servletRequest);
filterChain.doFilter(servletRequest, servletResponse);
}

private void logPostOrPutRequestBody(HttpServletRequest httpRequest) throws IOException {
if(Arrays.asList("POST", "PUT").contains(httpRequest.getMethod())) {
String characterEncoding = httpRequest.getCharacterEncoding();
Charset charset = Charset.forName(characterEncoding);
// 讀取輸入流轉為字符串
String bodyInStringFormat = readInputStreamInStringFormat(httpRequest.getInputStream(), charset);
logger.info("Request body: {}", bodyInStringFormat);
}
}

private String readInputStreamInStringFormat(InputStream stream, Charset charset) throws IOException {
final int MAX_BODY_SIZE = 1024;
final StringBuilder bodyStringBuilder = new StringBuilder();
if (!stream.markSupported()) {
stream = new BufferedInputStream(stream);
}

stream.mark(MAX_BODY_SIZE + 1);
final byte[] entity = new byte[MAX_BODY_SIZE + 1];
// 讀取流
final int bytesRead = stream.read(entity);

if (bytesRead != -1) {
bodyStringBuilder.append(new String(entity, 0, Math.min(bytesRead, MAX_BODY_SIZE), charset));
if (bytesRead > MAX_BODY_SIZE) {
bodyStringBuilder.append("...");
}
}
stream.reset();

return bodyStringBuilder.toString();
}

}

但是事情往往不是按照你預期的方向發展的, 但你按照上面的設計寫好代碼后,發一個post請求,卻返回下面的報錯:

DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: 
Required request body is missing

為什么會報錯呢?

原因就是輸入流只能讀取一次。 當我們調用getInputStream()方法獲取輸入流時得到的是一個InputStream對象,而實際類型是ServletInputStream,它繼承于InputStream。

InputStream的read()方法內部有一個postion,標志當前流被讀取到的位置,每讀取一次,該標志就會移動一次,如果讀到最后,read()會返回-1,表示已經讀取完了。如果想要重新讀取則需要調用reset()方法,position就會移動到上次調用mark的位置,mark默認是0,所以就能從頭再讀了。調用reset()方法的前提是已經重寫了reset()方法,當然能否reset也是有條件的,它取決于markSupported()方法是否返回true。

InputStream默認不實現reset(),并且markSupported()默認也是返回false,這一點查看InputStream源碼便知:

圖片

我們再來看看ServletInputStream,可以看到該類沒有重寫mark(),reset()以及markSupported()方法:

圖片

所以InputStream默認不實現reset的相關方法,而ServletInputStream也沒有重寫reset的相關方法,這樣就無法重復讀取流,這就是我們從request對象中獲取的輸入流就只能讀取一次的原因,最后導致再次讀取流的時候報錯。

那該如何解決呢?

改寫ServeltRequest

既然ServletInputStream不支持重新讀寫,那么為什么不把流讀出來后用容器存儲起來,后面就可以多次利用了。那么問題就來了,要如何存儲這個流呢?

所幸JavaEE提供了一個 HttpServletRequestWrapper類,從類名也可以知道它是一個http請求包裝器,其基于裝飾者模式實現了HttpServletRequest界面,部分源碼如下:

圖片

從上圖中的部分源碼可以看到,該類并沒有真正去實現HttpServletRequest的方法,而只是在方法內又去調用HttpServletRequest的方法,所以我們可以通過繼承該類并實現想要重新定義的方法以達到包裝原生HttpServletRequest對象的目的。

我們可以自己定義一個類CustomHttpRequestWrapper,繼承自HttpServletRequestWrapper,定義一個成員變量bodyInStringFormat,存儲body中獲取到的數據,其實字符串底層是字節數組,然后重寫getInputStream方法,構造一個ByteArrayInputStream輸入流,而ByteArrayInputStream實現了mark(),reset()以及markSupported()方法,然后讓ByteArrayInputStream去讀取前面保存的字符串bodyInStringFormat中的數組,從而達到重復使用的目的。

package com.filters;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.nio.charset.Charset;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class CustomHttpRequestWrapper extends HttpServletRequestWrapper {

private static final Logger logger = LoggerFactory.getLogger(CustomHttpRequestWrapper.class);
private final String bodyInStringFormat;

public CustomHttpRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
bodyInStringFormat = readInputStreamInStringFormat(request.getInputStream(), Charset.forName(request.getCharacterEncoding()));
logger.info("Body: {}", bodyInStringFormat);
}


private String readInputStreamInStringFormat(InputStream stream, Charset charset) throws IOException {
final int MAX_BODY_SIZE = 1024;
final StringBuilder bodyStringBuilder = new StringBuilder();
if (!stream.markSupported()) {
stream = new BufferedInputStream(stream);
}

stream.mark(MAX_BODY_SIZE + 1);
final byte[] entity = new byte[MAX_BODY_SIZE + 1];
final int bytesRead = stream.read(entity);

if (bytesRead != -1) {
bodyStringBuilder.append(new String(entity, 0, Math.min(bytesRead, MAX_BODY_SIZE), charset));
if (bytesRead > MAX_BODY_SIZE) {
bodyStringBuilder.append("...");
}
}
stream.reset();

return bodyStringBuilder.toString();
}

@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}

@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bodyInStringFormat.getBytes());

return new ServletInputStream() {
private boolean finished = false;

@Override
public boolean isFinished() {
return finished;
}

@Override
public int available() throws IOException {
return byteArrayInputStream.available();
}

@Override
public void close() throws IOException {
super.close();
byteArrayInputStream.close();
}

@Override
public boolean isReady() {
return true;
}

@Override
public void setReadListener(ReadListener readListener) {
throw new UnsupportedOperationException();
}

public int read () throws IOException {
int data = byteArrayInputStream.read();
if (data == -1) {
finished = true;
}
return data;
}
};
}
}

編寫玩上面的代碼以后,還需要再過濾器中使用,那么后續過濾器中的ServletRequest實現類都是CustomHttpRequestWrapper , 就可以再次讀取body的內容了,具體代碼如下:

@Component
public class LogRequestFilter implements Filter {

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {

HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
if(Arrays.asList("POST", "PUT").contains(httpRequest.getMethod())) {
// 設置自定義的ServletRequest
CustomHttpRequestWrapper requestWrapper = new CustomHttpRequestWrapper(httpRequest);
filterChain.doFilter(requestWrapper, servletResponse);
return;
}
filterChain.doFilter(servletRequest, servletResponse);
}

}

這一下你再次向應用程序發出POST或GET請求時,就不會看到任何報錯了。

責任編輯:武曉燕 來源: JAVA旭陽
相關推薦

2018-10-29 10:13:29

Windows 10應用程序卸載

2019-08-13 15:39:27

Linux應用程序

2024-01-18 07:53:37

2018-03-28 08:30:01

Linux倉庫應用程序

2019-05-08 11:30:41

MicrosoftWindows 10后臺應用程序

2018-08-02 11:15:06

應用程序Windows 10Windows

2019-12-06 10:05:28

Windows 10手機應用程序

2009-08-12 17:36:32

2011-01-28 09:12:53

jQuery Mobi

2016-08-02 10:34:17

LinuxWindows雙啟動

2021-05-07 15:36:50

iOS隱藏應用程序

2011-05-18 10:42:48

2021-01-30 17:57:23

Python緩存開發

2013-03-25 10:38:24

ASP.NETHttpModule

2021-05-10 23:39:31

Python日志記錄

2024-01-15 08:03:10

JVM內存工作效率

2014-06-26 15:17:17

安卓應用保存數據

2019-01-04 10:45:31

Windows 10Android應用程序

2022-04-27 08:55:01

Spring外部化配置

2019-07-17 15:23:23

Windows 10應用程序Windows
點贊
收藏

51CTO技術棧公眾號

日韩高清在线| 国产日韩欧美亚洲一区| 伊人久久大香线蕉成人综合网 | 久久精品日韩欧美| 欧美一卡二卡三卡| 日韩欧美视频一区二区三区四区| 国产亚洲精品久久久久久无几年桃| 欧美videos粗暴| 久久精品人人做人人综合| 国模精品一区二区三区色天香| 一个色综合久久| 国外av在线| 久久综合社区| 亚洲精品高清在线观看| 国产在线观看91精品一区| 国产精品一二三区在线观看| 香蕉伊大人中文在线观看| 成人av网站在线观看免费| 欧美激情一级欧美精品| 9191在线视频| 欧美亚洲天堂| 粉嫩13p一区二区三区| 美女性感视频久久久 | 国内精品写真在线观看| 日韩小视频在线| 成 人 黄 色 小说网站 s色| 麻豆mv在线看| 91丨porny丨蝌蚪视频| 欧美在线观看网站| 成人精品999| 国产私拍福利精品视频二区| 亚洲伊人色欲综合网| 成人av免费在线看| 国产无码精品在线观看| 欧美aaaaaaaa牛牛影院| 日本精品一级二级| 亚洲精品中字| 国产理论片在线观看| 欧美国产高潮xxxx1819| 亚洲成年人在线| 成人午夜视频免费在线观看| 69久久夜色| 国产最新精品精品你懂的| 国产精品jvid在线观看蜜臀| 日本一二三区在线观看| 88久久精品| 日韩欧美精品中文字幕| 一区二区在线不卡| 国产精品一区在线看| 国产毛片一区二区| 亚州国产精品久久久| av电影在线不卡| 日本免费成人| 亚洲电影激情视频网站| 日韩精品一区二区三区外面 | 99精品国产99久久久久久白柏| 欧美一区二区三区……| 四虎免费在线视频| 色综合久久久| 欧美日本在线一区| 久久综合久久网| 黄色视屏网站在线免费观看| 久久免费精品国产久精品久久久久| 国产精品欧美日韩| 日本系列第一页| 97精品一区| 日韩国产一区三区| 天天av天天操| 日日av拍夜夜添久久免费| 亚洲精品videosex极品| 黄网站色视频免费观看| 国产高清免费在线播放| 日本一区二区不卡视频| 国产精品免费一区二区三区| 亚洲无码久久久久| 亚洲黄色高清| www.欧美精品| 色在线观看视频| 国产亚洲网站| 欧美高清视频在线| 欧美人与禽zoz0善交| 麻豆精品av| 亚洲偷欧美偷国内偷| 97香蕉碰碰人妻国产欧美| 9999精品视频| 欧美日韩在线播放三区| 国产91在线免费| 日本伦理一区二区| 亚洲欧洲精品一区二区三区| 欧美日韩精品久久| 日本高清视频网站| 国产69精品久久久久777| 国产麻豆一区二区三区在线观看| 玖玖综合伊人| 91香蕉视频在线| 亚洲国产日韩欧美| 另类视频在线| 欧美视频一二三区| 五月天婷婷激情视频| 亚洲欧美se| 欧美群妇大交群的观看方式| 欧美夫妇交换xxx| 成人av动漫| 精品国产91九色蝌蚪| 超碰人人cao| 中文字幕中文字幕精品| 国产视频在线观看一区二区| 国产精品无码在线| 99精品美女| www.亚洲免费视频| 免费在线观看黄网站| 国产精品普通话对白| 国产精品视频在线观看| 特级西西444www大胆免费看| 久久综合影视| 国产精品激情自拍| 中文字幕乱码人妻无码久久| 蜜桃传媒麻豆第一区在线观看| 国产精品久久久久久久久久| 丰满熟妇乱又伦| av成人动漫在线观看| 久久精品成人一区二区三区蜜臀| 午夜视频1000| 国产肉丝袜一区二区| 欧美图片激情小说| 精品国产18久久久久久二百| 日韩免费电影一区| 你懂的在线观看网站| 婷婷久久一区| 国产日本欧美视频| 久色视频在线| 色综合久久久久久久| 少妇激情一区二区三区| 日韩大尺度在线观看| 亚洲女人天堂色在线7777| 亚洲国产日韩一区无码精品久久久| 亚洲天堂久久| 日韩免费不卡av| 中文精品久久久久人妻不卡| 91丨porny丨蝌蚪视频| 和岳每晚弄的高潮嗷嗷叫视频| 午夜欧美激情| 亚洲精品福利在线| 麻豆精品免费视频| 国产亚洲精品v| 国产专区一区二区| 国产拍在线视频| 欧美色欧美亚洲另类二区| 亚洲午夜久久久久久久久红桃| 久久亚洲精品中文字幕蜜潮电影| 日韩av片免费在线观看| 国产在线电影| 欧美人伦禁忌dvd放荡欲情| 免费成人深夜天涯网站| 蓝色福利精品导航| 国产综合精品一区二区三区| 国产三级伦理在线| 欧洲av在线精品| 欧美一级片在线免费观看| 亚洲盗摄视频| 欧美在线亚洲在线| h狠狠躁死你h高h| 91性感美女视频| 波多野结衣家庭教师视频| 在线欧美激情| 九色精品免费永久在线| 综合久久中文字幕| 中文字幕一区二区三区四区| 北条麻妃在线观看| 中文字幕亚洲影视| 国产日韩欧美夫妻视频在线观看| 国产秀色在线www免费观看| 欧美性色xo影院| 神马久久久久久久久久久| 蓝色福利精品导航| 妞干网视频在线观看| 丰满少妇一区| 亚洲精品中文字幕有码专区| 黄色污污网站在线观看| av一二三不卡影片| 91av在线免费播放| 99热精品久久| 国产精品一区二区三区在线| 羞羞影院欧美| 亚洲美女av在线| 一级做a爱片性色毛片| 久久精品亚洲精品国产欧美 | 精品极品在线| 最近日韩中文字幕中文| 国产成人一级片| 国产·精品毛片| 日av中文字幕| 欧美在线免费一级片| 国内视频一区二区| 亚洲精品无播放器在线播放| 国内成人精品视频| 欧美边添边摸边做边爱免费| 欧美日韩一二三区| 久久丫精品久久丫| 国产精品亲子伦对白| 日韩中文字幕免费在线| 偷拍自拍一区| 亚洲精品免费在线视频| 超碰在线观看免费版| 精品视频在线看| 亚洲一区 视频| 国产精品久久久久久久久动漫| 午夜影院福利社| 亚洲国产高清一区二区三区| 色一情一乱一伦一区二区三区丨 | 国产精品无码专区| 国产一区二区三区精品欧美日韩一区二区三区 | 成人在线免费在线观看| 亚洲色图88| 4444kk亚洲人成电影在线| av软件在线观看| 日韩欧美一区二区不卡| 青娱乐在线免费视频| 亚洲国产精品国自产拍av| 日本在线不卡一区二区| 亚洲久久成人| 男人天堂网站在线| 欧美第一在线视频| 国产精品影片在线观看| 二区三区不卡| 最近日韩中文字幕中文| 男人av在线| 日韩成人xxxx| 少妇高潮一区二区三区99小说 | 少妇av一区二区三区| 视频二区在线| 欧美日韩一区小说| 精品国产xxx| 国产精品日韩精品欧美在线| 美女久久久久久久久久| 99久久精品国产导航| 无码人妻精品一区二区三区99不卡| 久久成人精品无人区| 久久人妻无码一区二区| 91麻豆精品国产91久久久平台| 日韩精品无码一区二区三区| 国产欧美日韩免费观看| 成人激情av在线| av资源在线看片| 亚洲精品日韩丝袜精品| 内射无码专区久久亚洲| 亚洲第五色综合网| 天堂v在线观看| 日韩av综合网站| 天堂成人在线| 亚洲欧美综合v| av中文字幕观看| 欧美一区二区黄| 精品国产无码一区二区三区| 欧美日韩视频免费播放| 国产又黄又粗又猛又爽的| 成人午夜av影视| 日本va中文字幕| 日韩精品一区第一页| 免费看黄色a级片| 欧美在线观看天堂一区二区三区| 国产女教师bbwbbwbbw| 怡红院精品视频在线观看极品| 午夜精品亚洲一区二区三区嫩草| 国产一区二区欧美| 这里只有精品66| 国产精品www994| 欧美亚洲另类色图| 奇米色777欧美一区二区| 精品少妇在线视频| 国产欧美在线| 鲁一鲁一鲁一鲁一av| 国产农村妇女毛片精品久久莱园子 | 欧美视频三区在线播放| 6—12呦国产精品| 欧美tk丨vk视频| 亚洲一级视频在线观看| 欧美一区二区三区喷汁尤物| 日本高清视频网站| 中文字幕九色91在线| 头脑特工队2免费完整版在线观看| 亚洲国产成人久久综合一区| 成人午夜电影在线观看| 亚洲久久久久久久久久| 91porn在线观看| 欧美国产高跟鞋裸体秀xxxhd| 中文不卡1区2区3区| 成人久久18免费网站图片| 哺乳一区二区三区中文视频| 日本亚洲导航| 你懂的视频欧美| 久久久久一区二区| 久久亚洲在线| 国产成人黄色片| 国产精品99久久久| 国产毛片久久久久久| 99久久精品久久久久久清纯| 一二三四在线观看视频| 国产亚洲欧洲一区高清在线观看| 国产精品精品软件男同| 国产精品美女久久久久av爽李琼 | 第九色区av在线| 欧美激情一区二区三区成人| av成人免费看| 久久婷婷人人澡人人喊人人爽| gogo大尺度成人免费视频| 国产一区免费| 综合亚洲视频| 中文字幕国内自拍| 奇米影视在线99精品| 久久福利小视频| 亚洲欧洲成人精品av97| 无码日韩精品一区二区| 精品成人在线观看| 麻豆av在线导航| 国产精品流白浆视频| 四虎5151久久欧美毛片| 欧美日韩激情四射| 精品一区二区三区的国产在线播放| 日韩人妻一区二区三区| 亚洲高清在线精品| www.亚洲欧美| 欧美成人精品在线视频| 日韩成人在线电影| 亚洲精品日韩在线观看| 老司机精品视频网站| 日韩精品aaa| 日韩毛片视频在线看| 中文字幕一区二区三区四区视频| 亚洲欧洲日产国产网站| 国产自产自拍视频在线观看| 国产精品国产精品国产专区蜜臀ah| 亚洲自拍偷拍网| 亚洲自拍第三页| 最新国产成人在线观看| 国产精品久久婷婷| 按摩亚洲人久久| 9999在线精品视频| 4444在线观看| 国产精品资源站在线| 国产少妇在线观看| 福利一区视频在线观看| 日本激情一区二区| 69久久夜色精品国产69| 成人在线视频免费看| 亚洲韩国在线| 久久 天天综合| 97成人资源站| 欧美mv和日韩mv国产网站| 国产丝袜在线播放| 鲁丝一区二区三区免费| 玖玖视频精品| www成人啪啪18软件| 欧美日韩一级视频| 中文字幕在线观看播放| 成人情视频高清免费观看电影| 九九视频免费观看视频精品 | 久久久久久久久99| 亚洲国产91精品在线观看| 热三久草你在线| 天堂av一区二区| 久久99精品久久久久久| 美女毛片在线观看| 欧美日韩在线三级| a在线免费观看| 国产一级二级三级精品| 性色av一区二区怡红| 久久久久久久久久久影视| 亚洲一区二区三区视频在线| 桃花色综合影院| 国产精品三级美女白浆呻吟| 91精品国偷自产在线电影| 欧美色图色综合| 国产精品亚洲а∨天堂免在线| 特级片在线观看| 日韩成人av一区| 精品久久在线| 日韩欧美不卡在线| 国产欧美日韩不卡| 亚洲国产av一区二区三区| 日韩有码在线电影| 精品精品国产毛片在线看| 搡女人真爽免费午夜网站| 亚洲欧美二区三区| 一级全黄少妇性色生活片| 欧美大片网站在线观看| 亚洲最好看的视频| 青娱乐国产精品视频| 中文字幕在线一区| 亚洲免费一级片| 国产精品久久久久久久久| 伊人成人在线| 中文字幕在线观看二区| 亚洲国产91精品在线观看| 免费成人毛片| av观看免费在线| 一区二区三区高清| 超碰在线观看av| 国产精品黄视频| 亚洲深爱激情|