← Назад к вопросам
Какие знаешь способы перехвата HTTP запроса?
1.7 Middle🔥 111 комментариев
#Spring Framework
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Способы перехвата HTTP запроса
HTTP Interceptors позволяют перехватывать и модифицировать запросы и ответы на глобальном уровне. Это критично для логирования, аутентификации, обработки ошибок и трансформации данных.
1. Spring Filter (Servlet Filter) — нижний уровень
Самый низкоуровневый способ, работает на уровне Servlet
// Регистрация фильтра
@Component
public class LoggingFilter implements Filter {
private static final Logger logger = LoggerFactory.getLogger(LoggingFilter.class);
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
long startTime = System.currentTimeMillis();
logger.info("Request: {} {}", httpRequest.getMethod(), httpRequest.getRequestURI());
try {
// Продолжить цепочку фильтров
chain.doFilter(request, response);
} finally {
long duration = System.currentTimeMillis() - startTime;
logger.info("Response: {} Status={}, Duration={}ms",
httpRequest.getRequestURI(),
httpResponse.getStatus(),
duration);
}
}
}
// Или явная регистрация
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<LoggingFilter> loggingFilter() {
FilterRegistrationBean<LoggingFilter> bean = new FilterRegistrationBean<>();
bean.setFilter(new LoggingFilter());
bean.addUrlPatterns("/api/*"); // Применить только к /api/*
bean.setOrder(1); // Порядок выполнения
return bean;
}
}
Практический пример: Логирование тела запроса/ответа
@Component
@Order(1)
public class RequestBodyLoggingFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
// Обертка для повторного чтения тела
HttpServletRequest wrappedRequest = new ContentCachingRequestWrapper(request);
HttpServletResponse wrappedResponse = new ContentCachingResponseWrapper(response);
try {
filterChain.doFilter(wrappedRequest, wrappedResponse);
} finally {
// Логирование тела запроса
byte[] requestBody = ((ContentCachingRequestWrapper) wrappedRequest).getContentAsByteArray();
logger.info("Request body: {}", new String(requestBody, StandardCharsets.UTF_8));
// Логирование тела ответа
byte[] responseBody = ((ContentCachingResponseWrapper) wrappedResponse).getContentAsByteArray();
logger.info("Response body: {}", new String(responseBody, StandardCharsets.UTF_8));
// Важно: записать обратно тело ответа
wrappedResponse.copyBodyToResponse();
}
}
}
2. Spring Interceptor (HandlerInterceptor) — на уровне контроллера
Работает междуDispatcherServlet и Controller
@Component
public class AuthenticationInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
String token = request.getHeader("Authorization");
if (token == null || !isValidToken(token)) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write("Unauthorized");
return false; // Остановить обработку
}
// Сохранить пользователя в контексте
request.setAttribute("user", getUserFromToken(token));
return true; // Продолжить обработку
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView) throws Exception {
// Вызывается после обработки контроллером, но до отправки ответа
logger.info("Post-processing request");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
// Вызывается после отправки ответа
if (ex != null) {
logger.error("Error during request processing", ex);
}
}
}
// Регистрация
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Bean
public AuthenticationInterceptor authenticationInterceptor() {
return new AuthenticationInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authenticationInterceptor())
.addPathPatterns("/api/**") // Применить к /api/**
.excludePathPatterns("/api/auth/**"); // Исключить /api/auth/**
}
}
3. RestTemplate Interceptor (клиентский перехват)
Перехват исходящих HTTP запросов
@Component
public class RestTemplateInterceptor implements ClientHttpRequestInterceptor {
private static final Logger logger = LoggerFactory.getLogger(RestTemplateInterceptor.class);
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body,
ClientHttpRequestExecution execution)
throws IOException {
long startTime = System.currentTimeMillis();
// Добавить заголовки ко всем запросам
request.getHeaders().add("X-Request-ID", UUID.randomUUID().toString());
request.getHeaders().add("X-Client-Version", "1.0.0");
logger.info("Outgoing request: {} {}", request.getMethod(), request.getURI());
// Выполнить запрос
ClientHttpResponse response = execution.execute(request, body);
long duration = System.currentTimeMillis() - startTime;
logger.info("Response received: Status={}, Duration={}ms",
response.getStatusCode(), duration);
return response;
}
}
// Конфигурация
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(RestTemplateInterceptor interceptor) {
RestTemplate restTemplate = new RestTemplate();
restTemplate.setInterceptors(Collections.singletonList(interceptor));
return restTemplate;
}
}
4. WebClient Interceptor (Reactive, Spring WebFlux)
Для асинхронных запросов
@Configuration
public class WebClientConfig {
@Bean
public WebClient webClient() {
return WebClient.builder()
.filter(ExchangeFilterFunction.ofRequestProcessor(request -> {
// Логирование запроса
logger.info("Request: {} {}", request.getMethod(), request.getURL());
// Добавить заголовки
request.getHeaders().add("Authorization", getAuthToken());
request.getHeaders().add("X-Request-ID", UUID.randomUUID().toString());
return Mono.just(request);
}))
.filter(ExchangeFilterFunction.ofResponseProcessor(response -> {
// Логирование ответа
logger.info("Response status: {}", response.getStatusCode());
return Mono.just(response);
}))
.build();
}
}
// Использование
@Service
public class ExternalApiService {
@Autowired
private WebClient webClient;
public Mono<String> fetchData() {
return webClient
.get()
.uri("https://api.example.com/data")
.retrieve()
.bodyToMono(String.class)
.onErrorResume(error -> {
logger.error("Error fetching data", error);
return Mono.empty();
});
}
}
5. Aspect-Oriented Programming (AOP) — через аннотации
Использование @Aspect для перехвата методов контроллера
@Aspect
@Component
public class RequestLoggingAspect {
private static final Logger logger = LoggerFactory.getLogger(RequestLoggingAspect.class);
@Pointcut("execution(* com.example.controller..*(..))")
public void controllerPointcut() {}
@Before("controllerPointcut()")
public void logBefore(JoinPoint joinPoint) {
logger.info("Calling method: {}.{}",
joinPoint.getTarget().getClass().getSimpleName(),
joinPoint.getSignature().getName());
Object[] args = joinPoint.getArgs();
logger.info("Method arguments: {}", Arrays.toString(args));
}
@After("controllerPointcut()")
public void logAfter(JoinPoint joinPoint) {
logger.info("Exiting method: {}.{}",
joinPoint.getTarget().getClass().getSimpleName(),
joinPoint.getSignature().getName());
}
@Around("controllerPointcut()")
public Object logAround(ProceedingJoinPoint pjp) throws Throwable {
long startTime = System.currentTimeMillis();
try {
Object result = pjp.proceed(); // Вызвать оригинальный метод
return result;
} finally {
long duration = System.currentTimeMillis() - startTime;
logger.info("Method execution time: {}ms", duration);
}
}
}
6. Custom Annotation для перехвата
Создание специализированного перехвата
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogRequest {
String value() default "";
}
@Aspect
@Component
public class LogRequestAspect {
@Around("@annotation(logRequest)")
public Object logRequest(ProceedingJoinPoint pjp, LogRequest logRequest) throws Throwable {
logger.info("Logging request: {}", logRequest.value());
return pjp.proceed();
}
}
// Использование
@RestController
public class UserController {
@GetMapping("/users/{id}")
@LogRequest("Fetching user by ID")
public ResponseEntity<UserDto> getUser(@PathVariable Long id) {
// ...
}
}
7. Перехват в Spring Security
Специализированный перехват для безопасности
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public SecurityContextHolderAwareRequestFilter securityContextHolderAwareRequestFilter() {
return new SecurityContextHolderAwareRequestFilter();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilterBefore(new JwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.antMatchers("/api/**").authenticated()
.and()
.csrf().disable();
}
}
// Custom фильтр
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
String token = request.getHeader("Authorization");
if (token != null && token.startsWith("Bearer ")) {
token = token.substring(7);
try {
User user = parseToken(token);
UserDetails userDetails = new User(user.getUsername(), "", user.getAuthorities());
Authentication auth = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(auth);
} catch (Exception e) {
logger.error("Token validation failed", e);
}
}
filterChain.doFilter(request, response);
}
}
Сравнение способов
┌─────────────────────────────────────────────────────────┐
│ Filter (Servlet) │
│ - Самый низкоуровневый │
│ - Работает ДО Spring │
│ - Идеален для логирования, CORS, сжатия │
├─────────────────────────────────────────────────────────┤
│ Interceptor (HandlerInterceptor) │
│ - На уровне Spring MVC │
│ - Имеет доступ к Model, Handler, ModelAndView │
│ - Идеален для аутентификации, авторизации │
├─────────────────────────────────────────────────────────┤
│ AOP (@Aspect) │
│ - На уровне методов компонентов │
│ - Идеален для кроссрезовых задач (логирование, метрики) │
├─────────────────────────────────────────────────────────┤
│ RestTemplate/WebClient Interceptor │
│ - Для исходящих запросов │
│ - Добавление заголовков, retry logic │
└─────────────────────────────────────────────────────────┘
Best Practices
- Используй Filter для общей логики (CORS, логирование)
- Используй Interceptor для бизнес-логики (аутентификация, авторизация)
- Используй AOP для кроссрезовых задач (метрики, профилирование)
- Логируй все HTTP запросы для отладки
- Добавляй Request ID для отслеживания в распределенной системе
- Не обрабатывай в фильтре то, что можно в контроллере — разделение ответственности
- Помни об производительности — минимизируй обработку в фильтрах
Правильный выбор способа перехвата критичен для чистой архитектуры приложения.