앞의 기본적으로 라우팅과 필터를 간단하게 등록하여 사용해보았는데
이제는 Custom Filter를 등록해보자
Spring Cloud - Custom Filter를 사용한다면 요청이 들어오기 전에 작업할 것과 요청 후에 작업할 내용을 설정할 수 있다. 대표적으로는 로그인 인증, 로깅 등의 작업들을 처리할 수 있다.
filter 패키지 생성 후 CustomFilter.java 클래스를 만들어서 테스트 해보겠습니다.
CustomFilter.java
package com.example.apigatewayservice.filter;
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: response code -> {}", response.getStatusCode());
}));
};
}
public static class Config {
// Put the configuration properties
}
}
first-service, second-service의 프로젝트에 방금 추가한 filter 테스트용의 api 추가
FirstServiceController.java
package com.example.firstservice;
import lombok.extern.log4j.Log4j;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
// http://localhost:8081/welcome
// http://localhost:8081/first-service/welcome
@RestController
@RequestMapping("/first-service")
@Slf4j
public class FirstServiceController {
@GetMapping("/welcome")
public String welcome() {
return "Welcome to the First Service";
}
@GetMapping("/message")
public String message(@RequestHeader("first-request") String header) {
log.info(header);
return "Hello World in First Service.";
}
@GetMapping("/check")
public String check() {
return "Hi, there, This is a message from First Service.";
}
}
SecondServiceController.java
package com.example.secondservice;
import lombok.extern.log4j.Log4j;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
// http://localhost:8082/welcome
// http://localhost:8082/second-service/welcome
@RestController
@RequestMapping("/second-service")
@Slf4j
public class SecondServiceController {
@GetMapping("/welcome")
public String welcome() {
return "Welcome to the Second Service";
}
@GetMapping("/message")
public String message(@RequestHeader("second-request") String header) {
log.info(header);
return "Hello World in Second Service.";
}
@GetMapping("/check")
public String check() {
return "Hi, there, This is a message from Second Service.";
}
}
check API 테스트!
http://localhost:8000/first-service/check
로그 맨 아래 부분에 Custom PRE FIlter, Custom POST filter가 출력된 것을 확인할 수 있습니다.
Spring Cloud - Global Filter 설정
Global Filter는 이전 Custom Filter와 다르게 yml 파일에 각 서비스별로 추가 설정하는 것이아니라
공통적으로 처리하는 부분이기 때문에 한번에 설정한다.
filter 패키지에 GlobalFilter.java 를 생성하자
package com.example.apigatewayservice.filter;
import lombok.Data;
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 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.getBaseMessage());
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 code -> {}", response.getStatusCode());
}
}));
};
}
@Data
public static class Config {
// Put the configuration properties
private String baseMessage;
private boolean preLogger;
private boolean postLogger;
}
}
위의 Config에 들어올 baseMessage, preLogger, postLogger등을 전달할 매개변수를 application.yml에 전달하고 설정해보자
application.yml
spring.cloud.gateway.default-filters를 설정하면된다.
server:
port: 8000
eureka:
client:
register-with-eureka: false
fetch-registry: false
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: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
check API 테스트!
http://localhost:8000/first-service/check
이전과 달리 Custom Filter 전과 후에 Global Filter가 실행된 것을 확인 할 수 있다.
이제 Global Filter 처럼 Custom Filter에 매개변수를 사용하여 LoggingFilter를 생성해보자
LoggingFilter.java
package com.example.apigatewayservice.filter;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.OrderedGatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.core.Ordered;
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 LoggingFilter extends AbstractGatewayFilterFactory<LoggingFilter.Config> {
public LoggingFilter() {
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
// 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());
// }
//
// // Custom Post Filter
// return chain.filter(exchange).then(Mono.fromRunnable(() -> {
// if (config.isPostLogger()) {
// log.info("Global Filter End: response code -> {}", response.getStatusCode());
// }
// }));
// };
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: 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.HIGHEST_PRECEDENCE);
return filter;
}
@Data
public static class Config {
// Put the configuration properties
private String baseMessage;
private boolean preLogger;
private boolean postLogger;
}
}
application.yml > custom으로 등록
필터를 여러개 등록 시 name을 부여해야된다.
server:
port: 8000
eureka:
client:
register-with-eureka: false
fetch-registry: false
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: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
서버 재실행 및 테스트!
second-service에만 등록했기때문에 second check api로 테스트
http://localhost:8000/second-service/check
이전과 달리 GlobalFilter -> Custom Filter -> Logging Filter -> CustomFilter -> GlobalFilter 가 아니라
LoggingFilter -> GlobalFilter -> CustomFilter -> GlobalFilter -> LoggingFilter 인것을 알 수 있습니다.
이 순서는 Filter에서 설정할 때 우선순위를 아래설정값 처럼 변경하여 사용할 수 있습니다.
Ordered.LOWEST_PRECEDENCE
로 설정한다면
GlobalFilter -> Custom Filter -> Logging Filter -> CustomFilter -> GlobalFilter
이전 처럼 진행될 수 있습니다.
SpringBoot MSA (9) - Spring Cloud Gateway / Load Balancer (0) | 2023.10.10 |
---|---|
SpringBoot MSA (8) - Spring Cloud Gateway 와 Eureka 연동 (0) | 2023.10.10 |
SpringBoot MSA (6) - API Gateway Service Filter (1) | 2023.10.09 |
SpringBoot MSA (5) - API Gateway Service (0) | 2023.10.03 |
SpringBoot MSA (4) - UserService / Load Balancer (0) | 2023.10.03 |
댓글 영역