(资料图片仅供参考)
这种防请求重复提交的实现有基于Filter
的实现,也有基于HandlerInterceptor
的实现。最后考量下笔者认为利用RequestBodyAdviceAdapter
类来实现代码实现更加简洁,配置更加简单。
在此笔者提供一个注解
+RequestBodyAdviceAdapter
配合使用的防重复提交的实现。但是这个方案有个小弊端。仅生效于有RequestBody注解的参数,因为使用RequestBodyAdvice来实现。但是大部分我们需要做请求防重复提交的接口一般都是POST请求,且有requestBody。
完整实现在开源项目中:https://github.com/valarchie/AgileBoot-Back-End
/** * 自定义注解防止表单重复提交 * 仅生效于有RequestBody注解的参数 因为使用RequestBodyAdvice来实现 * @author valarchie */@Inherited@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Resubmit { /** * 间隔时间(s),小于此时间视为重复提交 */ int interval() default 5;}
大致的实现是。
supports
方法,指明我们仅处理拥有Resubmit
注解的方法。generateResubmitRedisKey
方法实现。格式如下:resubmit:{}:{}:{}。比如用户是userA。我们请求的类是UserService。方法名是addUser。则这个key为resubmit:userA:UserService:addUser
。/** * 重复提交拦截器 如果涉及前后端加解密的话 也可以通过继承RequestBodyAdvice来实现 * * @author valarchie */@ControllerAdvice(basePackages = "com.agileboot")@Slf4j@RequiredArgsConstructorpublic class ResubmitInterceptor extends RequestBodyAdviceAdapter { public static final String NO_LOGIN = "Anonymous"; public static final String RESUBMIT_REDIS_KEY = "resubmit:{}:{}:{}"; @NonNull private RedisUtil redisUtil; @Override public boolean supports(MethodParameter methodParameter, Type targetType, Class extends HttpMessageConverter>> converterType) { return methodParameter.hasMethodAnnotation(Resubmit.class); } /** * @param body 仅获取有RequestBody注解的参数 */ @NotNull @Override public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class extends HttpMessageConverter>> converterType) { // 仅获取有RequestBody注解的参数 String currentRequest = JSONUtil.toJsonStr(body); Resubmit resubmitAnno = parameter.getMethodAnnotation(Resubmit.class); if (resubmitAnno != null) { String redisKey = generateResubmitRedisKey(parameter.getMethod()); log.info("请求重复提交拦截,当前key:{}, 当前参数:{}", redisKey, currentRequest); String preRequest = redisUtil.getCacheObject(redisKey); if (preRequest != null) { boolean isSameRequest = Objects.equals(currentRequest, preRequest); if (isSameRequest) { throw new ApiException(ErrorCode.Client.COMMON_REQUEST_RESUBMIT); } } redisUtil.setCacheObject(redisKey, currentRequest, resubmitAnno.interval(), TimeUnit.SECONDS); } return body; } public String generateResubmitRedisKey(Method method) { String username; try { LoginUser loginUser = AuthenticationUtils.getLoginUser(); username = loginUser.getUsername(); } catch (Exception e) { username = NO_LOGIN; } return StrUtil.format(RESUBMIT_REDIS_KEY, method.getDeclaringClass().getName(), method.getName(), username); }}
通过在Controller上打上Resubmit
注解即可,interval即多久的间隔内相同参数视为重复请求。
/** * 新增通知公告 */@Resubmit(interval = 60)@PostMappingpublic ResponseDTO add(@RequestBody NoticeAddCommand addCommand) { noticeApplicationService.addNotice(addCommand); return ResponseDTO.ok();}
这是笔者关于中小型项目防请求重复提交的实现,如有不足欢迎大家评论指正。
标签:
Copyright © 2015-2023 港澳物业网版权所有 备案号:京ICP备2023022245号-31 联系邮箱:435 226 40 @qq.com