본문 바로가기
Spring/Spring Cloud

Spring Cloud Gateway - Filter

by 개복이 2022. 12. 25.

Spring Cloud Gateway - Filter

 

[FilterConfig]

  • 이전에 application.yml에 설정하였던 정보를 아래와 같이 Java로 설정할 수 있다.
@Configuration
public class FilterConfig {
    
    @Bean
    public RouteLocator gatewayRoutes(RouteLocatorBuilder builder, AuthorizationHeaderFilter myfilter) {
        return builder.routes()
            .route(r -> r.path("/first-service/**")
        .filters(f -> f.addRequestHeader("first-request", "first-request-header-by-java")
        .addResponseHeader("first-response", "first-response-header-by-java")
            .filter(myfilter.apply(new AuthorizationHeaderFilter . Config ()))
        )
        .uri("http://localhost:8081"))
        .route(r -> r.path("/second-service/**")
        .filters(f -> f.addRequestHeader("second-request", "second-request-header-by-java")
        .addResponseHeader("second-response", "second-response-header-by-java"))
        .uri("http://localhost:8082"))
        .build();
    }
}

 

  • Request : Gateway Client -> Gateway Handler -> GlobalFilter -> CustomFilter -> LoggingFilter ->
    Proxied Service
  • Response : Proxied Service -> LoggingFilter -> CustomFilter -> GlobalFilter -> Gateway Handler -> Gateway Client
  • ## 위에서의 Request, Response는 현재 작성한 프로젝트 기준으로 작성한 내용입니다. ##
  • 모든 필터는 AbstractGatewayFilterFactory를 상속 받아 apply method 구현
  • Config라는 static class를 작성하여 args 매개변수를 전달 받아 사용할 수 있습니다.

 

[GlobalFilter]

  • default-filters에 GlobalFilter을 기술하여 모든 인스턴스에 공통적으로 Filter를 적용
@Component
@Slf4j
public class GlobalFilter extends AbstractGatewayFilterFactory<GlobalFilter.Config> {
    public GlobalFilter(){
        super(Config.class);
    }
    
    @Data
    public static class Config {
        // Put the configuration properties
        private String baseMessage;
        private boolean preLogger;
        private boolean postLogger;
    }
    
    @Override
    public GatewayFilter apply(Config config){
        // Global PRE filter
        return (exchange, chain) -> {
            ServerHttpRequest request = exchange.getRequest();
            ServerHttpResponse response -> exchange.getResponse();
            
            log.info("Global Filter baseMessage : {}", config.getBaseMessage());
            if(config.isPreLogger()){
                log.info("Global Filter Start Request id -> {}", request.getId());
            }
            
            // Global POST filter
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                if(config.isPostLogger()){
                    log.info("Global Filter End : response code -> {}", response.getStatusCodde());
                }
            }));
        };
    }
}

 

[CustomFilter]

  • 각각의 route에 CustomFilter을 기술하여 별도의 Filter를 적용
@Component
@Slf4j
public class CustomFilter extends AbstractGatewayFilterFactory<CustomFilter.Config>{
    public CustomFilter(){
        super(Config.class);
    }
    
    public static class Config {
        // Put the configuration properties
    }
    
    @Override
    public GatewayFilter apply(Config config){
        // Custom PRE filter
        return (exchange, chain) -> {
            // 기존의 HttpServletRequest가 아닌 비동기 방식인 ServerHttpRequest 사용
            ServerHttpRequest request = exchange.request();
            ServerHttpResponse response = exchange.response();
            
            log.info("Custom PRE filter : request id -> {}", request.getId());
            
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                log.info("Custom POST filter : response code -> {}", response.getStatusCode());
            }));
        }
    }
}

 

 

[LoggingFilter]

  • 다른 Filter와 다르게 OrderedGatewayFilter를 사용함으로 Filter 호출의 순서를 변경
    • Ordered.HIGHEST_PRECEDENCE -> 필터 순서를 제일 먼저 적용
    • Ordered.LOWEST_PRECEDENCE -> 필터 순서를 제일 마지막에 적용
@Component
@Slf4j
public class LoggingFilter extends AbstractGatewayFilterFactory<LoggingFilter.Config> {
    public LoggingFilter(){
        super(Config.class);
    }
    
    @Data
    public static class Config {
        // Put the configuration properties
        private String baseMessage;
        private boolean preLogger;
        private boolean postLogger;
    }
    
    @Override
    public GatewayFilter apply(Config config){
        GatewayFilter filter = new OrderedGatewayFilter((exchange, chain) -> {
            ServerHttpRequest request = exchange.getRequest();
            ServerHttpResponse response = exchange.getResponse();
            
            log.info("Logging Filter baseMessage : {}", config.getBaseMessage());
            if(config.isPreLogger()){
                log.info("Logging PRE Filter Start : request id -> {}", request.getId());
            }
            
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                if(config.isPostLogger()){
                    log.info("Logging POST filter End : response code -> {}", response.getStatusCode());
                }
            }));
        }, Ordered.HIGHEST_PRECEDENCE);
        
        return filter;
    }
    
    
}

 

 

[application.yml]

server:
  port: 8000
  
eureka:
  client:
    fetch-registry: true
    register-with-eureka: true
    service-url:
      defaultZone: http://localhost:8761/eureka
      
spring:
  application:
    name: apigateway-service
  cloud:
    gateway:
      default-filters:
        - name: GlobalFilter
          args:
            baseMessage: Spring Cloud Gateway Global Filter
            preLogger: true
            postLogger: true
      routes:
        - id: first-service
          uri: http://localhost:8001
          predicates:
            - Path=/first-service/**
          filters:
            - name: CustomFilter
            - name: LoggingFilter
              args:
                baseMessage: Spring Cloud Gateway Logging Filter
                preLogger: true
                postLogger: true

 

[Global, Custom, Logging Filter 순서 확인]

  • 앞서 언급한 LoggingFilter에서 Ordered.HIGHEST_PRECEDENCE이기 때문에 제일 먼저 실행됩니다.
  • 다음에 GlobalFilter -> CustomFilter 순서로 호출됩니다.
  • LoggingFilter -> GlobalFilter -> CustomFilter

 

 

그렇다면 LoggingFilter를 Ordered.LOWEST_PRECEDENCE로 변경하고 호출하게 된다면??

  • GlobalFilter -> CustomFilter -> LoggingFilter 순서로 호출될 것입니다.

'Spring > Spring Cloud' 카테고리의 다른 글

Spring Cloud Gateway  (0) 2022.12.19
Spring Cloud DiscoveryClient  (0) 2022.08.13
Spring Cloud Netflix Eureka  (0) 2022.08.07

댓글