Recommend controller method * before @Valid annotation is processed

I am adding a speed limit for a calm webservice using Spring MVC 4.1.

I created an @RateLimited annotation that I can apply to controller methods. A Spring AOP aspect intercepts calls to these methods and throws an exception if there are too many requests:

 @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); } } 

All this works fine, except when the @RateLimited controller @RateLimited has parameters marked as @Valid , for example:

 @RateLimited @RequestMapping(method = RequestMethod.POST) public HttpEntity<?> createAccount( HttpServletRequest request, @Valid @RequestBody CreateAccountRequestDto dto) { ... } 

Problem: if the test fails, the validator throws a MethodArgumentNotValidException , which is handled by @ExceptionHandler , which returns the client’s response to the client, never calling my @Before and therefore bypassing the speed limit.

How can I intercept such a web request in a way that takes precedence over parameter validation?

I was thinking about using Spring hooks or simple servlet filters, but they are displayed using simple url templates, and I need to distinguish between GET / POST / PUT, etc.

+1
spring-mvc spring-security rate-limiting
source share
2 answers

In the end, I gave up trying to find an AOP solution and instead created a Spring interceptor. The preHandle interceptor all requests and hours for requests whose handler is @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); } } } } 
+2
source share

See if you can implement the same logic for the @@ AfterTrowing board, which will have the same pointcut.

0
source share

All Articles