转载请注明出处:https://blog.csdn.net/Strugglein/article/details/82023959
首先我们的项目是前后分离的,
然后今天有个需求就是如果在前台app请求的时候有可能会传来一些表情什么的,因为后台的数据库字段使用的是varchar接收的,会引起一些不必要的麻烦,即使把表情放到了数据库中,前台渲染也是个麻烦事,
所以经过和领导商量就直接用拦截器给把传过来的表情拦了,直接报400,然后返回错误信息,前台提醒就完了,
本想着挺简单个工作,被一个拦截器获取POST请求中的JSON信息
给废了段时间,然后顺便记一下,
需求: 写一个拦截器,拦截前台传来的含有表情的json串,如果有表情返回异常,没有继续走下去,
问题: 在使用拦截器获取post请求中的body信息的时候,无论是使用request.getReader()
方法还是使用request.getInputStream()
方法,
在获取JSON信息后,后面日志就紧跟着开始报异常getReader() has already been called for this request
然后翻阅了网上各种论坛,开始汲取各种大佬们的经验,头发以肉眼可见的速度掉着,这里省略10个字……
先来波解决思路
使用HttpServletRequestWrapper来包装HttpServletRequest,在BodyHttpServletRequestWrapper中初始化读取request的Reader数据,以byte[]形式缓存在其中,然后在filter中将request转换为包装过的request
直接上代码
filter
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* @author zhaopeng
* @Description: HttpServletRequest替换的filter
* @email [email protected]
*/
public class HttpServletRequestReplacedFilter implements Filter {
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
ServletRequest requestWrapper = null;
if (request instanceof HttpServletRequest) {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
if ("POST".equals(httpServletRequest.getMethod().toUpperCase())
) {
requestWrapper = new BodyHttpServletRequestWrapper(
(HttpServletRequest) request);
}
}
if (requestWrapper == null) {
chain.doFilter(request, response);
} else {
chain.doFilter(requestWrapper, response);
}
}
@Override
public void init(FilterConfig arg0) throws ServletException {
}
}
BodyHttpServletRequestWrapper
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.Enumeration;
/**
* @author zhaopeng
* @Description: bodyReader包装request
* @email [email protected]
*/
public class BodyHttpServletRequestWrapper extends
HttpServletRequestWrapper {
private final byte[] body;
public BodyHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
Enumeration e = request.getHeaderNames() ;
while(e.hasMoreElements()){
String name = (String) e.nextElement();
String value = request.getHeader(name);
}
body = HttpHelper.getBodyString(request).getBytes(Charset.forName("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 boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
@Override
public int read() throws IOException {
return bais.read();
}
};
}
@Override
public String getHeader(String name) {
return super.getHeader(name);
}
@Override
public Enumeration<String> getHeaderNames() {
return super.getHeaderNames();
}
@Override
public Enumeration<String> getHeaders(String name) {
return super.getHeaders(name);
}
}
获取请求body
import javax.servlet.ServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
/**
* @Description: 获取请求body
* @author zhaopeng
* @email [email protected]
* @date 2018/8/24 18:15
*/
public class HttpHelper {
public static String getBodyString(ServletRequest request) {
StringBuilder sb = new StringBuilder();
InputStream inputStream = null;
BufferedReader reader = null;
try {
inputStream = request.getInputStream();
reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("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();
}
}
web.xml中配置filter
<filter>
<filter-name>httpServletRequestReplacedFilter</filter-name>
<filter-class>top.qujiali.core.filter.HttpServletRequestReplacedFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>httpServletRequestReplacedFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
把上面的代码直接拷贝到你的项目中,然后就会先走filter再走拦截器了,后面就可以读取了
然后就是我们要对拦截到的内容进行处理了
拦截器
package com.qujiali.core.interceptor;
import top.qujiali.core.exception.ParamExcetpion;
import top.qujiali.core.interceptor.BaseInterceptor;
import top.qujiali.core.util.Emoji;
import top.qujiali.core.util.GetConstantsUtil;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
public class EmojiInterceptor extends BaseInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
StringBuilder sb = new StringBuilder();
String line;
BufferedReader reader = request.getReader();
while ((line = reader.readLine()) != null) {
sb.append(line);
}
String body = sb.toString();
boolean emoji = Emoji.containsEmoji(body);
if (emoji) {
throw new ParamExcetpion(GetConstantsUtil.NOTEMOJI);//这里是我自己封装了一个异常,根据自己需要写自己的异常
}
return super.preHandle(request, response, handler);
}
}
在项目中配置拦截器的地方配置好拦截器
<bean class="com.qujiali.core.interceptor.EmojiInterceptor"/>
工具类
package top.qujiali.core.util;
import org.apache.commons.lang3.StringUtils;
public class Emoji {
/**
* @Description: 是否有表情判断
* @author zhaopeng
* @email [email protected]
* @date 2018/8/24 18:27
*/
public static boolean containsEmoji(String source) {
int len = source.length();
boolean isEmoji = false;
for (int i = 0; i < len; i++) {
char hs = source.charAt(i);
if (0xd800 <= hs && hs <= 0xdbff) {
if (source.length() > 1) {
char ls = source.charAt(i + 1);
int uc = ((hs - 0xd800) * 0x400) + (ls - 0xdc00) + 0x10000;
if (0x1d000 <= uc && uc <= 0x1f77f) {
return true;
}
}
} else {
// non surrogate
if (0x2100 <= hs && hs <= 0x27ff && hs != 0x263b) {
return true;
} else if (0x2B05 <= hs && hs <= 0x2b07) {
return true;
} else if (0x2934 <= hs && hs <= 0x2935) {
return true;
} else if (0x3297 <= hs && hs <= 0x3299) {
return true;
} else if (hs == 0xa9 || hs == 0xae || hs == 0x303d
|| hs == 0x3030 || hs == 0x2b55 || hs == 0x2b1c
|| hs == 0x2b1b || hs == 0x2b50 || hs == 0x231a) {
return true;
}
if (!isEmoji && source.length() > 1 && i < source.length() - 1) {
char ls = source.charAt(i + 1);
if (ls == 0x20e3) {
return true;
}
}
}
}
return isEmoji;
}
private static boolean isEmojiCharacter(char codePoint) {
return (codePoint == 0x0) || (codePoint == 0x9) || (codePoint == 0xA)
|| (codePoint == 0xD)
|| ((codePoint >= 0x20) && (codePoint <= 0xD7FF))
|| ((codePoint >= 0xE000) && (codePoint <= 0xFFFD))
|| ((codePoint >= 0x10000) && (codePoint <= 0x10FFFF));
}
/**
* @Description: 过滤emoji 或者 其他非文字类型的字符
* @author zhaopeng
* @email [email protected]
* @date 2018/8/24 18:27
*/
public static String filterEmoji(String source) {
if (StringUtils.isBlank(source)) {
return source;
}
StringBuilder buf = null;
int len = source.length();
for (int i = 0; i < len; i++) {
char codePoint = source.charAt(i);
if (isEmojiCharacter(codePoint)) {
if (buf == null) {
buf = new StringBuilder(source.length());
}
buf.append(codePoint);
}
}
if (buf == null) {
return source;
} else {
if (buf.length() == len) {
buf = null;
return source;
} else {
return buf.toString();
}
}
}
}
这样就可以了,其实主要就是ServletRequest中getReader()和getInputStream()两个方法都注明方法只能被调用一次,由于RequestBody是流的形式读取,那么流读了一次就没有了,所以只能被调用一次
既然是因为流只能读一次的原因,那么只要将流的内容保存下来,就可以实现反复读取了,这样问题也就解决了
好的到这里本篇文章就先到此了,创作不易,如果那里有不合适的地方还请大家多多指教,写这篇博的目的主要就是为了方便自己以后的一个回忆和朋友学习时的一个参考,希望为大家可以带来帮助 ~ ~&
虚心的去学习,自信的去工作~