Spring Boot Servlet Filter
Filter, Java web dünyasının (Servlet API'si) en eski ara katman mekanizmasıdır (topic 65). Bir isteği, daha Spring MVC'ye (DispatcherServlet) ulaşmadan en dışta karşılar ve yanıt çıkarken son olarak görür. Spring Boot…
Spring Boot Servlet Filter
Filter, Java web dünyasının (Servlet API'si) en eski ara katman mekanizmasıdır (topic 65). Bir isteği, daha Spring MVC'ye (DispatcherServlet) ulaşmadan en dışta karşılar ve yanıt çıkarken son olarak görür. Spring Boot, filter'ları kolayca kaydetmeni sağlar. Bu konu, filter'ları ve interceptor'dan (topic 11) ne zaman ayrıldığını ele alır.
Filter: doFilter ve zincir
Bir Filter, tek metodu doFilter ile isteği sarmalar ve zinciri ilerletir:
class IzlemeFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) { // ÖNCE: istek henüz işlenmedi chain.doFilter(req, res); // zinciri ilerlet (sonraki filter -> DispatcherServlet -> controller) // SONRA: yanıt hazır } }
chain.doFilter(...) çağrısının öncesi isteğe, sonrası yanıta karşılık gelir. Çağırmazsan
istek hiç işlenmez (isteği burada kesebilirsin). Birden çok filter bir zincir oluşturur.
Örnek 1 (./Ornek1.java) her isteğe bir izleme kimliği (trace id) başlığı ekleyen ve süreyi
loglayan bir filter kurar; self-test bu başlığı yanıtta okur.
Filter'ı kaydetmek
Spring Boot'ta birkaç yol var:
// 1) FilterRegistrationBean — yol/sıra üzerinde tam kontrol (önerilen) @Bean FilterRegistrationBean<IzlemeFilter> f() { var reg = new FilterRegistrationBean<>(new IzlemeFilter()); reg.addUrlPatterns("/*"); reg.setOrder(1); return reg; } // 2) @Component olarak işaretle — otomatik tüm yollara kaydedilir (basit ama kontrol az) @Component class IzlemeFilter implements Filter { ... }
FilterRegistrationBean, hangi yollara (addUrlPatterns) ve hangi sırayla (setOrder)
uygulanacağını belirlemeni sağlar.
Filter vs Interceptor
İkisi de "araya girer" ama farklı katmanlarda:
| Filter (Servlet) | Interceptor (Spring MVC) | |
|---|---|---|
| Seviye | Konteyner — en dışta | DispatcherServlet sonrası (MVC içinde) |
| Kapsam | Her şey (statik dosyalar, hata sayfaları dahil) | Yalnızca controller'a giden istekler |
| Erişim | Ham ServletRequest/Response | Handler (controller metodu), model |
| Tipik | CORS, sıkıştırma, kimlik, kodlama, trace id | MVC yetki, model ekleme, süre ölçme |
Sıra: Filter → DispatcherServlet → Interceptor → Controller (ve dönüşte ters). Genel/düşük seviyeli işler filter'a, MVC-bilgisi gereken işler interceptor'a gider.
Tipik kullanım alanları
- Trace id / korelasyon kimliği ekleme (dağıtık loglama).
- CORS (genelde Spring'in CORS desteği veya bir CorsFilter ile — sonraki konu).
- Sıkıştırma, karakter kodlama, güvenlik başlıkları.
- Kimlik doğrulama (Spring Security büyük ölçüde filter zincirine dayanır!).
Not: Spring Security tamamen bir filter zinciri üzerine kuruludur (topic 04). Yani her güvenli istek, sen yazmasan da bir dizi filter'dan geçer.
Özet
Servlet Filter'ın isteği en dışta sarmalayan ara katman olduğunu; doFilter + zincir mantığını,
Spring Boot'ta FilterRegistrationBean ile kaydı (Örnek 1); Filter↔Interceptor farkını (seviye,
kapsam, erişim) ve tipik kullanımları (trace id, CORS, güvenlik) öğrendik. Sırada, tarayıcı
güvenliğinin önemli bir konusu: CORS desteği.
▶ Kod Örnekleri(1)
Ornek1
ortam gerekir1// Ornek1: Servlet Filter — servlet konteyneri seviyesinde her isteği sarmalamak.
2// Çalıştırma: portal gömülü Tomcat ile başlatır, self-test çıktısını alır.
3package com.egitim.springboot.filter;
4
5import jakarta.servlet.*;
6import jakarta.servlet.http.HttpServletRequest;
7import jakarta.servlet.http.HttpServletResponse;
8import org.springframework.boot.CommandLineRunner;
9import org.springframework.boot.SpringApplication;
10import org.springframework.boot.autoconfigure.SpringBootApplication;
11import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
12import org.springframework.boot.web.servlet.FilterRegistrationBean;
13import org.springframework.context.annotation.Bean;
14import org.springframework.web.bind.annotation.*;
15import org.springframework.web.client.RestClient;
16
17import java.io.IOException;
18import java.util.UUID;
19
20@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
21public class Ornek1 {
22
23 public static void main(String[] args) { SpringApplication.run(Ornek1.class, args); }
24
25 // Filter: en dışta, TÜM isteklerde (statik dosyalar dahil) çalışır; ham request/response görür.
26 static class IzlemeFilter implements Filter {
27 @Override
28 public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
29 throws IOException, ServletException {
30 HttpServletRequest http = (HttpServletRequest) req;
31 HttpServletResponse httpRes = (HttpServletResponse) res;
32
33 // Her isteğe bir izleme kimliği (trace id) ekle — log korelasyonu için.
34 String izlemeId = UUID.randomUUID().toString().substring(0, 8);
35 httpRes.setHeader("X-Izleme-Id", izlemeId);
36 long t0 = System.currentTimeMillis();
37 System.out.println(" [filter:önce] " + http.getMethod() + " " + http.getRequestURI() + " izlemeId=" + izlemeId);
38
39 chain.doFilter(req, res); // zinciri ilerlet -> isteğin geri kalanı (interceptor, controller) çalışır
40
41 System.out.println(" [filter:sonra] durum=" + httpRes.getStatus()
42 + " süre=" + (System.currentTimeMillis() - t0) + "ms");
43 }
44 }
45
46 // Filter'ı kaydet + hangi yollara/sırayla uygulanacağını belirle.
47 @Bean
48 FilterRegistrationBean<IzlemeFilter> izlemeFilter() {
49 FilterRegistrationBean<IzlemeFilter> reg = new FilterRegistrationBean<>(new IzlemeFilter());
50 reg.addUrlPatterns("/*"); // tüm yollar
51 reg.setOrder(1); // birden çok filter varsa sıra
52 return reg;
53 }
54
55 @RestController
56 static class DemoController {
57 @GetMapping("/selam") String selam() { return "merhaba"; }
58 }
59
60 @Bean
61 CommandLineRunner selfTest() {
62 return args -> {
63 RestClient c = RestClient.create("http://localhost:8080");
64 System.out.println("\n========= SERVLET FILTER SELF-TEST =========");
65 var yanit = c.get().uri("/selam").retrieve().toEntity(String.class);
66 System.out.println("yanıt gövdesi: " + yanit.getBody());
67 System.out.println("Filter'ın eklediği başlık X-Izleme-Id: " + yanit.getHeaders().getFirst("X-Izleme-Id"));
68 System.out.println("(Filter her istekte zincirden önce/sonra çalıştı ve başlık ekledi.)");
69 System.out.println("============================================");
70 };
71 }
72}Bu örnek Spring / Spring Boot (Gradle) ortamı gerektirir; tek dosya olarak java Ornek1.java ile çalışmaz. Orijinal portal bunu gömülü Tomcat / Spring context ile koşuyordu. Beklenen davranış yukarıdaki anlatım ve kodda açıklanmıştır.