[Spring Cloud] API Gateway Filter

jsieon97·2023년 3월 9일
0

API Gateway Filter란?

  • Request를 받으면 Mapping을 통해 Predicate에서 판단하고
  • 작업이 일어나기 전 사전 필터(Pre Filter) 통과
  • 맞는 서비스를 실행한다.
  • 작업 종료 이후 필터(Post Filter) 통과
  • 필터는 Property나 Java Code로 만들 수 있다.
  • Mapping을지나 Response된다.

Java로 필터 적용

application.yml파일(Property)의 cloud.gateway.routes설정을 주석처리 혹은 삭제한다.

#  cloud:
#    gateway:
#      routes:
#        - id: first-service
#          uri: http://localhost:8081/
#          predicates:
#            - Path=/first-service/**
#        - id: second-service
#          uri: http://localhost:8082/
#          predicates:
#            - Path=/second-service/**

Filter생성

// FilterConfig.java
// 위의 property설정과 동일한 javacode

@Configuration
public class FilterConfig {
    @Bean
    public RouteLocator gatewayRoutes(RouteLocatorBuilder builder) {
        return builder.routes()
                .route(r -> r.path("/first-service/**")
                            .filters(f -> f.addRequestHeader("first-request","first-request-header")
                                            .addResponseHeader("first-response","first-response-header"))
                            .uri("http://localhost:8081"))
                .route(r -> r.path("/second-service/**")
                        .filters(f -> f.addRequestHeader("second-request","second-request-header")
                                .addResponseHeader("second-response","second-response-header"))
                        .uri("http://localhost:8082"))
                .build();

    }

}

First Service에서 요청확인

@RestController
@RequestMapping("/first-service")
@Slf4j
public class FirstServiceController {

    ...
    
    // header값을 "first-request"로 설정했기에 그 값의 value를 저장
    @GetMapping("/message")
    public String message(@RequestHeader("first-request") String header) { 
        log.info(header);
        return "Hello world in First Service";
    }
}

Second Service에서 요청확인

@RestController
@RequestMapping("/second-service")
@Slf4j
public class SecondServiceController {

    ...

	// header값을 "second-request"로 설정했기에 그 값의 value를 저장
    @GetMapping("/message")
    public String message(@RequestHeader("second-request") String header) {
        log.info(header);
        return "Hello world in Second Service";
    }
}

결과 확인

log와 웹 페이지에서 제대로 표시된 것을 확인할 수 있다.

Property에서 필터

// application.yml

...

spring:
  application:
    name: apigateway-service
  cloud:
    gateway:
      routes:
        - id: first-service
          uri: http://localhost:8081/
          predicates:
            - Path=/first-service/**
          filters:
            - AddRequestHeader=first-request, first-request-header2
            - AddResponseHeader=first-response, first-response-header2
        - id: second-service
          uri: http://localhost:8082/
          predicates:
            - Path=/second-service/**
          filters:
            - AddRequestHeader=second-request, second-request-header2
            - AddResponseHeader=second-response, second-response-header2

filters 를 통해 필터를 걸 수 있다.

Postman을 이용한 결과 확인

200 OK 확인

header에서 response의 key, value값을 확인할 수 있다.

Custom Filter

사용자 정의 필터로 AbstractGatewayFilterFactory를 상속받아 사용할 수 있다.

// application.yml

...

spring:
  application:
    name: apigateway-service
  cloud:
    gateway:
      routes:
        - id: first-service
          uri: http://localhost:8081/
          predicates:
            - Path=/first-service/**
          filters:
#            - AddRequestHeader=first-request, first-request-header2
#            - AddResponseHeader=first-response, first-response-header2
            - CustomFilter
        - id: second-service
          uri: http://localhost:8082/
          predicates:
            - Path=/second-service/**
          filters:
#            - AddRequestHeader=second-request, second-request-header2
#            - AddResponseHeader=second-response, second-response-header2
            - CustomFilter

위에서 작성한 filter를 주석처리하고 CustomFilter를 넣는다.

JavaCode로 CustomFilter 생성

// CumtomFilter.java

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

@Component
@Slf4j
public class CustomFilter extends AbstractGatewayFilterFactory<CustomFilter.Config> {

    public CustomFilter() {
        super(Config.class);
    }

    @Override
    public GatewayFilter apply(Config config) {
        // Custom Pre Filter
        return (exchange, chain) -> {
            ServerHttpRequest request = exchange.getRequest();
            ServerHttpResponse response = exchange.getResponse();

            log.info("Custom PRE Filter: Request id -> {}", request.getId());

            // Custom Post Filter
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                log.info("Custom POST Filter: Request id -> {}", response.getStatusCode());
            }));
        };
    }

    public static class Config {
        // Put the configuration properties
    }
}
후에 이 부분에서 로그인 처리를 할 수 있다.

Postman 테스트



API Gateway를 통해 Request, Response되는 것을 확인할 수 있다.

Global Filter

Service별로 Filter 적용하는게 아닌 전체를 일괄 적용하는 Filter

// GlobalFilter.java

@Component
@Slf4j
public class GlobalFilter extends AbstractGatewayFilterFactory<GlobalFilter.Config> {

    public GlobalFilter() {
        super(Config.class);
    }

    @Override
    public GatewayFilter apply(Config config) {
        // Custom Pre Filter
        return (exchange, chain) -> {
            ServerHttpRequest request = exchange.getRequest();
            ServerHttpResponse response = exchange.getResponse();

            log.info("Global Filter baseMessage: {}", config.getBaseMessaqge());

            if(config.isPreLogger()) {
                log.info("Global Filter Start: request id -> {}", request.getId());
            }

            // Custom Post Filter
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                if(config.isPostLogger()) {
                    log.info("Global Filter End: response id -> {}", response.getStatusCode());
                }
            }));
        };
    }

    @Data
    public static class Config {
        // Put the configuration properties
        private String baseMessaqge;
        private boolean preLogger;
        private boolean postLogger;
    }
}![](https://velog.velcdn.com/images/jungse97/post/f7936680-bc2c-4f95-a426-82cc8acb425a/image.PNG)
// application.yml

...

spring:
  application:
    name: apigateway-service
  cloud:
    gateway:
      default-filters:
        - name: GlobalFilter
          args:
            baseMessage: Spring Cloud Gateway Global Filter
            preLogger: true
            postLogger: true
            
      ...

Postman 테스트

200 OK


Global Filter start > Custom Pre Filter > Custom Post Filter > Global Filter End

Global Filter 종료되기 전에 Custom Filter 로직이 적용되고 Global Filter 종료

Logging Filter

// LoggingFilter.java

@Component
@Slf4j
public class LoggingFilter extends AbstractGatewayFilterFactory<LoggingFilter.Config> {
    public LoggingFilter() {
        super(Config.class);
    }

    @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());
            }

            //Custom Post Filter
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                if (config.isPostLogger()) {
                    log.info("Logging POST Filter End: response code -> {} ", response.getStatusCode());
                }
            }));
        }, Ordered.LOWEST_PRECEDENCE); // 우선순위 지정할 수 있다
        //HIGHEST_PRECEDENCE

        return filter;
    }

    @Data
    public static class Config {
        private String baseMessage;
        private boolean preLogger;
        private boolean postLogger;
    }

}
// application.yml

...

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:8081/
          predicates:
            - Path=/first-service/**
          filters:
#            - AddRequestHeader=first-request, first-request-header2
#            - AddResponseHeader=first-response, first-response-header2
            - CustomFilter
        - id: second-service
          uri: http://localhost:8082/
          predicates:
            - Path=/second-service/**
          filters:
#            - AddRequestHeader=second-request, second-request-header2
#            - AddResponseHeader=second-response, second-response-header2
            - name: CustomFilter
            - name: LoggingFilter
              args:
                baseMessage: Hi. there.
                preLogger: true
                postLogger: true

LOWEST_PRECEDENCE적용 시 Global > Custom > Logging
HIGHEST_PRECEDENCE적용 시 Logging > Global > Custom

profile
개발자로써 성장하는 방법

0개의 댓글