上一篇介绍了,使用AOP的方式去拦截校验参数,本章讲解使用拦截器去校验参数,以及遇到的问题。
Spring web mvc 处理拦截器,就是案例所用到的去校验参数,类似与serlvet开发中里的filter过滤器。用于对拦截前及后处理。
public interface HandlerInterceptor {
boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception;
void postHandle(
HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception;
void afterCompletion(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception;
}
public abstract class HandlerInterceptorAdapter implements AsyncHandlerInterceptor {
/**
* This implementation always returns {@code true}.
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
/**
* This implementation is empty.
*/
@Override
public void postHandle(
HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception {
}
/**
* This implementation is empty.
*/
@Override
public void afterCompletion(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
}
/**
* This implementation is empty. 这个方法实现自AsyncHandlerInterceptor
*/
@Override
public void afterConcurrentHandlingStarted(
HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface CheckParam {
/**
* 请求当前接口所需要的参数,多个以小写的逗号隔开
* @return
*/
String fieldNames() default "";
/**
*传递参数的对象类型
*/
Class> parameter() default Object.class;
/**
* 默认不校验参数;
* @return
*/
boolean require() default false;
}
请求中获取流:
public class HttpHelper {
public static String getBodyString(HttpServletRequest request) throws IOException {
StringBuilder sb = new StringBuilder();
try(BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream(), Charset.forName("UTF-8")))) {
String line = "";
while ((line = reader.readLine()) != null) {
sb.append(line);
}
}
return sb.toString();
}
}
拦截器实现:
public class OpenHandlerInterceptor extends HandlerInterceptorAdapter {
private static final Logger LOGGER = Logger.getLogger(OpenHandlerInterceptor.class);
protected static final String APPLICATION_CHARSET="application/json; charset=utf-8";
protected static final String UTF_8 = "UTF-8";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
try {
if(handler instanceof HandlerMethod){
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
CheckParam checkParam = method.getAnnotation(CheckParam.class);
if(checkParam == null || !checkParam.require() || StringUtils.isEmpty(checkParam.fieldNames())) {
return true;
}
//防止中文,符号
String fieldNames = checkParam.fieldNames().replace(",",",");
boolean jsonParam = CheckFieldsNotNull(request, fieldNames);
if(!jsonParam){
ErrorMsg(response);
return false;
}
}
} catch (Exception e) {
e.printStackTrace();
ErrorMsg(response);
return false;
}
return true;
}
/**
* 校验必传参数属性是否存在
* @param request
* @param fieldNames
* @return
* @throws Exception
*/
public static boolean CheckFieldsNotNull(HttpServletRequest request, String fieldNames) throws Exception{
LOGGER.info(String.format("校验字段:{%s}",fieldNames));
String requestParam = HttpHelper.getBodyString(request);
if(StringUtils.isEmpty(requestParam)){
return false;
}
Gson gson = new Gson();
ArrayList
以上代码就实现了拦截器的参数校验,但是从中遇到一个问题,流只能读一次。所以我们在controller再次获取的是null值,为了解决请求中的流多次复用,所以实现了一个Filter,通过Filter来解决流只能读取一次的问题。代码如下:
public class RequestReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {
private final byte[] body;
public RequestReaderHttpServletRequestWrapper(ServletRequest request) throws IOException {
super(request);
body = HttpHelper.getBodyString(request).getBytes(Charset.forName(OpenHandlerInterceptor.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 boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
};
}
}
HttpServletRequestWrapper
这个类是HttpServletRequest的一个包装类,大家应该知道装饰器模式。这个类对接下来过滤器中的使用是有非常重要的意义的。
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) {
requestWrapper = new RequestReaderHttpServletRequestWrapper((HttpServletRequest) request);
}
//获取请求中的流如何,将取出来的字符串,再次转换成流,然后把它放入到新request对象中。
// 在chain.doFiler方法中传递新的request对象
if(requestWrapper == null) {
chain.doFilter(request, response);
} else {
chain.doFilter(requestWrapper, response);
}
}
@Override
public void init(FilterConfig arg0) throws ServletException {
}
}