2015-03-10 1 views
1

Я добавляю ограничение скорости для спокойного webservice с использованием Spring MVC 4.1.советьте метод контроллера * перед обработкой @ @ Valid аннотации

Я создал аннотацию @RateLimited, которую я могу применить к методам контроллера. A 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, который возвращает ошибочный ответ клиента, никогда не запускающий мой @Before и, следовательно, обходя ограничивающую скорость.

Как я могу перехватить веб-запрос, подобный этому, таким образом, который имеет приоритет над проверкой параметров?

Я думал об использовании перехватчиков Spring или простых сервлетов Фильтры, но они сопоставляются простыми url-образцами, и мне нужно различать GET/POST/PUT/etc.

+0

Это хорошая проблема. Интересно, что делает '@ RateLimited'. По умолчанию проверка поля с использованием '@ Valid' является болезненной в некоторых случаях. Возможно ли создать собственный 'Validator'. Я могу подумать о добавлении «Заголовок», чтобы отличить этот конкретный запрос. Но это добавит условную проверку для каждого запроса. Я попытался бы решить его на уровне метода, и если это не сработает, то подойдите к «перехватчику» –

ответ

2

В конце концов я отказался от попытки найти решение АОП и вместо этого создал Spring Interceptor. Перехватчик preHandle s все запросы и часы для запросов, чей обработчик @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); 
      } 
     } 
    } 
} 
0

Посмотрите, возможно ли реализовать аналогичную логику для @@ AfterTrowing совета, который будет иметь аналогичный pointcut.

 Смежные вопросы

  • Нет связанных вопросов^_^