1. Обзор
В большинстве веб-приложений используются такие операции, как ведение журнала запросов, проверка или аутентификация. Более того, такие задачи обычно распределяются между несколькими конечными точками HTTP .
Хорошей новостью является то, что веб-фреймворк Spring предоставляет механизм фильтрации именно для этой цели.
В этом руководстве мы узнаем, как можно включить или исключить задачу в стиле фильтра для заданного набора URL-адресов .
2. Фильтр для определенных URL-адресов
Допустим, нашему веб-приложению необходимо регистрировать некоторую информацию о своих запросах, например их пути и типы содержимого. Один из способов сделать это — создать фильтр ведения журнала.
2.1. Фильтр регистрации
Во-первых, давайте создадим наш фильтр ведения журнала в классе LogFilter
, который расширяет класс 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> logFilter() {
FilterRegistrationBean<LogFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new LogFilter());
registrationBean.addUrlPatterns("/health","/faq/*");
return registrationBean;
}
2.2. Исключающий фильтр
Если мы хотим исключить URL-адреса из выполнения задачи ведения журнала, мы можем легко добиться этого двумя способами:
- Для нового URL-адреса убедитесь, что он не соответствует шаблонам URL-адресов, используемым фильтром.
- Для старого URL-адреса, для которого ранее было включено ведение журнала, мы можем изменить шаблон URL-адреса, чтобы исключить этот URL-адрес.
3. Фильтр для всех возможных URL-адресов
Мы легко справились с нашим предыдущим вариантом использования включения URL-адресов в LogFilter
с минимальными усилиями. Однако становится сложнее, если фильтр
использует подстановочный знак (*) для сопоставления всех возможных шаблонов 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. Регистрация фильтра
Для начала давайте воспользуемся подстановочным знаком звездочки (*), чтобы зарегистрировать наш фильтр для соответствия всем возможным шаблонам URL:
@Bean
public FilterRegistrationBean<HeaderValidatorFilter> headerValidatorFilter() {
FilterRegistrationBean<HeaderValidatorFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new HeaderValidatorFilter());
registrationBean.addUrlPatterns("*");
return registrationBean;
}
Позже мы можем исключить шаблоны URL, которые не требуются для выполнения задачи проверки информации заголовка запроса локали.
4. Исключение URL-адресов
В этом разделе мы узнаем, как исключить URL-адреса для нашего фильтра
клиентов .
4.1. Наивная стратегия
Давайте снова представим, что у нас есть веб-маршрут в /health
, который можно использовать для пинг-понговой проверки работоспособности приложения.
Пока что все запросы будут запускать наш фильтр. Как мы можем догадаться, это накладные расходы, когда дело доходит до проверки нашего здоровья.
Итак, давайте упростим наши запросы /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. Использование метода shouldNotFilter
В предыдущем подходе мы ввели тесную связь между исключением URL-адресов и логикой выполнения задачи для фильтра. Можно непреднамеренно внести ошибку в одну часть, намереваясь внести изменения в другую часть.
Вместо этого мы можем изолировать два набора логики, переопределив метод shouldNotFilter
:
@Override
protected boolean shouldNotFilter(HttpServletRequest request)
throws ServletException {
String path = request.getRequestURI();
return "/health".equals(path);
}
В результате метод doInternalFilter () соблюдает
принцип единой ответственности :
@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 .