成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

JSON 數據讀一次就沒了,怎么辦?

開發 項目管理
接口冪等性的處理,同一個接口,在短時間內接收到相同參數的請求,接口可能會拒絕處理。那么在判斷的時候,就需要先把請求的參數提取出來進行判斷,如果是 JSON 參數,此時就會有問題,參數提前取出來了,將來在接口中再去獲取 JSON 參數,就會發現沒有了。

對于前端傳來的 JSON 數據,我們在服務端基本上都是通過 IO 流來解析,如果是古老的 Servlet,那么我們直接解析 IO 流;如果是在 SpringMVC 中,我們往往通過 @RequestBody 注解來解析。

如果通過 IO 流來解析參數,默認情況下,IO 流讀一次就結束了,就沒有了。而往往有些場景,需要我們多次讀取參數,我舉一個例子:

接口冪等性的處理,同一個接口,在短時間內接收到相同參數的請求,接口可能會拒絕處理。那么在判斷的時候,就需要先把請求的參數提取出來進行判斷,如果是 JSON 參數,此時就會有問題,參數提前取出來了,將來在接口中再去獲取 JSON 參數,就會發現沒有了。

我們來看看這個問題怎么解決,這也是最近松哥在做的 TienChin 項目的一個小知識點,和大家分享下。

新建一個 Spring Boot 項目,引入 Web 依賴,我們一起來看下面的問題。

1. 問題演示

假設我現在有一個處理接口冪等性的攔截器,在這個攔截器中,我需要先獲取到請求的參數,然后進行比對等等,我這里先簡單模擬一下,比如我們項目中有如下攔截器:

public class IdempotenceInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String s = request.getReader().readLine();
System.out.println("s = " + s);
return true;
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

}
}

在這個攔截器中先把請求的參數拎出來,瞅一眼。通過 IO 流讀取出來的參數最大特點是一次性,也就是讀一次就失效了。

然后我們配置一下這個攔截器:

@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new IdempotenceInterceptor()).addPathPatterns("/**");
}
}

最后再來看看 Controller 接口:

@RestController
public class HelloController {
@PostMapping("/hello")
public void hello(@RequestBody String msg) throws IOException {
System.out.println("msg = " + msg);
}
}

在接口參數上我們加了 @RequestBody 注解,這個底層也是通過 IO 流來讀取數據的,但是由于 IO 流在攔截器中已經被讀取過一次了,所以到了接口中再去讀取就會出錯。報錯信息如下:

然而很多時候,我們希望 IO 流能夠被多次讀取,那么怎么辦呢?

2. 問題解決

這里我們可以利用裝飾者模式對 HttpServletRequest 的功能進行增強,具體做法也很簡單,我們重新定義一個 HttpServletRequest:

public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper {
private final byte[] body;

public RepeatedlyRequestWrapper(HttpServletRequest request, ServletResponse response) throws IOException {
super(request);
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
body = request.getReader().readLine().getBytes("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 int available() throws IOException {
return body.length;
}

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

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

@Override
public void setReadListener(ReadListener readListener) {

}
};
}
}

這段代碼并不難,很好懂。

首先在構造 RepeatedlyRequestWrapper 的時候,就通過 IO 流將數據讀取出來并存入到一個 byte 數組中,然后重寫 getReader 和 getInputStream 方法,在這兩個讀取 IO 流的方法中,都從 byte 數組中返回 IO 流數據出來,這樣就實現了反復讀取了。

接下來我們定義一個過濾器,讓這個裝飾后的 Request 生效:

public class RepeatableFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {

}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
ServletRequest requestWrapper = null;
if (request instanceof HttpServletRequest
&& StringUtils.startsWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE)) {
requestWrapper = new RepeatedlyRequestWrapper((HttpServletRequest) request, response);
}
if (null == requestWrapper) {
chain.doFilter(request, response);
} else {
chain.doFilter(requestWrapper, response);
}
}

@Override
public void destroy() {

}
}

判斷一下,如果請求數據類型是 JSON 的話,就把 HttpServletRequest “偷梁換柱”改為 RepeatedlyRequestWrapper,然后讓過濾器繼續往下走。

最后再配置一下這個過濾器:

@Bean
FilterRegistrationBean<RepeatableFilter> repeatableFilterBean() {
FilterRegistrationBean<RepeatableFilter> bean = new FilterRegistrationBean<>();
bean.addUrlPatterns("/*");
bean.setFilter(new RepeatableFilter());
return bean;
}

好啦大功告成。

以后,我們的 JSON 數據就可以通過 IO 流反復讀取了。

責任編輯:武曉燕 來源: 江南一點雨
相關推薦

2017-12-21 20:01:38

潤乾報表

2010-12-22 14:40:51

3Q大戰

2021-10-14 11:11:58

WiFi電腦網絡

2012-12-03 09:37:39

ForefrontExchange

2020-03-29 08:56:07

文件系統磁盤Java

2020-12-14 08:07:06

Mybatis源碼java

2024-04-22 08:17:23

MySQL誤刪數據

2011-06-10 10:25:48

2020-09-25 07:57:42

生產事故系統

2015-08-12 10:20:47

2018-09-12 09:07:43

服務器數據RAID5

2012-07-02 13:26:28

電線連接

2021-01-05 10:48:38

RedisAOF日志RDB快照

2022-02-09 12:11:57

數據丟失數據恢復硬盤

2015-11-18 13:05:09

2025-02-21 10:59:22

2009-11-03 08:56:02

linux死機操作系統

2022-12-19 11:31:57

緩存失效數據庫

2017-02-21 13:11:43

SDN網絡體系SDN架構

2022-05-19 08:01:49

PostgreSQL數據庫
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 伊人久久大香线 | 亚洲第一女人av | 国产精品久久久久久久免费观看 | 97免费视频在线观看 | 日韩第一区 | 日韩电影免费在线观看中文字幕 | 色姑娘av | 亚洲午夜精品久久久久久app | 亚洲精品免费在线观看 | 香蕉大人久久国产成人av | 亚洲视频第一页 | 国产在线精品一区二区三区 | 毛片网站免费观看 | 欧美黄色片| 婷婷一级片 | 亚洲精品第一 | 97精品国产97久久久久久免费 | 国产视频一区二区三区四区五区 | 狠狠色狠狠色综合日日92 | 在线观看毛片网站 | 亚洲一区电影 | 福利网站在线观看 | 欧美视频网 | 艹逼网 | 国产98色在线 | 日韩 | 国产极品车模吞精高潮呻吟 | 欧美高清视频一区 | 伊人在线| 狠狠艹| 亚洲视频www | 亚洲看片 | 久久亚洲欧美日韩精品专区 | 91免费电影| 亚洲精品9999 | 国产精品99久久久久久宅男 | 国产综合网站 | 欧美在线视频网站 | 天天插日日操 | 大乳boobs巨大吃奶挤奶 | 人人99| 亚洲免费精品 |