SpringMVC防止表单重复提交(拦截器实现)

本文介绍了如何使用SpringMVC的拦截器来防止表单重复提交,避免因网络延迟导致的脏数据问题。通过自定义注解、拦截器和异常处理,实现了请求的重复性校验,确保接口幂等性。代码示例详细展示了整个实现过程。
摘要由CSDN通过智能技术生成

SpringMVC防止表单重复提交(拦截器实现)

       之前项目中在表单提交的时候由于网络原因造成响应慢,用户会重复点击,造成新增或修改时数据库产生脏数据,首先想到的解决方式就是在表中加索引,但是由于业务需求,加索引不太合适,便选择了使用SpringMVC的拦截器实现防止表单重复提交的方式解决。
实现思路:
1.自定义注解PreventRepeat,标注表单提交的后台处理方法上,方便拦截器选择性处理
2.自定义拦截器类RepeatUrlDataInterceptor,继承SpringMVC的拦截器HandlerInterceptorAdapter,在preHandle()方法内进行重复性校验,重复性校验大致逻辑:
(1)通过反射拿到请求方法上的注解,判断是否有注解PreventRepeat,有的话进行重复性校验,没有的话将请求放行;
(2)重复性校验时,将request中的请求参数转换为String字符串,并以请求url为key,String参数字符串为value存储在map中,再将map以String的形式存在session中,每次请求时拿到上一次的请求信息与当前的请求信息比较,相同则为重复提交,重复提交抛出自定义的重复异常,交给全局异常处理器处理。

话不多说,这就上代码

1.自定义注解PreventRepeat

package cc.rengu.ecp.platform.mcmp.busidict;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Created by Administrator on 2021/2/2.
 * 防止重复提交的注解
 *
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PreventRepeat {
}

2.自定义重复异常

public class RepeatException extends Exception {

    private String message;
    
    @Override
    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
    
    public RepeatException(String message){
        this.message = message;
    }
}

3.自定义拦截器类RepeatUrlDataInterceptor

package cc.rengu.ecp.platform.mcmp.common;

import cc.rengu.ecp.platform.mcmp.busidict.PreventRepeat;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by Administrator on 2021/2/2.
 *
 * 重复数据校验拦截器
 *
 */
public class RepeatUrlDataInterceptor extends HandlerInterceptorAdapter{


    /**
     *
     * @param request
     * @param response
     * @param handler
     * @return   true:继续往下走;false:中断
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        if(handler instanceof HandlerMethod){
            HandlerMethod handlerMethod = (HandlerMethod)handler;
            Method method = handlerMethod.getMethod();
            //判断该方法上是否有防止重复提交的注解
            PreventRepeat repeat = method.getAnnotation(PreventRepeat.class);
            if(repeat != null){
                //有重复判断的注解,进行重复判断
                boolean result = repeatDataValidator(request);
                if(result){
                    //重复,给客户端返回重复信息 (可以自定义异常,抛给springmvc的前端控制器,在全局异常处理内处理)

                  throw new RepeatException("请勿重复提交");

                    return false;
                }else{
                    return true;
                }
            }else{
                return true;
            }
        }else{
            return super.preHandle(request, response, handler);
        }
    }


    /**
     *  重复数据校验
     * @param httpServletRequest  请求request
     * @return  返回true:重复数据;返回false:不是重复数据
     */
    public boolean repeatDataValidator(HttpServletRequest httpServletRequest){

         try{
             //URL+请求参数转换为json字符串
             ObjectMapper mapper = new ObjectMapper();
             Map<String,String[]> map1 = httpServletRequest.getParameterMap();
             String params = mapper.writeValueAsString(httpServletRequest.getParameterMap());
             //获取当前的URL地址,以url为key,请求参数为值,放到map里
             String url = httpServletRequest.getRequestURI();
             Map<String,String> map = new HashMap<>();
             map.put(url,params);
             String curUrlData = map.toString();
             System.out.println("当前请求数据(URL+参数):" + curUrlData);
             //以上代码拿到了当前请求的URL+请求参数,并转换为string,
             //从session中获取上一次请求存储的url和请求数据
             Object preUrlData = httpServletRequest.getSession().getAttribute("preUrlData");
             //如果上次请求的URL+数据为null,则是第一次请求服务器,将当前请求的url+数据存到session 当中(第一次请求的情况)
             if(preUrlData == null){
                 httpServletRequest.getSession().setAttribute("preUrlData",curUrlData);
                 return false;
             }else{
                 //不为空,则拿上一次请求与当前请求进行比较,相同则是重复提交,不同则不是重复提交,把当前的请求存入session,下一次请求时做判断
                 if(preUrlData.toString().equals(curUrlData)){
                     return true;
                 }else{
                     httpServletRequest.getSession().setAttribute("preUrlData",curUrlData);
                     return false;
                 }
             }
         }catch (Exception e){
             e.printStackTrace();
             System.out.println("重复数据校验出现异常");
             return false;
         }
    }

}

将注解@PreventRepeat加到表单的后台处理方法上,经测试,功能实现

防止表单重复提交和保证接口的幂等性是有区别的
防止表单重复提交是防止同一操作发生多次请求,而幂等性是同一操作发生了多次请求后的处理,切勿混淆

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值