Eğitim Portalı/Spring Boot/Spring Boot Interceptor (HandlerInterceptor)
Spring Boot03-spring-boot/11-interceptor

Spring Boot Interceptor (HandlerInterceptor)

Birçok istekte tekrar eden işler vardır: loglama, süre ölçme, kimlik/yetki kontrolü, istek sayacı, dil ayarı. Bunları her controller metoduna tek tek yazmak hem tekrar hem hata kaynağıdır. Interceptor, Spring MVC sevi…

Spring Boot Interceptor (HandlerInterceptor)

Birçok istekte tekrar eden işler vardır: loglama, süre ölçme, kimlik/yetki kontrolü, istek sayacı, dil ayarı. Bunları her controller metoduna tek tek yazmak hem tekrar hem hata kaynağıdır. Interceptor, Spring MVC seviyesinde isteğin önüne ve arkasına girerek bu ortak işleri tek yerde toplar. Servlet Filter'a benzer ama Spring MVC'ye özeldir (sonraki konuda farkı göreceğiz).

HandlerInterceptor: üç kanca

HandlerInterceptor üç metot sunar:

class ZamanlamaInterceptor implements HandlerInterceptor {
    boolean preHandle(req, res, handler)   { ... return true; }  // controller'dan ÖNCE
    void postHandle(req, res, handler, mav) { ... }              // controller SONRASI, view öncesi
    void afterCompletion(req, res, handler, ex) { ... }          // her şey bitince (hata dahil)
}
  • preHandle: Controller metodu çağrılmadan önce. false dönerse istek controller'a hiç ulaşmaz (örn. yetki yoksa burada kes). İstek başlangıç zamanını koymak, kimlik kontrolü.
  • postHandle: Controller başarıyla döndükten sonra, yanıt render edilmeden önce.
  • afterCompletion: İstek tamamen bittiğinde (başarı veya hata) — kaynak temizleme, süre/durum loglama.

Örnek 1 (./Ornek1.java) her isteğin süresini ve durum kodunu ölçen bir interceptor kurar.

Interceptor'ı kaydetmek

Bir WebMvcConfigurer ile interceptor'ı ekler ve hangi yollara uygulanacağını belirlersin:

@Bean WebMvcConfigurer config() {
    return new WebMvcConfigurer() {
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(new ZamanlamaInterceptor())
                    .addPathPatterns("/api/**")        // bu yollara uygula
                    .excludePathPatterns("/api/health"); // bunları hariç tut
        }
    };
}

Interceptor vs Filter

İkisi de "araya girer" ama farklı seviyelerde (sonraki konu Filter'ı ayrıntılı ele alır):

InterceptorFilter (Servlet)
SeviyeSpring MVC (DispatcherServlet sonrası)Servlet konteyneri (daha önce/dışta)
ErişimHandler (controller metodu) bilgisine erişirYalnızca ham request/response
KullanımMVC'ye özgü: yetki, loglama, model eklemeGenel: CORS, sıkıştırma, kimlik, kodlama
Spring beanDoğal (Spring yönetir)Bean olabilir ama servlet seviyesinde

Genel kural: Spring MVC bağlamına (handler, model) ihtiyaç varsa interceptor; tüm isteklerde ham seviyede genel bir iş varsa filter.

Tipik kullanım alanları

  • İstek/yanıt loglama ve süre ölçme (gözlemlenebilirlik).
  • Kimlik/yetki kontrolü (preHandle'da false ile erken kesme).
  • İstek sayacı / hız sınırlama (rate limiting).
  • Ortak model/başlık ekleme, dil (locale) belirleme.

Özet

Interceptor'ın Spring MVC seviyesinde isteğin önüne/arkasına girdiğini; üç kancasını (preHandle ile erken kesme, postHandle, afterCompletion) ve WebMvcConfigurer ile yol-bazlı kaydı (Örnek 1); Filter ile farkını ve tipik kullanımları öğrendik. Sırada, daha düşük seviyede (servlet) araya giren yapı: Servlet Filter.

Kod Örnekleri(1)

Ornek1

ortam gerekir
Ornek1.java
1// Ornek1: HandlerInterceptor — controller'a girmeden önce/sonra araya girmek.
2// Çalıştırma: portal gömülü Tomcat ile başlatır, self-test çıktısını alır.
3package com.egitim.springboot.interceptor;
4
5import jakarta.servlet.http.HttpServletRequest;
6import jakarta.servlet.http.HttpServletResponse;
7import org.springframework.boot.CommandLineRunner;
8import org.springframework.boot.SpringApplication;
9import org.springframework.boot.autoconfigure.SpringBootApplication;
10import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
11import org.springframework.context.annotation.Bean;
12import org.springframework.web.bind.annotation.*;
13import org.springframework.web.client.RestClient;
14import org.springframework.web.servlet.HandlerInterceptor;
15import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
16import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
17
18@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
19public class Ornek1 {
20
21    public static void main(String[] args) { SpringApplication.run(Ornek1.class, args); }
22
23    // Interceptor: Spring MVC seviyesinde, controller metodu ÇAĞRILMADAN önce ve sonra çalışır.
24    static class ZamanlamaInterceptor implements HandlerInterceptor {
25        @Override
26        public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) {
27            req.setAttribute("baslangic", System.currentTimeMillis());
28            System.out.println("  [interceptor:preHandle] " + req.getMethod() + " " + req.getRequestURI());
29            // false dönerse istek controller'a HİÇ ulaşmaz (örn. yetki yoksa burada kesilir).
30            return true;
31        }
32        @Override
33        public void afterCompletion(HttpServletRequest req, HttpServletResponse res, Object handler, Exception ex) {
34            long sure = System.currentTimeMillis() - (long) req.getAttribute("baslangic");
35            System.out.println("  [interceptor:afterCompletion] durum=" + res.getStatus() + ", süre=" + sure + "ms");
36        }
37    }
38
39    // Interceptor'ı kaydet (hangi yollara uygulanacağını belirle).
40    @Bean
41    WebMvcConfigurer interceptorConfig() {
42        return new WebMvcConfigurer() {
43            @Override public void addInterceptors(InterceptorRegistry registry) {
44                registry.addInterceptor(new ZamanlamaInterceptor())
45                        .addPathPatterns("/api/**");   // yalnızca /api/** yollarına uygula
46            }
47        };
48    }
49
50    @RestController
51    @RequestMapping("/api")
52    static class DemoController {
53        @GetMapping("/selam") String selam() { return "merhaba"; }
54        @GetMapping("/veri") java.util.Map<String,Object> veri() { return java.util.Map.of("deger", 42); }
55    }
56
57    @Bean
58    CommandLineRunner selfTest() {
59        return args -> {
60            RestClient c = RestClient.create("http://localhost:8080");
61            System.out.println("\n========= INTERCEPTOR SELF-TEST =========");
62            System.out.println("İstek 1: GET /api/selam");
63            System.out.println("  yanıt -> " + c.get().uri("/api/selam").retrieve().body(String.class));
64            System.out.println("İstek 2: GET /api/veri");
65            System.out.println("  yanıt -> " + c.get().uri("/api/veri").retrieve().body(String.class));
66            System.out.println("(Her istekte interceptor pre/afterCompletion çalıştı.)");
67            System.out.println("=========================================");
68        };
69    }
70}
canlı çalıştırma için ortam gerekir

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.