最新要闻

广告

手机

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

家电

天天短讯!一步一步实现若依框架--2.3防止重复提交 repeat_submit

来源:博客园


【资料图】

原理:  常见的场景端页面多次点击提交按钮,通常是前端通过点击一次后使按钮disable进行处理,后端同样也需要进行限制。若依使用了注解+拦截器的方式,这里其实也可以用AOP。在缓存中(若依的缓存就是使用redis)记录每个客户端的请求方法和参数,在redis中设置超时时间。如果在超时时间内进行了第二次请求且参数都一致,拦截器进行拦截抛出异常不进行真正的处理。思路其实和限流类似,只是这里多了对请求参数的处理,并且没有采用AOP而是用了拦截器去实现。    1)添加过滤器,处理请求参数不能多次读的问题。 自定义了一个HttpServletRequest的包装器,通过在变量中保存流中读取的内容,解决了不能多次读入请求参数的问题。然后通过添加过滤器在dofilter中替换了这个增强功能后的request。包装器代码:
public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper{    private final byte[] body;    public RepeatedlyRequestWrapper(HttpServletRequest request, ServletResponse response) throws IOException {        super(request);        request.setCharacterEncoding(Constants.UTF8);        response.setCharacterEncoding(Constants.UTF8);        // 把reques的参数读入到byte数组中,就可以多次使用        body= HttpHelper.getBodyString(request).getBytes(Constants.UTF8);    }    @Override    public BufferedReader getReader() throws IOException {        return new BufferedReader(new InputStreamReader(getInputStream()));    }    @Override    public ServletInputStream getInputStream() throws IOException {        ByteArrayInputStream bis = 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 bis.read();            }        };    }}

把这个包装器通过过滤器加入到请求处理中去。

public class RepeatableFilter implements Filter {    @Override    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {        ServletRequest requestWrapper = null;        /**         * 参数如果是json类型,需要HttpServletRequest包装器进行加强,多次读取参数         */        if(request instanceof HttpServletRequest                && request.getContentType().startsWith(MediaType.APPLICATION_JSON_VALUE)){            requestWrapper = new RepeatedlyRequestWrapper((HttpServletRequest) request,response);        }        if(requestWrapper == null){            filterChain.doFilter(request,response);        }else{            filterChain.doFilter(requestWrapper, response);        }    }}

最后要记得把这个过滤器注册到spring环境里。

@Componentpublic class FilterConfig {    @Bean    public FilterRegistrationBean repeatableFilterRegistration(){        FilterRegistrationBean registration = new FilterRegistrationBean();        registration.setFilter(new RepeatableFilter());        registration.addUrlPatterns("/*");        registration.setName("repeatableFilter");        registration.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE);        return registration;    }}

springboot通过拦截器获取参数有两种方式,一种通过request.getParameter获取Get方式传递的参数,另外一种是通过request.getInputStream或reques.getReader获取通过POST/PUT/DELETE/PATCH传递的参数。@PathVariable注解是REST风格url获取参数的方式,只能用在GET请求类型,通过getParameter获取参数;@RequestParam注解支持GET和POST/PUT/DELETE/PATCH方式,Get方式通过getParameter获取参数和post方式通过getInputStream或getReader获取参数;@RequestBody注解支持POST/PUT/DELETE/PATCH,可以通过getInputStream和getReader获取参数。通过getInputStream或getReader在拦截器中获取会导致控制器拿到的参数为空,这是因为流读取一次之后流的标志位已经发生了变化,无法多次读取参数,所以通过装饰者模式,增强了HttpServletRequest,把流中的数据读取后暂存在byte[]中,可以实现多次读取的需求。

2)添加拦截器

拦截到自定义注解进行处理 。通过AbstractRepeatSubmitInterceptor拦截请求,进行重复提交的判断和处理。  3)测试  controller中建立添加注解的测试方法:  
@PostMapping("/repeatTest")@RepeatSubmit(interval = 100000, message = "重复提交提示!!!")public String repeatTest(@RequestBody String json) {    return json;}

代码地址:

https://github.com/hunji/RYMirror/releases/tag/2.3%E9%98%B2%E6%AD%A2%E9%87%8D%E5%A4%8D%E6%8F%90%E4%BA%A4

关键词: 请求参数 进行处理 一步一步