Spring -- 通过拦截器使用注解方式校验参数
来源:互联网 发布:安卓高仿充值q币软件 编辑:程序博客网 时间:2024/05/22 03:25
前言:
上一篇介绍了,使用AOP的方式去拦截校验参数,本章讲解使用拦截器去校验参数,以及遇到的问题。
简介:
Spring web mvc 处理拦截器,就是案例所用到的去校验参数,类似与serlvet开发中里的filter过滤器。用于对拦截前及后处理。
常见场景:
日记记录、校验参数、权限检查等等。比喻我们在学习jdbc的时候,获取连接,最后关闭连接。其实本质也是AOP的方法(面向切面编程),也就是说符合AOP的横向切入点的功能都可以使用拦截器去实现。
一、拦截器类:
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;}
preHandle:预处理回调方法,实现拦截器的预处理,第三个参数为响应的处理器;返回值:true表示继续流程;false表示流程中断(如校验失败,参数缺少),不会继续调用其他的拦截器或处理器,此时我们需要通过response来产生响应;
postHandle:后处理回调方法,实现处理拦截器的后处理(返回请求结果之前)。
afterCompletion:整个请求处理完毕回调方法,在返回结果之后进行调用,但仅调用处理器执行链中preHandle返回true的拦截器的afterCompletion。
二、拦截器适配器
有个场景,就是我们实现拦截器的时候只需要实现某个方法,或者不需要去实现全部的方法,可以使用拦截器的适配器类(使用了一种适配器模式的设计模式),允许我们只实现需要的方法。
public abstract class HandlerInterceptorAdapter implements AsyncHandlerInterceptor {/** * This implementation always returns {@code true}. */@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {return true;}/** * This implementation is empty. */@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)throws Exception {}/** * This implementation is empty. */@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)throws Exception {}/** * This implementation is empty. 这个方法实现自AsyncHandlerInterceptor */@Overridepublic void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {}}
三、流程图如下:
1、正常流程如下图:
2、被拦截运行流程图如下:
3、运行流程介绍如下;
1、拦截器执行顺序是按照Spring配置文件中定义的顺序而定的。
2、会先按照配置拦截器的顺序执行所有拦截器的preHandle方法,一直遇到return false为止,比如第二个preHandle方法是return false,则第三个以及以后所有拦截器都不会执行。若都是return true,则按配置的拦截器配置顺序加载完preHandle方法。
3、然后执行目标方法(目标controller接口),若中间抛出异常,则跟return false效果一致,不会继续执行postHandle,只会倒序执行afterCompletion方法。
4、在目标方法执行完业务逻辑(页面还未渲染数据)时,按倒序执行postHandle方法。若第三个拦截器的preHandle方法return false,则会执行第二个和第一个的postHandle方法和afterCompletion(postHandle都执行完才会执行这个,也就是页面渲染完数据后,执行after进行清理工作)方法。(postHandle和afterCompletion都是倒序执行)
四、校验参数demo:
以下内容和上篇AOP方式的注解一样,可以忽略:
注解:
@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<Map> arrayList = gson.fromJson(requestParam, ArrayList.class); for (String fieldName : fieldNames.split(",")) { Preconditions.checkNotNull(arrayList.get(0).get(fieldName),fieldName + "is null"); } return true; } /** * 错误信息 * @param response * @throws IOException */ public static void ErrorMsg( HttpServletResponse response) throws Exception { PrintWriter out = response.getWriter(); response.setCharacterEncoding(UTF_8); response.setContentType(APPLICATION_CHARSET); out.write(JsonMapper.toJsonString(Result.fail(ErrorMessageEnum.MISS_REQUEST_PARAM.getValue(),ErrorMessageEnum.MISS_REQUEST_PARAM.getDesc()))); }}以上代码就实现了拦截器的参数校验,但是从中遇到一个问题,流只能读一次。所以我们在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的一个包装类,大家应该知道装饰器模式。这个类对接下来过滤器中的使用是有非常重要的意义的。
解决问题就是,流一旦读取就无法再次读取,所以通过Filter的重写回写请求对象中;使请求中的流多次复用。
接下来是过滤器的实现,过滤器的实现使用了责任链设计模式:
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 { }}
总结:
除了以上方法:使用servlet提供的HttpServletRequestWrapper类,重写相关ServletRequest方法,实现流的多次读取。
另外一个方法:在Filter时把请求中的流放入ThreadLocal,然后在需要请求参数的地方调用ThreadLocal的get方法即可。
阅读全文
0 0
- Spring -- 通过拦截器使用注解方式校验参数
- Spring AOP注解的方式校验参数
- Spring拦截器校验Json格式参数
- 通过注解在方法级别上使用拦截器做登录权限校验
- 企业实战之Spring拦截器+注解实现《登录校验》
- java注解方式参数校验
- Spring MVC拦截器通过注解方式实现防止表单重复提交
- spring注解式参数校验
- 企业实战之Spring拦截器《统一参数校验》
- Spring Boot 拦截器 请求参数MD5签名校验
- 使用参数拦截器通过注解直接从JSON对象中获取数据
- 使用注解方式调用自定义拦截器
- Spring使用注解方式对url进行拦截
- spring aop拦截Controller做参数校验
- spring boot注解实现参数校验
- Spring MVC AOP通过注解方式拦截Controller等实现日志管理
- Spring MVC AOP通过注解方式拦截Controller等实现日志管理
- Spring MVC AOP通过自定义注解方式拦截Controller等实现日志管理
- Indy10 IdudpServer获取字符串数据Read事件,TIdBytes类型的使用,支持中文
- 程序员一事无成,奈何桥上走一走
- 字符流中第一个不重复的字符(字符串)
- 一只小蜜蜂
- 数据结构10————树的基本概念和存储
- Spring -- 通过拦截器使用注解方式校验参数
- Systemverilog中的信箱,事件以及旗语
- 1034. 有理数四则运算(20)
- 欢迎使用CSDN-markdown编辑器
- 中介者模式(java实现)
- Socket编程实现多人聊天室(c语言版)
- Warning the selected directory is not a valid tomcat home
- Linux_Git 服务器安装笔记
- linux 数据库备份