众所周知, request
对象中的 inputStream
只能读取一次,下次再读就没有了,在一些场景中,这种特性是不适用的:比如需要在拦截器中读取请求体,然后做相关的参数校验,做完校验后请求打到 Controller
中就读取不到了。
解决的方法很简单,通过 HttpServletRequestWrapper
将 HttpServletRequest
对象包装一下,保存 requestBody
的副本,最后通过过滤器将包装后的 request
对象替换掉。
附完整代码:
/** * 包装HttpServletRequest,使其inputStream可复用。 * @author wuwenze * @date 2019-06-21 */ @Configuration @Order(1) @WebFilter(urlPatterns = ["/**"]) class HttpServletRequestWrapperFilter : Filter { override fun doFilter(req: ServletRequest?, resp: ServletResponse?, chain: FilterChain?) { when (req) { is HttpServletRequest -> chain?.doFilter(MyHttpServletRequestWrapper(req), resp) else -> chain?.doFilter(req, resp) } } companion object class MyHttpServletRequestWrapper(request: HttpServletRequest) : HttpServletRequestWrapper(request) { private var body: ByteArray = request.getBodyString().toByteArray(Charset.forName("UTF-8")) @Throws(IOException::class) override fun getReader(): BufferedReader { return BufferedReader(InputStreamReader(inputStream)) } @Throws(IOException::class) override fun getInputStream(): ServletInputStream { val byteArrayInputStream = ByteArrayInputStream(body) return object : ServletInputStream() { @Throws(IOException::class) override fun read(): Int { return byteArrayInputStream.read() } override fun isFinished(): Boolean { return false } override fun isReady(): Boolean { return false } override fun setReadListener(readListener: ReadListener) { } } } } }
获取requestBody的扩展函数:
fun HttpServletRequest.getBodyString(): String { val result = StringBuffer() var reader: BufferedReader? = null try { var line: String? = null reader = BufferedReader(InputStreamReader(this.inputStream, Charset.forName("UTF-8"))) while ({ line = reader.readLine();line }() != null) { result.append(line) } } catch (e: IOException) { // ignore } finally { reader?.close() } return result.toString() }
这样就能多次使用了,每次读取的其实是存储在 MyHttpServletRequestWrapper
中的副本。