매일 매일, 차곡 차곡 쌓기



완벽하지 않은 것을 두려워 말며,
완성도를 높히는데 집중하자.

Spring/WebClient

[WebClient] Filter

blockbuddy93 2024. 3. 28. 14:20

WebClient.Builder를 통해 클라이언트 필터(ExchangeFilterFunction)를 등록하여 요청을 가로채고 수정할 수 있습니다. 아래 예제에서 볼 수 있습니다.

WebClient client = WebClient.builder()
        .filter((request, next) -> {

            ClientRequest filtered = ClientRequest.from(request)
                    .header("foo", "bar")
                    .build();

            return next.exchange(filtered);
        })
        .build();

 

이는 인증과 같은 cross-cutting concerns? 에 사용할 수 있습니다. 아래 예제는 정적 팩토리 메서드를 통해 기본 인증을 위한 필터를 사용하는 방법을 보여줍니다.

import static org.springframework.web.reactive.function.client.ExchangeFilterFunctions.basicAuthentication;

WebClient client = WebClient.builder()
        .filter(basicAuthentication("user", "password"))
        .build();

 

필터는 기존 WebClient 인스턴스를 변형하여 추가하거나 제거할 수 있으며, 이는 원본 WebClient에 영향을 주지 않는 새로운 WebClient 인스턴스를 생성합니다. 예를 들어:

import static org.springframework.web.reactive.function.client.ExchangeFilterFunctions.basicAuthentication;

WebClient client = webClient.mutate()
        .filters(filterList -> {
            filterList.add(0, basicAuthentication("user", "password"));
        })
        .build();

 

 

WebClient는 ExchangeFunction을 따라가는 필터 체인 주위의 얇은 퍼사드입니다. 요청을 수행하고, 상위 수준 객체로부터 인코딩 및 디코딩을 수행하며, 응답 콘텐츠가 항상 소비되도록 보장합니다. 필터가 응답을 처리하는 경우, 응답 콘텐츠가 항상 소비되거나, 그렇지 않으면 웹클라이언트로 다운스트림으로 전파되어야 합니다.

 

아래는 UNAUTHORIZED 상태 코드를 처리하지만, 응답 콘텐츠를 항상 소비하거나 다운스트림으로 전파하여 동일한 응답을 보장하는 필터입니다.

public ExchangeFilterFunction renewTokenFilter() {
    return (request, next) -> next.exchange(request).flatMap(response -> {
        if (response.statusCode().value() == HttpStatus.UNAUTHORIZED.value()) {
            return response.releaseBody()
                    .then(renewToken())
                    .flatMap(token -> {
                        ClientRequest newRequest = ClientRequest.from(request).build();
                        return next.exchange(newRequest);
                    });
        } else {
            return Mono.just(response);
        }
    });
}

 

아래 예제는 ExchangeFilterFunction 인터페이스를 사용하여, PUT 및 POST 멀티파트/form-data 요청에 대한 Content-Length 헤더를 계산하는 데 도움이 되는 사용자 정의 필터 클래스를 만드는 방법을 보여줍니다.

public class MultipartExchangeFilterFunction implements ExchangeFilterFunction {

    @Override
    public Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) {
        if (MediaType.MULTIPART_FORM_DATA.includes(request.headers().getContentType())
                && (request.method() == HttpMethod.PUT || request.method() == HttpMethod.POST)) {
            return next.exchange(ClientRequest.from(request).body((outputMessage, context) ->
                request.body().insert(new BufferingDecorator(outputMessage), context)).build()
            );
        } else {
            return next.exchange(request);
        }
    }

    private static final class BufferingDecorator extends ClientHttpRequestDecorator {

        private BufferingDecorator(ClientHttpRequest delegate) {
            super(delegate);
        }

        @Override
        public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
            return DataBufferUtils.join(body).flatMap(buffer -> {
                getHeaders().setContentLength(buffer.readableByteCount());
                return super.writeWith(Mono.just(buffer));
            });
        }
    }
}

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

[WebClient] Context  (0) 2024.03.28
[WebClient] Attributes  (0) 2024.03.28
[WebClient] RequestBody  (0) 2024.03.28
[WebClient] Exchange  (0) 2024.03.28
[WebClient] Retrieve  (0) 2024.03.28