Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Фильтры

Некоторые действия нужны для каждого запроса: записать его в лог, добавить заголовок, проверить доступ. Копировать такой код в каждый сервлет неудобно. Фильтр — класс, через который запрос проходит до сервлета, а ответ — после. Фильтры срабатывают и для сервлетов, и для REST-ресурсов, и для статических файлов.

Фильтр-логгер

Создайте файл src/main/java/com/example/blog/LoggingFilter.java:

// src/main/java/com/example/blog/LoggingFilter.java
package com.example.blog;
 
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
 
@WebFilter("/*")
public class LoggingFilter extends HttpFilter {
 
    @Override
    protected void doFilter(HttpServletRequest request, HttpServletResponse response,
                            FilterChain chain) throws IOException, ServletException {
        long start = System.currentTimeMillis();
 
        chain.doFilter(request, response);
 
        long time = System.currentTimeMillis() - start;
        System.out.println(request.getMethod() + " " + request.getRequestURI()
                + " -> " + response.getStatus() + " (" + time + " ms)");
    }
}

@WebFilter("/*") включает фильтр для всех адресов приложения. Вызов chain.doFilter передаёт запрос дальше — следующему фильтру или сервлету. Код до этой строки выполняется перед обработкой, код после — когда ответ уже готов.

Сделайте несколько запросов, и в терминале liberty:dev (и в console.log сервера) появятся строки:

GET /blog/ -> 200 (27 ms)
GET /blog/search -> 200 (4 ms)
GET /blog/search -> 400 (4 ms)
POST /blog/subscribe -> 302 (5 ms)

getRequestURI() не включает строку запроса, поэтому ?q=java в логе не видно.

Фильтр, который меняет ответ

Второй фильтр добавляет заголовок к каждому ответу:

// src/main/java/com/example/blog/PoweredByFilter.java
package com.example.blog;
 
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
 
@WebFilter("/*")
public class PoweredByFilter extends HttpFilter {
 
    @Override
    protected void doFilter(HttpServletRequest request, HttpServletResponse response,
                            FilterChain chain) throws IOException, ServletException {
        response.setHeader("X-Powered-By", "Blog on Jakarta EE");
        chain.doFilter(request, response);
    }
}

Заголовок ставится до chain.doFilter: когда сервлет начнёт писать тело, менять заголовки будет поздно.

curl -i http://localhost:8080/blog/hello
HTTP/1.1 200 OK
X-Powered-By: Blog on Jakarta EE
Content-Type: text/plain; charset=UTF-8

Что обычно делают фильтры

ЗадачаПример
ЛогированиеМетод, адрес, время ответа
ЗаголовкиCORS, кеширование, безопасность
Проверка доступаНет сессии — перенаправить на вход
КодировкаУстановить UTF-8 для всех запросов

Шаблоны адресов те же, что у сервлетов: /*, /admin/*, *.html. Порядок нескольких фильтров с аннотациями не гарантирован. Если он важен, фильтры перечисляют в файле src/main/webapp/WEB-INF/web.xml.

Частая ошибка

Забыть вызвать chain.doFilter(request, response). Тогда запрос до сервлета не доходит, а клиент получает пустой ответ с кодом 200:

HTTP/1.1 200 OK
X-Powered-By: Blog on Jakarta EE
Content-Length: 0

Не вызывать chain.doFilter можно только намеренно — например, когда фильтр сам отвечает ошибкой 403.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.