반응형
문제
이번 과제에서 처리된 서비스의 서버 포트를 응답 헤더에 담으라는 요구사항이 있었다.
@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/products")
public class ProductController {
private final ProductService productService;
@Value("${server.port}")
private String port;
/**
* 상품 목록 조회 API
*/
@GetMapping
public ResponseEntity<List<GetProductsRes>> getProductList(GetProductsReq request, Pageable pageable) {
List<GetProductsRes> productList = productService.getProductList(request, pageable);
return ResponseEntity.ok()
.header(GlobalConstant.CUSTOM_SERVER_PORT_HEADER, port)
.body(productList);
}
나는 무식하게 일일이 컨트롤러에 이렇게 다 담았었는데, 제출 이후 팀원들과 이야기하다가 이 중복을 어떻게 해결했는지를 논의했었다.
한 팀원분께서 게이트웨이에 넣으셨다는 말씀을 해주셔서 바로 커비 해버렸다.
해결
@Component
public class AddServerPortInHeaderFilter implements GlobalFilter, Ordered {
private static final String SERVER_PORT_HEADERS_NAME = "Server-Port";
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
return chain.filter(exchange)
.then(Mono.fromRunnable(() -> addServerPortInHeader(exchange)));
}
private void addServerPortInHeader(ServerWebExchange exchange) {
URI uri = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);
if (uri != null) {
ServerHttpResponse response = exchange.getResponse();
HttpHeaders headers = response.getHeaders();
String port = String.valueOf(uri.getPort());
headers.add(SERVER_PORT_HEADERS_NAME, port);
}
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
}
URI uri = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);
스프링 클라우드 게이트웨이는 클라이언트의 요청이 게이트웨이에 도착한 후, 처리할 서비스로 요청을 보내는데, 이 과정에서 GATEWAY_REQUEST_URL_ATTR 속성으로 속성값을 가져오면 최종적으로 결정된 목적지 URI를 가져올 수 있다.
uri : http://<ip주소>:19093/products
로그로 가져와본 상품 조회 API URI이다.
ProductApplication인 19093 포트를 잘 가져오는 것을 볼 수 있다.
public final class ServerWebExchangeUtils {
private static final Log log = LogFactory.getLog(ServerWebExchangeUtils.class);
public static final String PRESERVE_HOST_HEADER_ATTRIBUTE = qualify("preserveHostHeader");
public static final String URI_TEMPLATE_VARIABLES_ATTRIBUTE = qualify("uriTemplateVariables");
public static final String CLIENT_RESPONSE_ATTR = qualify("gatewayClientResponse");
public static final String CLIENT_RESPONSE_CONN_ATTR = qualify("gatewayClientResponseConnection");
public static final String CLIENT_RESPONSE_HEADER_NAMES = qualify("gatewayClientResponseHeaderNames");
public static final String GATEWAY_ROUTE_ATTR = qualify("gatewayRoute");
public static final String GATEWAY_REACTOR_CONTEXT_ATTR = qualify("gatewayReactorContext");
public static final String GATEWAY_REQUEST_URL_ATTR = qualify("gatewayRequestUrl");
public static final String GATEWAY_ORIGINAL_REQUEST_URL_ATTR = qualify("gatewayOriginalRequestUrl");
public static final String GATEWAY_HANDLER_MAPPER_ATTR = qualify("gatewayHandlerMapper");
public static final String GATEWAY_SCHEME_PREFIX_ATTR = qualify("gatewaySchemePrefix");
public static final String GATEWAY_PREDICATE_ROUTE_ATTR = qualify("gatewayPredicateRouteAttr");
public static final String GATEWAY_PREDICATE_MATCHED_PATH_ATTR = qualify("gatewayPredicateMatchedPathAttr");
public static final String GATEWAY_PREDICATE_MATCHED_PATH_ROUTE_ID_ATTR = qualify("gatewayPredicateMatchedPathRouteIdAttr");
public static final String GATEWAY_PREDICATE_PATH_CONTAINER_ATTR = qualify("gatewayPredicatePathContainer");
public static final String WEIGHT_ATTR = qualify("routeWeight");
public static final String ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR = "original_response_content_type";
public static final String CIRCUITBREAKER_EXECUTION_EXCEPTION_ATTR = qualify("circuitBreakerExecutionException");
public static final String GATEWAY_ALREADY_ROUTED_ATTR = qualify("gatewayAlreadyRouted");
public static final String GATEWAY_ALREADY_PREFIXED_ATTR = qualify("gatewayAlreadyPrefixed");
public static final String CACHED_SERVER_HTTP_REQUEST_DECORATOR_ATTR = "cachedServerHttpRequestDecorator";
public static final String CACHED_REQUEST_BODY_ATTR = "cachedRequestBody";
public static final String GATEWAY_LOADBALANCER_RESPONSE_ATTR = qualify("gatewayLoadBalancerResponse");
public static final String GATEWAY_OBSERVATION_ATTR = qualify("gateway.observation");
private static final byte[] EMPTY_BYTES = new byte[0];
// ...
}
URI 말고도 다양한 속성값들을 가져올 수 있다.
그리고 팀원분은 AbstractGatewayFilterFactory 를 구현하셨는데, 이는 특정 라우트에만 적용되는 필터를 정의할 때 사용한다고 한다. 만약 전역적으로 적용되는 필터를 사용해야 한다면 GlobalFilter를 구현하면 된다.
반응형
'TIL ✍️' 카테고리의 다른 글
24/08/14(수) 99번째 TIL : Spring Security 없이 CORS 설정하기 (0) | 2024.08.19 |
---|---|
24/08/13(화) 98번째 TIL : Spring boot에서 record로 application.yml 읽기 (0) | 2024.08.13 |
24/08/09(금) 96번째 TIL : Spring Gateway 애플 실리콘 맥 에러 (0) | 2024.08.10 |
24/08/08(목) 95번째 TIL : Spring Data Redis 및 Redis-cli 문자열 인코딩 방식 (2) | 2024.08.09 |
24/08/07(수) 94번째 TIL : EurekaServerConfig 빈 중복 해결하기 (0) | 2024.08.07 |