Рубрики
Без рубрики

Исключение URL-адресов для фильтра в веб-приложении Spring

Узнайте, как исключить URL-адреса из выполнения в веб-фильтрах Spring. Немного продвинутый, но очень полезный.

Автор оригинала: baeldung.

1. Обзор

Большинство веб-приложений используют такие операции, как ведение журнала запросов, проверка подлинности или аутентификация. И, более того, такие задачи обычно разделяются между набором конечных точек HTTP .

Хорошей новостью является то, что веб-фреймворк Spring предоставляет механизм фильтрации именно для этой цели.

В этом уроке мы узнаем, как задача в стиле фильтра может быть включена или исключена из выполнения для заданного набора URL-адресов .

2. Фильтр для определенных URL-адресов

Допустим, наше веб-приложение должно регистрировать некоторую информацию о своих запросах, такую как их пути и типы контента. Один из способов сделать это-создать фильтр ведения журнала.

2.1. Фильтр регистрации

Во-первых, давайте создадим наш loggingfilter в классе Log Filter , который расширяет класс | OncePerRequestFilter и реализует метод doFilterInternal :

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
  FilterChain filterChain) throws ServletException, IOException {
    String path = request.getRequestURI();
    String contentType = request.getContentType();
    logger.info("Request URL path : {}, Request content type: {}", path, contentType);
    filterChain.doFilter(request, response);
}

2.1. Фильтр правил

Предположим, что нам нужно, чтобы задача ведения журнала выполнялась только для выбранных шаблонов URL-адресов, а именно /health , /faq/*. Для этого мы зарегистрируем наш фильтр ведения журнала с помощью FilterRegistrationBean таким образом, чтобы он соответствовал только требуемым шаблонам URL-адресов:

@Bean
public FilterRegistrationBean logFilter() {
    FilterRegistrationBean registrationBean = new FilterRegistrationBean<>();
    registrationBean.setFilter(new LogFilter());
    registrationBean.addUrlPatterns("/health","/faq/*");
    return registrationBean;
}

2.2. Фильтр исключения

Если мы хотим исключить URL-адреса из выполнения задачи ведения журнала, мы можем легко добиться этого двумя способами:

  • Для нового URL-адреса убедитесь, что он не соответствует шаблонам URL-адресов, используемым фильтром
  • Для старого URL-адреса, для которого ранее было включено ведение журнала, мы можем изменить шаблон URL-адреса, чтобы исключить этот URL-адрес

3. Фильтр для всех возможных URL-адресов

Мы легко выполнили наш предыдущий вариант использования включения URL-адресов в фильтр Log с минимальными усилиями. Однако становится сложнее, если Фильтр использует подстановочный знак (*) для сопоставления всех возможных URL-шаблонов.

В этом случае нам нужно будет самостоятельно написать логику включения и исключения.

3.1. Пользовательский фильтр

Клиенты могут отправлять полезную информацию на сервер с помощью заголовков запросов. Допустим, ваше веб-приложение в настоящее время работает только в Соединенных Штатах, а это означает, что мы не хотим обрабатывать запросы, поступающие из других стран.

Давайте далее представим, что наше веб-приложение указывает локаль с помощью заголовка запроса X-Country-Code . Следовательно, каждый запрос поставляется с этой информацией, и у нас есть четкий аргумент в пользу использования фильтра.

Давайте реализуем Фильтр , который проверяет заголовок, отклоняя запросы, которые не соответствуют нашим условиям:

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
      FilterChain filterChain) throws ServletException, IOException {

    String countryCode = request.getHeader("X-Country-Code");
    if (!"US".equals(countryCode)) {
        response.sendError(HttpStatus.BAD_REQUEST.value(), "Invalid Locale");
        return;
    }

    filterChain.doFilter(request, response);
}

3.2. Регистрация фильтра

Начнем с того, что l et использует подстановочный знак звездочка (*) для регистрации нашего фильтра , чтобы соответствовать всем возможным URL-шаблонам:

@Bean
public FilterRegistrationBean headerValidatorFilter() {
    FilterRegistrationBean registrationBean = new FilterRegistrationBean<>();
    registrationBean.setFilter(new HeaderValidatorFilter());
    registrationBean.addUrlPatterns("*");
    return registrationBean;
}

В более поздний момент времени мы можем исключить шаблоны URL-адресов, которые не требуются для выполнения задачи проверки информации заголовка запроса локали.

4. Исключение URL-адресов

В этом разделе мы узнаем, как исключить URL-адреса для нашего клиента Filter .

4.1. Наивная стратегия

Давайте еще раз представим, что у нас есть веб-маршрут в /health , который можно использовать для проверки работоспособности приложения для пинг-понга.

До сих пор все запросы запускали наш фильтр. Как мы можем догадаться, это накладные расходы, когда речь заходит о нашей проверке здоровья.

Итак, давайте упростим наши запросы /здоровье , исключив их из основной части нашего фильтра:

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
  FilterChain filterChain) throws ServletException, IOException {
    String path = request.getRequestURI();
    if ("/health".equals(path)) {
    	filterChain.doFilter(request, response);
    	return;
    }

    String countryCode = request.getHeader("X-Country-Code");
    // ... same as before
}

Мы должны отметить, что добавление этой пользовательской логики в метод doFilter вводит связь между конечной точкой /health и нашим фильтром . Таким образом, это не оптимально, так как мы можем нарушить логику фильтрации, если изменим конечную точку проверки работоспособности без внесения соответствующих изменений в метод doFilter .

4.2. Использование метода “Не следует фильтровать”

В предыдущем подходе мы ввели тесную связь между исключением URL-адреса и логикой выполнения задачи для фильтра. Можно непреднамеренно ввести ошибку в одну часть, намереваясь внести изменения в другую часть.

Вместо этого мы можем изолировать два набора логики, переопределив метод Не должен фильтровать :

@Override
protected boolean shouldNotFilter(HttpServletRequest request)
  throws ServletException {
    String path = request.getRequestURI();
    return "/health".equals(path);
}

В результате метод do Internal Filter() соблюдает принцип единой ответственности :

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
  FilterChain filterChain) throws ServletException, IOException {
    String countryCode = request.getHeader("X-Country-Code");
    // ... same as before
}

5. Заключение

В этом руководстве мы изучили, как исключить шаблон(ы) URL-адресов из фильтра сервлетов в веб-приложении Spring Boot для двух вариантов использования, а именно ведения журнала и проверки заголовка запроса.

Более того, мы узнали, что становится сложно исключить определенный набор URL-адресов для фильтра, который использует подстановочный знак * для сопоставления всех возможных шаблонов URL-адресов .

Как всегда, полный исходный код учебника доступен на GitHub .