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:
-
Boshqa mijozlar
ClientHttpConnector
orqali ulanishi mumkin.
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 moslashtirilganUriBuilderFactory
. -
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 uchunConsumer
. -
filter
: Har bir so'rov uchun mijoz filtri. -
exchangeStrategies
: HTTP-xabarlarni o‘qish/yozish strategiyalari. -
clientConnector
: Mijozning HTTP kutubxonasi sozlamalari.
Masalan
WebClient client = WebClient.builder()
.codecs(configurer -> ... )
.build();
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:
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
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:
WebClient webClient = WebClient.builder()
.codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(2 * 1024 * 1024))
.build();
val webClient = WebClient.builder()
.codecs { configurer -> configurer.defaultCodecs().maxInMemorySize(2 * 1024 * 1024) }
.build()
Reactor Netty
Reactor Netty sozlamalarini moslashtirish uchun oldindan sozlangan HttpClient
ni taqdim eting:
HttpClient httpClient = HttpClient.create().secure(sslSpec -> ...);
WebClient webClient = WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();
val httpClient = HttpClient.create().secure { ... }
val webClient = WebClient.builder()
.clientConnector(ReactorClientHttpConnector(httpClient))
.build()
Resurslar
Standart holda, HttpClient
reactor.netty.http.HttpResources
da 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:
@Bean
public ReactorResourceFactory reactorResourceFactory() {
return new ReactorResourceFactory();
}
@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:
@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();
}
- Global resurslardan mustaqil resurslarni yaratamiz.
- Resurslar fabrikasi bilan
ReactorClientHttpConnector
konstruktoridan foydalanamiz. - Konnektorni
WebClient.Builder
ga ulaymiz.
@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()
}
- Global resurslardan mustaqil resurslarni yaratamiz.
- Resurslar fabrikasi bilan
ReactorClientHttpConnector
konstruktoridan foydalanamiz. - Konnektorni
WebClient.Builder
ga ulaymiz.
Kutish vaqti
Ulanish kutish vaqtini sozlash:
import io.netty.channel.ChannelOption;
HttpClient httpClient = HttpClient.create()
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000);
WebClient webClient = WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();
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:
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...
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:
HttpClient httpClient = HttpClient.create()
.responseTimeout(Duration.ofSeconds(2));
// WebClient yaratamiz...
val httpClient = HttpClient.create()
.responseTimeout(Duration.ofSeconds(2));
// WebClient yaratamiz...
Quyidagi misolda Jetty'dan HttpClient
sozlamalarini qanday sozlash ko‘rsatilgan:
WebClient.create().get()
.uri("https://example.org/path")
.httpRequest(httpRequest -> {
HttpClientRequest reactorRequest = httpRequest.getNativeRequest();
reactorRequest.responseTimeout(Duration.ofSeconds(2));
})
.retrieve()
.bodyToMono(String.class);
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:
HttpClient httpClient = new HttpClient();
httpClient.setCookieStore(...);
WebClient webClient = WebClient.builder()
.clientConnector(new JettyClientHttpConnector(httpClient))
.build();
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 ApplicationContext
dan Spring ni to‘xtatishda resursdan foydalanishni yakunlashni ta'minlash uchun, JettyResourceFactory
turidagi Spring boshqariladigan binni quyidagi misolda ko'rsatilganidek e'lon qilishi mumkin:
@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();
}
- Resurslar fabrikasi bilan
JettyClientHttpConnector
konstruktoridan foydalanamiz. - Konnektorni
WebClient.Builder
ga ulaymiz.
@Bean
fun resourceFactory() = JettyResourceFactory()
@Bean
fun webClient(): WebClient {
val httpClient = HttpClient()
// Keyingi sozlamalar...
val connector = JettyClientHttpConnector(httpClient, resourceFactory())
return WebClient.builder().clientConnector(connector).build()
}
- Resurslar fabrikasi bilan
JettyClientHttpConnector
konstruktoridan foydalanamiz. - Konnektorni
WebClient.Builder
ga ulaymiz.
HttpComponents
Quyidagi misolda Apache HttpComponents dan HttpClient
sozlamalarini qanday sozlash ko'rsatilgan:
HttpAsyncClientBuilder clientBuilder = HttpAsyncClients.custom();
clientBuilder.setDefaultRequestConfig(...);
CloseableHttpAsyncClient client = clientBuilder.build();
ClientHttpConnector connector = new HttpComponentsClientHttpConnector(client);
WebClient webClient = WebClient.builder().clientConnector(connector).build();
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:
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);
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:
WebClient client = WebClient.create("https://example.org");
Mono<Person> result = client.get()
.uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(Person.class);
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:
Flux<Quote> result = client.get()
.uri("/quotes").accept(MediaType.TEXT_EVENT_STREAM)
.retrieve()
.bodyToFlux(Quote.class);
val result = client.get()
.uri("/quotes").accept(MediaType.TEXT_EVENT_STREAM)
.retrieve()
.bodyToFlow<Quote>()
Standart holda, 4xx yoki 5xx javoblari WebClientResponseException
ni keltirib chiqaradi, muayyan HTTP holat kodlari uchun subklasslarni qo‘shing. Xato xabarlarini qayta ishlashni moslashtirish uchun, onStatus
ishlatib, quyidagicha foydalaning:
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);
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:
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);
}
});
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:
Mono<Person> personMono = ... ;
Mono<Void> result = client.post()
.uri("/persons/{id}", id)
.contentType(MediaType.APPLICATION_JSON)
.body(personMono, Person.class)
.retrieve()
.bodyToMono(Void.class);
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:
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);
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:
Person person = ... ;
Mono<Void> result = client.post()
.uri("/persons/{id}", id)
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(person)
.retrieve()
.bodyToMono(Void.class);
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:
MultiValueMap<String, String> formData = ... ;
Mono<Void> result = client.post()
.uri("/path", id)
.bodyValue(formData)
.retrieve()
.bodyToMono(Void.class);
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:
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);
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:
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();
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-Type
ni 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 MediaType
ni aniq tarzda ko'rsatish mumkin part
yig‘uvchi usulining bir nechta ortiqcha yuklangan usullaridan biri orqali.
MultiValueMap
ni tayyorlagandan so‘ng, uni WebClient
ga body
usuli orqali o‘tkazish eng oson usuldir, quyidagi misolda ko‘rsatilganidek:
MultipartBodyBuilder builder = ...;
Mono<Void> result = client.post()
.uri("/path", id)
.body(builder.build())
.retrieve()
.bodyToMono(Void.class);
val builder: MultipartBodyBuilder = ...
client.post()
.uri("/path", id)
.body(builder.build())
.retrieve()
.awaitBody<Unit>()
Agar MultiValueMap
da 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-Type
ni multipart/form-data
ga 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:
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);
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:
WebClient client = WebClient.builder()
.filter((request, next) -> {
ClientRequest filtered = ClientRequest.from(request)
.header("foo", "bar")
.build();
return next.exchange(filtered);
})
.build();
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:
import static org.springframework.web.reactive.function.client.ExchangeFilterFunctions.basicAuthentication;
WebClient client = WebClient.builder()
.filter(basicAuthentication("user", "password"))
.build();
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:
import static org.springframework.web.reactive.function.client.ExchangeFilterFunctions.basicAuthentication;
WebClient client = webClient.mutate()
.filters(filterList -> {
filterList.add(0, basicAuthentication("user", "password"));
})
.build();
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 WebClient
ga 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:
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);
}
});
}
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 {
GO TO FULL VERSION