SpringBoot处理短时间内重复提交

为了解决用户短时间内频繁访问一个接口,造成可能出现的数据异常或者为了减轻服务器的压力。必须对短时间内的频繁请求进行处理。

  1. 添加用到的依赖



    org.springframework.boot
    spring-boot-starter-aop



    org.springframework.boot
    spring-boot-starter-data-redis
  1. 自定义注解

@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RepeatSubmit {

    /**
     * 默认失效时间2秒
     * @return
     */
    long seconds() default 2;
}
  1. 自定义一个重复提交异常类

public class RepeatSubmitException extends RuntimeException{

    public RepeatSubmitException(String msg){
        super(msg);
    }
}
  1. 创建一个重复请求拦截器

import com.example.demo.annotation.RepeatSubmit;
import com.example.demo.exception.RepeatSubmitException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Component
public class RepeatSubmitInterceptor implements HandlerInterceptor {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws RepeatSubmitException {
        if (handler instanceof HandlerMethod) {
            //只拦截标注了@RepeatSubmit该注解
            HandlerMethod method = (HandlerMethod) handler;
            //标注在方法上的@RepeatSubmit
            RepeatSubmit repeatSubmitByMethod =
                    AnnotationUtils.findAnnotation(method.getMethod(), RepeatSubmit.class);
            //标注在controler类上的@RepeatSubmit
            RepeatSubmit repeatSubmitByCls =
                    AnnotationUtils.findAnnotation(method.getMethod().getDeclaringClass(), RepeatSubmit.class);
            //没有限制重复提交,直接跳过
            if (Objects.isNull(repeatSubmitByMethod) && Objects.isNull(repeatSubmitByCls))
                return true;
            // todo: 组合判断条件,这里仅仅是演示,实际项目中根据架构组合条件
            //请求的URI
            String uri = request.getRequestURI();
            //存在即返回false,不存在即返回true
            Boolean ifAbsent = stringRedisTemplate.opsForValue().setIfAbsent(uri, "",
                    Objects.nonNull(repeatSubmitByMethod) ? repeatSubmitByMethod.seconds() : repeatSubmitByCls.seconds(),
                    TimeUnit.SECONDS);
            //如果存在,表示已经请求过了,直接抛出异常,由全局异常进行处理返回指定信息
            if (ifAbsent != null && !ifAbsent) {
                throw new RepeatSubmitException("重复请求");
            }
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}
  1. 创建统一结果返回类

@Getter
@Setter
public class ResultResponse {

    String msg;

    public ResultResponse(String msg){
        this.msg = msg;
    }
}
  1. 创建全局统一异常处理类 处理重复请求异常

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     *  重复请求的异常
     * @param ex
     * @return
     */
    @ExceptionHandler(RepeatSubmitException.class)
    public ResultResponse onException(RepeatSubmitException ex){
        log.error("错误异常:"+ex.getMessage());

        return new ResultResponse("请勿重复提交");
    }
}
  1. 测试

@RestController
//类上标注了@RepeatSubmit注解,该类全部请求都需要拦截
//@RepeatSubmit
public class LoginController {

    /**
     * 方法上标注@RepeatSubmit注解 只针对该请求进行拦截
     */
    @RepeatSubmit
    @GetMapping("/login")
    public String toLogin(){
        return "login success";
    }

}
  1. 结果正常情况

SpringBoot处理短时间内重复提交_第1张图片

错误情况

SpringBoot处理短时间内重复提交_第2张图片

你可能感兴趣的:(SpringBoot,spring,boot,后端,java)