WebClient

All lectures for UZ purposes
Daraja , Dars
Mavjud

Spring WebFlux HTTP-so‘rovlarini bajarish uchun mijozni o‘z ichiga oladi. WebClient asinxron mantiqni oqimlar yoki paralellik bilan ishlashga ehtiyoj bo‘lmasdan deklarativ tarzda kompozitsiya qilish imkonini beruvchi Reactor asosidagi funksional, oqimli APIga ega. U to'liq bloklanmaydi, oqim uzatishni qo'llab-quvvatlaydi va server tomonidagi so'rovlar va javoblarning mazmunini kodlash va dekodlash uchun ishlatiladigan bir xil kodeklarga asoslangan.

WebClient HTTP-so'rovlarini bajarish uchun mijozning HTTP-kutubxonasini talab qiladi. Quyidagi uchun ichki qo‘llab-quvvatlash mavjud:

Konfiguratsiya

WebClient yaratishning eng oson yo'li bu statik fabrika metodlaridan birini ishlatishdir:

  • WebClient.create()

  • WebClient.create(String baseUrl)

Siz, shuningdek, qo'shimcha parametrlar bilan WebClient.builder() dan foydalanishingiz mumkin:

  • uriBuilderFactory: Bazaviy URL-manzil sifatida ishlatish uchun moslashtirilgan UriBuilderFactory.

  • defaultUriVariables: URI shablonlarini kengaytirishda foydalanish uchun standart qiymatlar.

  • defaultHeader: Har bir so'rov uchun sarlavhalar.

  • defaultCookie: Har bir so'rov uchun cookies.

  • defaultRequest: Har bir so'rovni sozlash uchun Consumer.

  • filter: Har bir so'rov uchun mijoz filtri.

  • exchangeStrategies: HTTP-xabarlarni o‘qish/yozish strategiyalari.

  • clientConnector: Mijozning HTTP kutubxonasi sozlamalari.

Masalan

Java
WebClient client = WebClient.builder()
.codecs(configurer -> ... )
.build();
Kotlin
val webClient = WebClient.builder()
.codecs { configurer -> ... }
.build()

WebClient yaratilgandan so‘ng o‘zgarmas xususiyatga ega bo‘ladi. Ammo quyidagi usulda uni klonlash va o‘zgartirilgan nusxasini yaratish mumkin:

Java
WebClient client1 = WebClient.builder()
.filter(filterA).filter(filterB).build();
WebClient client2 = client1.mutate()
.filter(filterC).filter(filterD).build();
// client1 filterA, filterB ga ega
// client2 filterA, filterB, filterC, filterD ga ega
Kotlin
val client1 = WebClient.builder()
.filter(filterA).filter(filterB).build()
val client2 = client1.mutate()
.filter(filterC).filter(filterD).build()
// client1 filterA, filterB ga ega
// client2 filterA, filterB, filterC, filterD ga ega

MaxInMemorySize

Kodeklar ma'lumotlarni xotirada buferlashni cheklaydilar, bu esa ilovaning xotira bilan bog‘liq muammolardan saqlanishiga yordam beradi. Standart holda, bu 256 Kbayt o‘rnatilgan. Agar bu yetarli bo‘lmasa, quyidagi xato olinadi:

org.springframework.core.io.buffer.DataBufferLimitException: Exceeded limit on max bytes to buffer

Kodeklar uchun standart cheklovni o'zgartirish uchun quyidagini bajaring:

Java
WebClient webClient = WebClient.builder()
.codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(2 * 1024 * 1024))
.build();
Kotlin
val webClient = WebClient.builder()
.codecs { configurer -> configurer.defaultCodecs().maxInMemorySize(2 * 1024 * 1024) }
.build()

Reactor Netty

Reactor Netty sozlamalarini moslashtirish uchun oldindan sozlangan HttpClientni taqdim eting:

Java
HttpClient httpClient = HttpClient.create().secure(sslSpec -> ...);
WebClient webClient = WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();
Kotlin
val httpClient = HttpClient.create().secure { ... }
val webClient = WebClient.builder()
.clientConnector(ReactorClientHttpConnector(httpClient))
.build()

Resurslar

Standart holda, HttpClient reactor.netty.http.HttpResourcesda saqlanadigan global Reactor Netty resurslarida ishtirok etadi, ular orasida voqealar tsikli oqimi va ulanish havzasi mavjud. Bu rejim tavsiya etiladi, chunki voqealar tsikli oqimi uchun parallellik maqsadida sobit, umumiy resurslardan foydalanish afzalroqdir. Bu rejimda global resurslar jarayon yakunlanmaguncha faol qoladi.

Agar server jarayon bilan sinxronlashtirilgan bo‘lsa, odatda aniq tugatishga ehtiyoj yo‘q. Ammo, agar server jarayon ichida ishga tushirilishi yoki to‘xtatilishi mumkin bo‘lsa (masalan, WAR-fayl shaklida joylashtirilgan Spring MVC ilovasi), ReactorResourceFactory turidagi Spring boshqariladigan binni globalResources=true parametri bilan e'lon qilish mumkin (standart), shuning uchun global Reactor Netty resurslaridan foydalanish Springning ApplicationContext ni yopishda tugatilishi kafolatlanadi, quyidagi misolda ko'rsatilgandek:

Java
@Bean
public ReactorResourceFactory reactorResourceFactory() {
return new ReactorResourceFactory();
}
Kotlin
@Bean
fun reactorResourceFactory() = ReactorResourceFactory()

Shuningdek, global Reactor Netty resurslaridan foydalanishni istisno qilish mumkin. Ammo, ushbu rejimda barcha Reactor Netty mijoz va server namunalari umumiy resurslardan foydalanishini ta'minlash mas'uliyati sizga yuklanadi, quyidagi misolda ko'rsatilgandek:

Java
@Bean
public ReactorResourceFactory resourceFactory() {
ReactorResourceFactory factory = new ReactorResourceFactory();
factory.setUseGlobalResources(false); 
return factory;
}
@Bean
public WebClient webClient() {
Function<HttpClient, HttpClient> mapper = client -> {
// Keyingi sozlamalar...
};
ClientHttpConnector connector =
    new ReactorClientHttpConnector(resourceFactory(), mapper); 
return WebClient.builder().clientConnector(connector).build(); 
}
  1. Global resurslardan mustaqil resurslarni yaratamiz.
  2. Resurslar fabrikasi bilan ReactorClientHttpConnector konstruktoridan foydalanamiz.
  3. Konnektorni WebClient.Builder ga ulaymiz.
Kotlin
@Bean
fun resourceFactory() = ReactorResourceFactory().apply {
isUseGlobalResources = false 
}
@Bean
fun webClient(): WebClient {
val mapper: (HttpClient) -> HttpClient = {
// Keyingi sozlamalar...
}
val connector = ReactorClientHttpConnector(resourceFactory(), mapper) 
return WebClient.builder().clientConnector(connector).build() 
}
  1. Global resurslardan mustaqil resurslarni yaratamiz.
  2. Resurslar fabrikasi bilan ReactorClientHttpConnector konstruktoridan foydalanamiz.
  3. Konnektorni WebClient.Builder ga ulaymiz.

Kutish vaqti

Ulanish kutish vaqtini sozlash:

Java
import io.netty.channel.ChannelOption;
HttpClient httpClient = HttpClient.create()
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000);
WebClient webClient = WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();
Kotlin
import io.netty.channel.ChannelOption
val httpClient = HttpClient.create()
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000);
val webClient = WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();

O'qish va yozish kutish vaqtini sozlash:

Java
import io.netty.handler.timeout.ReadTimeoutHandler;
import io.netty.handler.timeout.WriteTimeoutHandler;
HttpClient httpClient = HttpClient.create()
.doOnConnected(conn -> conn
        .addHandlerLast(new ReadTimeoutHandler(10))
        .addHandlerLast(new WriteTimeoutHandler(10)));
// WebClient yaratamiz...
Kotlin
import io.netty.handler.timeout.ReadTimeoutHandler
import io.netty.handler.timeout.WriteTimeoutHandler
val httpClient = HttpClient.create()
.doOnConnected { conn -> conn
        .addHandlerLast(new ReadTimeoutHandler(10))
        .addHandlerLast(new WriteTimeoutHandler(10))
}
// WebClient yaratamiz...

Aniq so'rov uchun javob kutish vaqtini sozlash:

Java
HttpClient httpClient = HttpClient.create()
.responseTimeout(Duration.ofSeconds(2));
// WebClient yaratamiz...
Kotlin
val httpClient = HttpClient.create()
.responseTimeout(Duration.ofSeconds(2));
// WebClient yaratamiz...

Quyidagi misolda Jetty'dan HttpClient sozlamalarini qanday sozlash ko‘rsatilgan:

Java
WebClient.create().get()
.uri("https://example.org/path")
.httpRequest(httpRequest -> {
    HttpClientRequest reactorRequest = httpRequest.getNativeRequest();
    reactorRequest.responseTimeout(Duration.ofSeconds(2));
})
.retrieve()
.bodyToMono(String.class);
Kotlin
WebClient.create().get()
.uri("https://example.org/path")
.httpRequest { httpRequest: ClientHttpRequest ->
    val reactorRequest = httpRequest.getNativeRequest<HttpClientRequest>()
    reactorRequest.responseTimeout(Duration.ofSeconds(2))
}
.retrieve()
.bodyToMono(String::class.java)

Jetty

Quyidagi misolda Jetty'dan HttpClient sozlamalarini qanday sozlash ko‘rsatilgan:

Java
HttpClient httpClient = new HttpClient();
httpClient.setCookieStore(...);
WebClient webClient = WebClient.builder()
.clientConnector(new JettyClientHttpConnector(httpClient))
.build();
Kotlin
val httpClient = HttpClient()
httpClient.cookieStore = ...
val webClient = WebClient.builder()
.clientConnector(new JettyClientHttpConnector(httpClient))
.build();

Standart holda, HttpClient o‘z resurslarini (Executor, ByteBufferPool, Scheduler) yaratadi, ular jarayon yakuniga yetgunga qadar yoki stop() funktsiyasi chaqirilgunga qadar faol bo‘lib qoladi.

Bir nechta Jetty mijoz (va server) namunalari o'rtasida resurslarni bo'lishish va ApplicationContextdan Spring ni to‘xtatishda resursdan foydalanishni yakunlashni ta'minlash uchun, JettyResourceFactory turidagi Spring boshqariladigan binni quyidagi misolda ko'rsatilganidek e'lon qilishi mumkin:

Java
@Bean
public JettyResourceFactory resourceFactory() {
return new JettyResourceFactory();
}
@Bean
public WebClient webClient() {
HttpClient httpClient = new HttpClient();
// Keyingi sozlamalar...
ClientHttpConnector connector =
    new JettyClientHttpConnector(httpClient, resourceFactory()); 
return WebClient.builder().clientConnector(connector).build(); 
}
  1. Resurslar fabrikasi bilan JettyClientHttpConnector konstruktoridan foydalanamiz.
  2. Konnektorni WebClient.Builderga ulaymiz.
Kotlin
@Bean
fun resourceFactory() = JettyResourceFactory()
@Bean
fun webClient(): WebClient {
val httpClient = HttpClient()
// Keyingi sozlamalar...
val connector = JettyClientHttpConnector(httpClient, resourceFactory()) 
return WebClient.builder().clientConnector(connector).build() 
}
  1. Resurslar fabrikasi bilan JettyClientHttpConnector konstruktoridan foydalanamiz.
  2. Konnektorni WebClient.Builderga ulaymiz.

HttpComponents

Quyidagi misolda Apache HttpComponents dan HttpClient sozlamalarini qanday sozlash ko'rsatilgan:

Java
HttpAsyncClientBuilder clientBuilder = HttpAsyncClients.custom();
clientBuilder.setDefaultRequestConfig(...);
CloseableHttpAsyncClient client = clientBuilder.build();
ClientHttpConnector connector = new HttpComponentsClientHttpConnector(client);
WebClient webClient = WebClient.builder().clientConnector(connector).build();
Kotlin
val client = HttpAsyncClients.custom().apply {
setDefaultRequestConfig(...)
}.build()
val connector = HttpComponentsClientHttpConnector(client)
val webClient = WebClient.builder().clientConnector(connector).build()

retrieve()

retrieve() methodini javobni olish usulini e'lon qilish uchun ishlatish mumkin. Masalan:

Java
WebClient client = WebClient.create("https://example.org");
Mono<ResponseEntity<Person>> result = client.get()
.uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
.retrieve()
.toEntity(Person.class);
Kotlin
val client = WebClient.create("https://example.org")
val result = client.get()
.uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
.retrieve()
.toEntity<Person>().awaitSingle()

Yoki faqat tanani olish:

Java
WebClient client = WebClient.create("https://example.org");
Mono<Person> result = client.get()
.uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(Person.class);
Kotlin
val client = WebClient.create("https://example.org")
val result = client.get()
.uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
.retrieve()
.awaitBody<Person>()

Dekodlangan obyektlar oqimini olish:

Java
Flux<Quote> result = client.get()
.uri("/quotes").accept(MediaType.TEXT_EVENT_STREAM)
.retrieve()
.bodyToFlux(Quote.class);
Kotlin
val result = client.get()
.uri("/quotes").accept(MediaType.TEXT_EVENT_STREAM)
.retrieve()
.bodyToFlow<Quote>()

Standart holda, 4xx yoki 5xx javoblari WebClientResponseExceptionni keltirib chiqaradi, muayyan HTTP holat kodlari uchun subklasslarni qo‘shing. Xato xabarlarini qayta ishlashni moslashtirish uchun, onStatus ishlatib, quyidagicha foydalaning:

Java
Mono<Person> result = client.get()
.uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
.retrieve()
.onStatus(HttpStatus::is4xxClientError, response -> ...)
.onStatus(HttpStatus::is5xxServerError, response -> ...)
.bodyToMono(Person.class);
Kotlin
val result = client.get()
.uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
.retrieve()
.onStatus(HttpStatus::is4xxClientError) { ... }
.onStatus(HttpStatus::is5xxServerError) { ... }
.awaitBody<Person>()

Exchange

exchangeToMono() va exchangeToFlux() (yoki Kotlin'da awaitExchange { } va exchangeToFlow { }) metodlari javob holatiga qarab javobni dekodlash talab qiladigan murakkab holatlar uchun foydalidir:

Java
Mono<Person> entityMono = client.get()
.uri("/persons/1")
.accept(MediaType.APPLICATION_JSON)
.exchangeToMono(response -> {
    if (response.statusCode().equals(HttpStatus.OK)) {
        return response.bodyToMono(Person.class);
    }
    else {
        // Xatoga murojaat qilamiz
        return response.createException().flatMap(Mono::error);
    }
});
Kotlin
val entity = client.get()
.uri("/persons/1")
.accept(MediaType.APPLICATION_JSON)
.awaitExchange {
if (response.statusCode() == HttpStatus.OK) {
     return response.awaitBody<Person>()
}
else {
     throw response.createExceptionAndAwait()
}
}

Yuqoridagi koddan foydalanilganda, qaytarilgan Mono yoki Flux ishi tugatgandan so'ng, javob tanasi tekshiriladi va agar foydalanilmasa, xotira va ulanishlarning yo'qligini oldini olish uchun bo'shatiladi. Shuning uchun javobni keyingi qatorlarda dekodlash mumkin emas. Berilgan vazifa kerak bo'lganda javobni qanday dekodlash kerakligini aniqlashi kerak.

So'rov Tanasi

So'rov tanasi misolida ko'rsatilgandek, ReactiveAdapterRegistry tomonidan ishlov berilgan har qanday asinxron turdan, masalan, Mono yoki Kotlin ning Deferred dan kodlanishi mumkin:

Java
Mono<Person> personMono = ... ;
Mono<Void> result = client.post()
.uri("/persons/{id}", id)
.contentType(MediaType.APPLICATION_JSON)
.body(personMono, Person.class)
.retrieve()
.bodyToMono(Void.class);
Kotlin
val personDeferred: Deferred<Person> = ...
client.post()
.uri("/persons/{id}", id)
.contentType(MediaType.APPLICATION_JSON)
.body<Person>(personDeferred)
.retrieve()
.awaitBody<Unit>()

Oqim obyektlarini kodlash ham mumkin, quyidagi misolda ko'rsatilgandek:

Java
Flux<Person> personFlux = ... ;
Mono<Void> result = client.post()
.uri("/persons/{id}", id)
.contentType(MediaType.APPLICATION_STREAM_JSON)
.body(personFlux, Person.class)
.retrieve()
.bodyToMono(Void.class);
Kotlin
val people: Flow<Person> = ...
client.post()
.uri("/persons/{id}", id)
.contentType(MediaType.APPLICATION_JSON)
.body(people)
.retrieve()
.awaitBody<Unit>()

Bundan tashqari, agar amalda bir qiymat mavjud bo'lsa, unda quyidagi misolda ko'rsatilganidek, bodyValue qisqartirilgan usuldan foydalanish mumkin:

Java
Person person = ... ;
Mono<Void> result = client.post()
.uri("/persons/{id}", id)
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(person)
.retrieve()
.bodyToMono(Void.class);
Kotlin
val person: Person = ...
client.post()
.uri("/persons/{id}", id)
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(person)
.retrieve()
.awaitBody<Unit>()

Forma ma'lumotlari

Forma ma'lumotlarini yuborish uchun MultiValueMap<String, String> ni tan sifatida ko'rsatish mumkin. E'tibor bering, mazmun avtomatik ravishda application/x-www-form-urlencoded ga o'rnatiladi FormHttpMessageWriter orqali. Quyidagi misolda MultiValueMap<String, String> dan qanday foydalanish ko'rsatilgan:

Java
MultiValueMap<String, String> formData = ... ;
Mono<Void> result = client.post()
.uri("/path", id)
.bodyValue(formData)
.retrieve()
.bodyToMono(Void.class);
Kotlin
val formData: MultiValueMap<String, String> = ...
client.post()
.uri("/path", id)
.bodyValue(formData)
.retrieve()
.awaitBody<Unit>()

Bundan tashqari, BodyInserters orqali ichki ravishda forma ma'lumotlarini qo'shish mumkin, quyidagi misolda ko'rsatilgandek:

Java
import static org.springframework.web.reactive.function.BodyInserters.*;
Mono<Void> result = client.post()
.uri("/path", id)
.body(fromFormData("k1", "v1").with("k2", "v2"))
.retrieve()
.bodyToMono(Void.class);
Kotlin
import org.springframework.web.reactive.function.BodyInserters.*
client.post()
.uri("/path", id)
.body(fromFormData("k1", "v1").with("k2", "v2"))
.retrieve()
.awaitBody<Unit>()

Ko‘p komponentli ma'lumotlar

Ko‘p komponentli ma'lumotlarni yuborish uchun MultiValueMap<String, ?>ni, qiymatlari komponent mazmunini ifodalovchi Object yoki komponent mazmunini va sarlavhalarini ifodalovchi HttpEntity bo‘lganini ko‘rsatish kerak. MultipartBodyBuilder ko'p komponentli so'rovni tayyorlash uchun qulay APIni taqdim etadi. Quyidagi misolda MultiValueMap<String, ?>ni qanday yaratish ko‘rsatilgan:

Java
MultipartBodyBuilder builder = new MultipartBodyBuilder();
builder.part("fieldPart", "fieldValue");
builder.part("filePart1", new FileSystemResource("...logo.png"));
builder.part("jsonPart", new Person("Jason"));
builder.part("myPart", part); // Server so'rovlaridan qismi
MultiValueMap<String, HttpEntity<?>> parts = builder.build();
Kotlin
val builder = MultipartBodyBuilder().apply {
part("fieldPart", "fieldValue")
part("filePart1", new FileSystemResource("...logo.png"))
part("jsonPart", new Person("Jason"))
part("myPart", part) // Server so'rovlaridan qismi
}
val parts = builder.build()

Ko‘p hollarda har bir komponent uchun Content-Typeni ko‘rsatishga hojat yo'q. Mazmun turi saralash uchun tanlangan HttpMessageWriter asosida avtomatik ravishda aniqlanadi yoki, Resource holatida, fayl kengaytmasi asosida. Zarur bo'lganda, har bir komponent uchun MediaTypeni aniq tarzda ko'rsatish mumkin part yig‘uvchi usulining bir nechta ortiqcha yuklangan usullaridan biri orqali.

MultiValueMapni tayyorlagandan so‘ng, uni WebClientga body usuli orqali o‘tkazish eng oson usuldir, quyidagi misolda ko‘rsatilganidek:

Java
MultipartBodyBuilder builder = ...;
Mono<Void> result = client.post()
.uri("/path", id)
.body(builder.build())
.retrieve()
.bodyToMono(Void.class);
Kotlin
val builder: MultipartBodyBuilder = ...
client.post()
.uri("/path", id)
.body(builder.build())
.retrieve()
.awaitBody<Unit>()

Agar MultiValueMapda har qanday String bo‘lmagan qiymat mavjud bo‘lsa, bu oddiy forma ma'lumotlarini (ya'ni application/x-www-form-urlencoded) ham ifodalay olishi mumkin, Content-Typeni multipart/form-dataga o‘rnatishga hojat yo‘q. Bu doimo HttpEntity o'rama funktsiyasini ta'minlaydigan MultipartBodyBuilder dan foydalanganda yuz beradi.

Muqobil tarzda, MultipartBodyBuilder dan foydalanmasdan, ichki uslubda ko'p komponentli mazmunni ichki BodyInserters yordamida taqdim etish mumkin, quyidagi misolda ko'rsatilgandek:

Java
import static org.springframework.web.reactive.function.BodyInserters.*;
Mono<Void> result = client.post()
.uri("/path", id)
.body(fromMultipartData("fieldPart", "value").with("filePart", resource))
.retrieve()
.bodyToMono(Void.class);
Kotlin
import org.springframework.web.reactive.function.BodyInserters.*
client.post()
.uri("/path", id)
.body(fromMultipartData("fieldPart", "value").with("filePart", resource))
.retrieve()
.awaitBody<Unit>()

Filtrlar

So'rovlarni ushlab turish va o'zgartirish uchun WebClient.Builder orqali mijoz filtr ExchangeFilterFunction) ro'yxatdan o'tkazish mumkin, quyidagi misolda ko'rsatilgandek:

Java
WebClient client = WebClient.builder()
.filter((request, next) -> {
    ClientRequest filtered = ClientRequest.from(request)
            .header("foo", "bar")
            .build();
    return next.exchange(filtered);
})
.build();
Kotlin
val client = WebClient.builder()
.filter { request, next ->
    val filtered = ClientRequest.from(request)
            .header("foo", "bar")
            .build()
    next.exchange(filtered)
}
.build()

Bu autentifikatsiya kabi skvoz funksionallik uchun ishlatilishi mumkin. Quyidagi misolda statik fabrika metodi orqali bazaviy autentifikatsiya uchun filtrdan foydalanilgan:

Java
import static org.springframework.web.reactive.function.client.ExchangeFilterFunctions.basicAuthentication;
WebClient client = WebClient.builder()
.filter(basicAuthentication("user", "password"))
.build();
Kotlin
import org.springframework.web.reactive.function.client.ExchangeFilterFunctions.basicAuthentication
val client = WebClient.builder()
.filter(basicAuthentication("user", "password"))
.build()

Filtrlarni o‘zgartirilgan WebClient nusxasini yaratish orqali qo‘shish yoki olib tashlash mumkin, bu esa asl nusxaga ta'sir qilmaydi. Masalan:

Java
import static org.springframework.web.reactive.function.client.ExchangeFilterFunctions.basicAuthentication;
WebClient client = webClient.mutate()
.filters(filterList -> {
    filterList.add(0, basicAuthentication("user", "password"));
})
.build();
Kotlin
val client = webClient.mutate()
.filters { it.add(0, basicAuthentication("user", "password")) }
.build()

WebClient - bu ExchangeFunction bilan birga keladigan filtrlar zanjiridan o‘tkazuvchi nozik interfeysdir. Bu so‘rovlarni bajarish jarayoni, yuqori darajadagi obyektlarga kodlash va qaytarish uchun yordam beradi, va javob mazmuni har doim iste'mol qilinishini kafolatlaydi. Agar filtrlar javobni qandaydir usulda qayta ishlasa, uning mazmuni doimo iste'mol qilinishi yoki boshqa yo‘llar bilan WebClientga tarqatilishi kerak, u esa xuddi shunday qilishni ta'minlaydi. Quyida UNAUTHORIZED holat kodini qayta ishlaydigan, lekin har qanday javob mazmuni, kutilgan bo‘lsa ham, berilishini kafolatlaydigan filtr keltirilgan:

Java
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);
}
});
}
Kotlin
fun renewTokenFilter(): ExchangeFilterFunction? {
return ExchangeFilterFunction { request: ClientRequest?, next: ExchangeFunction ->
next.exchange(request!!).flatMap { response: ClientResponse ->
    if (response.statusCode().value() == HttpStatus.UNAUTHORIZED.value()) {
        return@flatMap response.releaseBody()
                .then(renewToken())
                .flatMap { token: String? ->
                    val newRequest = ClientRequest.from(request).build()
                    next.exchange(newRequest)
                }
    } else {

Izohlar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION