前言:在日常开发中,客户可能会存在反复点击提交按钮导致表单的重复提交,这个问题也是非常需要重视的,在本篇博客中,采用的是session、自定义注解和拦截器的方式来防止重复表单的重复提交,提高整体代码的优雅和整洁度!
目录
一、核心实现思路
二、代码实现
1、编写自定义注解@RepeatSubmit
2、核心配置类
3、撰写拦截器核心代码
4、JSON工具类
5、编写请求层
6、运行结果
三、Gitee源码
一、核心实现思路
这边我用自己的理解方式进行的梳理,大概逻辑如下:
1、撰写一个自定义注解。
2、撰写自定义拦截器。
3、每次有请求之前都会进入此拦截器,判断当前的接口是否存在自定义的注解,不存在则直接放行请求。
4、比对当前传入的参数和之前存入session的参数以及时间差的别对是否成立,成立则为重复请求,拦截当前url请求,并返回错误信息。
二、代码实现
整体代码实现非常简洁以及通俗易懂,都是本人精心整理出来的。
1、编写自定义注解@RepeatSubmit
package com.example.repeat.annotation;import java.lang.annotation.*;@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RepeatSubmit
{/*** 间隔时间(ms),小于此时间视为重复提交*/public int interval() default 5000;/*** 提示消息*/public String message() default "不允许重复提交,请稍后再试";
}
2、核心配置类
package com.example.repeat.config;import com.example.repeat.interceptor.RepeatSubmitInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class ResourcesConfig implements WebMvcConfigurer
{@Autowiredprivate RepeatSubmitInterceptor repeatSubmitInterceptor;/*** 自定义拦截规则*/@Overridepublic void addInterceptors(InterceptorRegistry registry){registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**");}
}
3、撰写拦截器核心代码
package com.example.repeat.interceptor;import com.example.repeat.annotation.RepeatSubmit;
import com.example.repeat.utils.JSON;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;@Component
public class RepeatSubmitInterceptor implements HandlerInterceptor {public final String REPEAT_PARAMS = "repeatParams";public final String REPEAT_TIME = "repeatTime";public final String SESSION_REPEAT_KEY = "repeatData";@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {if (handler instanceof HandlerMethod){HandlerMethod handlerMethod = (HandlerMethod) handler;Method method = handlerMethod.getMethod();RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class);if (annotation != null){if (this.isRepeatSubmit(request, annotation)){System.out.println(annotation.message());return false;}}return true;}else{return true;}}public boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation) throws Exception{// 本次参数及系统时间String nowParams = JSON.marshal(request.getParameterMap());Map<String, Object> nowDataMap = new HashMap<String, Object>();nowDataMap.put(REPEAT_PARAMS, nowParams);nowDataMap.put(REPEAT_TIME, System.currentTimeMillis());// 请求地址(作为存放session的key值)String url = request.getRequestURI();HttpSession session = request.getSession();Object sessionObj = session.getAttribute(SESSION_REPEAT_KEY);if (sessionObj != null){Map<String, Object> sessionMap = (Map<String, Object>) sessionObj;if (sessionMap.containsKey(url)){Map<String, Object> preDataMap = (Map<String, Object>) sessionMap.get(url);if (compareParams(nowDataMap, preDataMap) && compareTime(nowDataMap, preDataMap, annotation.interval())){return true;}}}Map<String, Object> sessionMap = new HashMap<String, Object>();sessionMap.put(url, nowDataMap);session.setAttribute(SESSION_REPEAT_KEY, sessionMap);return false;}/*** 判断参数是否相同*/private boolean compareParams(Map<String, Object> nowMap, Map<String, Object> preMap){String nowParams = (String) nowMap.get(REPEAT_PARAMS);String preParams = (String) preMap.get(REPEAT_PARAMS);return nowParams.equals(preParams);}/*** 判断两次间隔时间*/private boolean compareTime(Map<String, Object> nowMap, Map<String, Object> preMap, int interval){long time1 = (Long) nowMap.get(REPEAT_TIME);long time2 = (Long) preMap.get(REPEAT_TIME);if ((time1 - time2) < interval){return true;}return false;}
}
4、JSON工具类
package com.example.repeat.utils;import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;public class JSON
{public static final String DEFAULT_FAIL = "\"Parse failed\"";private static final ObjectMapper objectMapper = new ObjectMapper();private static final ObjectWriter objectWriter = objectMapper.writerWithDefaultPrettyPrinter();public static void marshal(File file, Object value) throws Exception{try{objectWriter.writeValue(file, value);}catch (JsonGenerationException e){throw new Exception(e);}catch (JsonMappingException e){throw new Exception(e);}catch (IOException e){throw new Exception(e);}}public static void marshal(OutputStream os, Object value) throws Exception{try{objectWriter.writeValue(os, value);}catch (JsonGenerationException e){throw new Exception(e);}catch (JsonMappingException e){throw new Exception(e);}catch (IOException e){throw new Exception(e);}}public static String marshal(Object value) throws Exception{try{return objectWriter.writeValueAsString(value);}catch (JsonGenerationException e){throw new Exception(e);}catch (JsonMappingException e){throw new Exception(e);}catch (IOException e){throw new Exception(e);}}public static byte[] marshalBytes(Object value) throws Exception{try{return objectWriter.writeValueAsBytes(value);}catch (JsonGenerationException e){throw new Exception(e);}catch (JsonMappingException e){throw new Exception(e);}catch (IOException e){throw new Exception(e);}}public static <T> T unmarshal(File file, Class<T> valueType) throws Exception{try{return objectMapper.readValue(file, valueType);}catch (JsonParseException e){throw new Exception(e);}catch (JsonMappingException e){throw new Exception(e);}catch (IOException e){throw new Exception(e);}}public static <T> T unmarshal(InputStream is, Class<T> valueType) throws Exception{try{return objectMapper.readValue(is, valueType);}catch (JsonParseException e){throw new Exception(e);}catch (JsonMappingException e){throw new Exception(e);}catch (IOException e){throw new Exception(e);}}public static <T> T unmarshal(String str, Class<T> valueType) throws Exception{try{return objectMapper.readValue(str, valueType);}catch (JsonParseException e){throw new Exception(e);}catch (JsonMappingException e){throw new Exception(e);}catch (IOException e){throw new Exception(e);}}public static <T> T unmarshal(byte[] bytes, Class<T> valueType) throws Exception{try{if (bytes == null){bytes = new byte[0];}return objectMapper.readValue(bytes, 0, bytes.length, valueType);}catch (JsonParseException e){throw new Exception(e);}catch (JsonMappingException e){throw new Exception(e);}catch (IOException e){throw new Exception(e);}}
}
5、编写请求层
package com.example.repeat.controller;import com.example.repeat.annotation.RepeatSubmit;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/user")
public class UserController {@RepeatSubmit@RequestMapping("/hello")public String hello(){return "hello";}
}
6、运行结果
这边使用了get请求,为了图方便,在浏览器不断请求,后台输出如下!
三、Gitee源码
SpringBoot使用Session防止表单重复提交: 在日常开发中,客户可能会存在反复点击提交按钮导致表单的重复提交,这个问题也是非常需要重视的,在本篇博客中,采用的是session、自定义注解和拦截器的方式来防止重复表单的重复提交,提高整体代码的优雅和整洁度!