建议控制器方法*之前*处理@Valid注释

我正在使用Spring MVC 4.1向宁静的Web服务添加速率限制。

我创建了@RateLimited可以应用于控制器方法的注释。Spring AOP方面会拦截对这些方法的调用,如果请求过多,则会引发异常:

@Aspect

@Component

@Order(Ordered.HIGHEST_PRECEDENCE)

public class RateLimitingAspect {

@Autowired

private RateLimitService rateLimitService;

@Before("execution(* com.example..*.*(.., javax.servlet.ServletRequest+, ..)) " +

"&& @annotation(com.example.RateLimited)")

public void wait(JoinPoint jp) throws Throwable {

ServletRequest request =

Arrays

.stream(jp.getArgs())

.filter(Objects::nonNull)

.filter(arg -> ServletRequest.class.isAssignableFrom(arg.getClass()))

.map(ServletRequest.class::cast)

.findFirst()

.get();

String ip = request.getRemoteAddr();

int secondsToWait = rateLimitService.secondsUntilNextAllowedAttempt(ip);

if (secondsToWait > 0) {

throw new TooManyRequestsException(secondsToWait);

}

}

除了@RateLimited控制器方法的参数标记为@Valid,例如,这一切都可以正常工作:

@RateLimited

@RequestMapping(method = RequestMethod.POST)

public HttpEntity<?> createAccount(

HttpServletRequest request,

@Valid @RequestBody CreateAccountRequestDto dto) {

...

}

问题:如果验证失败,则验证器将抛出MethodArgumentNotValidException,由来处理@ExceptionHandler,该会向客户端返回错误响应,从不触发my

@Before,因此绕过了速率限制。

我曾经考虑过使用Spring Interceptor或普通的Servlet过滤器,但是它们是通过简单的url模式映射的,我需要通过GET / POST /

PUT / etc进行区分。

回答:

我最终放弃了尝试寻找AOP解决方案的方法,而是创建了一个Spring

Interceptor。拦截器将处理preHandle所有请求,并监视其处理程序为的请求@RateLimited

@Component

public class RateLimitingInterceptor extends HandlerInterceptorAdapter {

@Autowired

private final RateLimitService rateLimitService;

@Override

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

if (HandlerMethod.class.isAssignableFrom(handler.getClass())) {

rateLimit(request, (HandlerMethod)handler);

}

return super.preHandle(request, response, handler);

}

private void rateLimit(HttpServletRequest request, HandlerMethod handlerMethod) throws TooManyRequestsException {

if (handlerMethod.getMethodAnnotation(RateLimited.class) != null) {

String ip = request.getRemoteAddr();

int secondsToWait = rateLimitService.secondsUntilNextAllowedInvocation(ip);

if (secondsToWait > 0) {

throw new TooManyRequestsException(secondsToWait);

} else {

rateLimitService.recordInvocation(ip);

}

}

}

}

以上是 建议控制器方法*之前*处理@Valid注释 的全部内容, 来源链接: utcz.com/qa/419162.html

回到顶部