REST client - Spring Boot application boshqa servis yoki tashqi API’ga HTTP so‘rov yuborishi uchun ishlatiladi.
Bu bo‘lim ichida quyidagilar bor: RestTemplate, WebClient, HttpInterface, timeout va retry configuration.
1. REST client nima?
Oldingi darslarda biz REST API yozdik:
Frontend / Mobile App → Spring Boot API → DatabaseLekin real loyihada backend faqat database bilan ishlamaydi. U boshqa servislar bilan ham gaplashadi:
Spring Boot API → Payment service
Spring Boot API → SMS service
Spring Boot API → Telegram bot API
Spring Boot API → CRM service
Spring Boot API → Currency exchange API
Spring Boot API → Delivery serviceMana shu tashqi servisga HTTP request yuboradigan qism REST client deyiladi.
2. Server va client farqi
Biz server bo‘lgan holat
Frontend bizga request yuboradi:
GET /api/productsBizning controller javob qaytaradi:
@GetMapping("/api/products")
public List<ProductResponse> getProducts() {
return productService.getProducts();
}Bu yerda biz servermiz.
Biz client bo‘lgan holat
Biz boshqa servisga request yuboramiz:
GET https://api.example.com/currency/USDBu yerda biz clientmiz.
Bizning Spring Boot app → tashqi API3. HTTP qisqa eslatma
REST client ishlatishdan oldin HTTP asoslarini bilish kerak.
HTTP methodlar
Method | Ma’nosi | Misol |
|---|---|---|
| Ma’lumot olish | Product list olish |
| Yangi ma’lumot yaratish | Order yaratish |
| To‘liq update | Product update |
| Qisman update | Order status update |
| O‘chirish | Product delete |
HTTP status code
Status | Ma’nosi |
|---|---|
| Muvaffaqiyatli |
| Yaratildi |
| Request noto‘g‘ri |
| Login kerak |
| Ruxsat yo‘q |
| Topilmadi |
| Conflict, masalan duplicate |
| Server xatosi |
| Servis vaqtincha ishlamayapti |
REST client yozganda status codelarni to‘g‘ri ushlash juda muhim.
4. Spring’da REST client variantlari
Spring’da bir nechta HTTP client bor:
RestTemplate
RestClient
WebClient
HttpInterfaceSpring Framework 6.1+ da RestClient ham bor, shuni ham bilib qo‘yish foydali.
Client | Turi | Qachon ishlatiladi |
|---|---|---|
| Synchronous / blocking | Legacy projectlarda ko‘p uchraydi |
| Synchronous / blocking | Modern Spring’da RestTemplate o‘rniga qulayroq |
| Reactive / non-blocking | Reactive, async, streaming, high-load |
| Declarative client | Interface yozib, boilerplate kamaytirish |
Spring rasmiy hujjatlarida RestTemplate synchronous HTTP client ekani aytilgan. WebClient esa non-blocking reactive client sifatida ta’riflanadi. (Home)
5. Blocking va non-blocking farqi
Blocking
Request yuboriladi va thread javob kelguncha kutadi.
Thread → request yubordi → javob kutdi → davom etdiRestTemplate va RestClient blocking client hisoblanadi.
Oddiy backend API’lar uchun tushunish oson.
Non-blocking
Request yuboriladi, lekin thread bekor kutib turmaydi.
Thread → request yubordi → boshqa ishga o‘tdi
Javob kelganda callback/reactive pipeline ishlaydiWebClient non-blocking reactive client. Rasmiy hujjatlarda WebClient request bajarish uchun HTTP client library kerak qilishi va Reactor Netty, JDK HttpClient, Jetty Reactive HttpClient kabi variantlarni qo‘llashi aytiladi. (Home)
6. Qaysi birini tanlash kerak?
Java developer uchun amaliy qoida:
Legacy project → RestTemplate ko‘rasiz
Oddiy modern blocking API → RestClient
Reactive/WebFlux project → WebClient
Boilerplate kamaytirish → HttpInterfaceAgar siz hozir Spring Boot MVC bilan oddiy REST API yozayotgan bo‘lsangiz:
RestTemplate’ni tushuning
RestClient/WebClient’ni ham bilib qo‘ying
HttpInterface bilan tanishing7. Example scenario
Tasavvur qilamiz, bizda Product API bor. U tashqi Currency APIdan kurs olib, product narxini USD’dan UZS’ga o‘tkazadi.
Tashqi API taxminan shunday response qaytaradi:
{
"base": "USD",
"target": "UZS",
"rate": 12650.50
}DTO:
import java.math.BigDecimal;
public record CurrencyRateResponse(
String base,
String target,
BigDecimal rate
) {
}8. RestTemplate
RestTemplate eski, lekin hali ko‘p loyihalarda uchraydi. Spring hujjatlarida u HTTP request bajaradigan synchronous client sifatida berilgan. (Home)
Dependency
Agar sizda spring-boot-starter-web bo‘lsa, odatda kerakli web client classlar mavjud bo‘ladi.
Bean yaratish
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RestClientConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}GET request
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Service
public class CurrencyClient {
private final RestTemplate restTemplate;
public CurrencyClient(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
public CurrencyRateResponse getUsdToUzsRate() {
String url = "https://api.example.com/currency/USD/UZS";
return restTemplate.getForObject(url, CurrencyRateResponse.class);
}
}Bu yerda:
getForObject → GET request yuboradi va response body’ni objectga aylantiradiResponseEntity bilan olish
Agar status, header ham kerak bo‘lsa:
import org.springframework.http.ResponseEntity;
public CurrencyRateResponse getUsdToUzsRate() {
String url = "https://api.example.com/currency/USD/UZS";
ResponseEntity<CurrencyRateResponse> response =
restTemplate.getForEntity(url, CurrencyRateResponse.class);
if (!response.getStatusCode().is2xxSuccessful()) {
throw new BadRequestException("Currency API xato qaytardi");
}
return response.getBody();
}POST request
Masalan SMS servisga request yuboramiz.
Request DTO:
public record SmsSendRequest(
String phone,
String message
) {
}Response DTO:
public record SmsSendResponse(
String id,
String status
) {
}Client:
public SmsSendResponse sendSms(String phone, String message) {
String url = "https://sms.example.com/api/send";
SmsSendRequest request = new SmsSendRequest(phone, message);
return restTemplate.postForObject(
url,
request,
SmsSendResponse.class
);
}9. exchange() method
exchange() ko‘proq nazorat beradi: method, headers, body, response type.
import org.springframework.http.*;
import org.springframework.stereotype.Service;
@Service
public class SmsClient {
private final RestTemplate restTemplate;
public SmsClient(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
public SmsSendResponse sendSms(String phone, String message) {
String url = "https://sms.example.com/api/send";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setBearerAuth("token-value");
SmsSendRequest body = new SmsSendRequest(phone, message);
HttpEntity<SmsSendRequest> entity = new HttpEntity<>(body, headers);
ResponseEntity<SmsSendResponse> response = restTemplate.exchange(
url,
HttpMethod.POST,
entity,
SmsSendResponse.class
);
return response.getBody();
}
}Bu yerda:
HttpHeaders → headerlar
HttpEntity → body + header
exchange → to‘liq HTTP request10. RestTemplate timeout
Timeout juda muhim. Timeout bo‘lmasa, tashqi API osilib qolsa, sizning servis ham kutib qoladi.
Minimal config:
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
import java.time.Duration;
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder
.connectTimeout(Duration.ofSeconds(2))
.readTimeout(Duration.ofSeconds(5))
.build();
}
}Connect timeout
Tashqi serverga ulanish uchun maksimal kutish vaqtiRead timeout
Ulanib bo‘lgandan keyin response kelishini kutish vaqtiAmaliy qoida:
Timeoutsiz HTTP client - production’da xavfli.11. RestTemplate xatolarini ushlash
RestTemplate 4xx/5xx statuslarda exception tashlashi mumkin.
Masalan:
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.client.ResourceAccessException;
public CurrencyRateResponse getUsdToUzsRate() {
try {
return restTemplate.getForObject(
"https://api.example.com/currency/USD/UZS",
CurrencyRateResponse.class
);
} catch (HttpClientErrorException ex) {
throw new BadRequestException("Currency API client xato: " + ex.getStatusCode());
} catch (HttpServerErrorException ex) {
throw new ExternalServiceException("Currency API server xato");
} catch (ResourceAccessException ex) {
throw new ExternalServiceException("Currency API timeout yoki network xato");
}
}Custom exception:
public class ExternalServiceException extends RuntimeException {
public ExternalServiceException(String message) {
super(message);
}
}12. WebClient
WebClient - modern, reactive HTTP client.
Rasmiy Javadoc’da WebClient non-blocking, reactive API ekanligi aytiladi. (Home)
Dependency
WebClient uchun odatda:
implementation 'org.springframework.boot:spring-boot-starter-webflux'Maven:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>MVC projectda ham WebClient ishlatish mumkin. Faqat reactive dependency qo‘shiladi.
WebClient bean
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.client.WebClient;
@Configuration
public class WebClientConfig {
@Bean
public WebClient currencyWebClient() {
return WebClient.builder()
.baseUrl("https://api.example.com")
.build();
}
}GET request
Reactive ko‘rinish:
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
@Service
public class CurrencyWebClient {
private final WebClient webClient;
public CurrencyWebClient(WebClient currencyWebClient) {
this.webClient = currencyWebClient;
}
public Mono<CurrencyRateResponse> getUsdToUzsRate() {
return webClient.get()
.uri("/currency/USD/UZS")
.retrieve()
.bodyToMono(CurrencyRateResponse.class);
}
}Bu Mono<CurrencyRateResponse> qaytaradi.
MVC projectda .block() ishlatish
Agar siz oddiy Spring MVC projectda bo‘lsangiz va service method oddiy object qaytarishi kerak bo‘lsa:
public CurrencyRateResponse getUsdToUzsRateBlocking() {
return webClient.get()
.uri("/currency/USD/UZS")
.retrieve()
.bodyToMono(CurrencyRateResponse.class)
.block();
}Lekin ehtiyot bo‘ling:
WebFlux reactive flow ichida block() ishlatish yomon.
MVC projectda tashqi API chaqirish uchun ba’zan ishlatiladi.13. WebClient error handling
import org.springframework.http.HttpStatusCode;
import reactor.core.publisher.Mono;
public Mono<CurrencyRateResponse> getUsdToUzsRate() {
return webClient.get()
.uri("/currency/USD/UZS")
.retrieve()
.onStatus(
HttpStatusCode::is4xxClientError,
response -> Mono.error(
new BadRequestException("Currency API client xato")
)
)
.onStatus(
HttpStatusCode::is5xxServerError,
response -> Mono.error(
new ExternalServiceException("Currency API server xato")
)
)
.bodyToMono(CurrencyRateResponse.class);
}14. WebClient timeout
Reactor Netty bilan timeout config:
import io.netty.channel.ChannelOption;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.netty.http.client.HttpClient;
import java.time.Duration;
@Configuration
public class WebClientConfig {
@Bean
public WebClient currencyWebClient() {
HttpClient httpClient = HttpClient.create()
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 2000)
.responseTimeout(Duration.ofSeconds(5));
return WebClient.builder()
.baseUrl("https://api.example.com")
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();
}
}Bu yerda:
CONNECT_TIMEOUT_MILLIS → ulanish timeout
responseTimeout → response kutish timeout15. WebClient POST request
public Mono<SmsSendResponse> sendSms(String phone, String message) {
SmsSendRequest request = new SmsSendRequest(phone, message);
return webClient.post()
.uri("/api/send")
.header("Authorization", "Bearer token-value")
.bodyValue(request)
.retrieve()
.bodyToMono(SmsSendResponse.class);
}Blocking variant:
public SmsSendResponse sendSmsBlocking(String phone, String message) {
return webClient.post()
.uri("/api/send")
.header("Authorization", "Bearer token-value")
.bodyValue(new SmsSendRequest(phone, message))
.retrieve()
.bodyToMono(SmsSendResponse.class)
.block();
}16. RestClient
Spring Framework 6.1'dan boshlab RestClient bor. Rasmiy hujjatda RestClient synchronous HTTP client bo‘lib, fluent API orqali request bajarishi aytilgan. (Home)
Bu mavzuda alohida ko‘rsatilmagan, lekin hozirgi Spring loyihalarda bilish foydali.
Bean
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestClient;
@Configuration
public class RestClientConfig {
@Bean
public RestClient currencyRestClient() {
return RestClient.builder()
.baseUrl("https://api.example.com")
.build();
}
}GET request
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClient;
@Service
public class CurrencyRestClient {
private final RestClient restClient;
public CurrencyRestClient(RestClient currencyRestClient) {
this.restClient = currencyRestClient;
}
public CurrencyRateResponse getUsdToUzsRate() {
return restClient.get()
.uri("/currency/USD/UZS")
.retrieve()
.body(CurrencyRateResponse.class);
}
}Bu RestTemplatega qaraganda ko‘proq fluent va o‘qilishi qulay.
17. HttpInterface
HttpInterface - declarative HTTP client. Ya’ni siz interface yozasiz, Spring implementation’ni proxy orqali yaratadi.
Rasmiy hujjatlarda HTTP Interface client’lar REST client ustida declarative HTTP service interface yaratish usuli sifatida berilgan. (Home)
Interface
import org.springframework.web.service.annotation.GetExchange;
public interface CurrencyApiClient {
@GetExchange("/currency/USD/UZS")
CurrencyRateResponse getUsdToUzsRate();
}Agar path variable kerak bo‘lsa:
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.service.annotation.GetExchange;
public interface CurrencyApiClient {
@GetExchange("/currency/{base}/{target}")
CurrencyRateResponse getRate(
@PathVariable String base,
@PathVariable String target
);
}HttpInterface config
Spring 6'da HttpServiceProxyFactory orqali proxy yaratiladi.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.support.RestClientAdapter;
import org.springframework.web.service.invoker.HttpServiceProxyFactory;
@Configuration
public class HttpInterfaceConfig {
@Bean
public RestClient currencyRestClient() {
return RestClient.builder()
.baseUrl("https://api.example.com")
.build();
}
@Bean
public CurrencyApiClient currencyApiClient(RestClient currencyRestClient) {
HttpServiceProxyFactory factory = HttpServiceProxyFactory
.builderFor(RestClientAdapter.create(currencyRestClient))
.build();
return factory.createClient(CurrencyApiClient.class);
}
}Service’da ishlatish:
@Service
public class CurrencyService {
private final CurrencyApiClient currencyApiClient;
public CurrencyService(CurrencyApiClient currencyApiClient) {
this.currencyApiClient = currencyApiClient;
}
public BigDecimal getUsdRate() {
CurrencyRateResponse response = currencyApiClient.getRate("USD", "UZS");
return response.rate();
}
}18. HttpInterface POST request
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.service.annotation.PostExchange;
public interface SmsApiClient {
@PostExchange("/api/send")
SmsSendResponse sendSms(@RequestBody SmsSendRequest request);
}Service:
@Service
public class SmsService {
private final SmsApiClient smsApiClient;
public SmsService(SmsApiClient smsApiClient) {
this.smsApiClient = smsApiClient;
}
public void sendOrderCreatedSms(String phone) {
SmsSendResponse response = smsApiClient.sendSms(
new SmsSendRequest(phone, "Order yaratildi")
);
if (!"SUCCESS".equals(response.status())) {
throw new ExternalServiceException("SMS yuborilmadi");
}
}
}19. Config bilan base URL berish
Hardcode yomon:
.baseUrl("https://api.example.com")Yaxshiroq:
application.yml:
external:
currency:
base-url: https://api.example.com
sms:
base-url: https://sms.example.com
token: ${SMS_TOKEN}Properties:
import jakarta.validation.constraints.NotBlank;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;
@Validated
@ConfigurationProperties(prefix = "external.currency")
public record CurrencyApiProperties(
@NotBlank
String baseUrl
) {
}Config:
@Configuration
@EnableConfigurationProperties(CurrencyApiProperties.class)
public class HttpClientConfig {
@Bean
public RestClient currencyRestClient(CurrencyApiProperties properties) {
return RestClient.builder()
.baseUrl(properties.baseUrl())
.build();
}
}Bu professionalroq:
URL kodda emas
local/dev/prod’da alohida config bo‘ladi
secret env orqali beriladi20. Authorization header qo‘shish
Ko‘p tashqi API token talab qiladi.
RestClient bilan
@Bean
public RestClient smsRestClient(SmsApiProperties properties) {
return RestClient.builder()
.baseUrl(properties.baseUrl())
.defaultHeader("Authorization", "Bearer " + properties.token())
.build();
}WebClient bilan
@Bean
public WebClient smsWebClient(SmsApiProperties properties) {
return WebClient.builder()
.baseUrl(properties.baseUrl())
.defaultHeader("Authorization", "Bearer " + properties.token())
.build();
}21. Timeout har doim bo‘lishi kerak
REST client yozishda eng muhim production qoidalardan biri:
Har bir external API call timeout bilan ishlasin.Nega?
Agar tashqi SMS servis javob bermasa:
Sizning thread kutib qoladi
Requestlar yig‘iladi
Connection pool to‘ladi
API sekinlashadi
Oxiri service down bo‘lishi mumkinTimeoutlar:
connect timeout
read/response timeoutDefault uchun oddiy qiymatlar:
connect timeout: 1-3 sekund
read timeout: 3-10 sekundLekin real qiymat biznesga bog‘liq.
22. Retry nima?
Retry - vaqtinchalik xato bo‘lsa requestni qayta urinish.
Masalan:
1-urinish → network timeout
2-urinish → 503
3-urinish → successSpring Retry loyihasi Spring applicationlarda declarative retry support berishini rasmiy repository ko‘rsatadi. (GitHub)
Retry qachon kerak?
Retry foydali:
Network glitch
503 Service Unavailable
504 Gateway Timeout
Temporary timeout
Rate limitdan keyin qisqa kutishRetry xavfli:
Payment charge qilish
Order yaratish
Pul yechish
Duplicate yaratadigan POST requestMuhim qoida:
Retry faqat idempotent yoki xavfsiz operationlarda ishlatilsin.23. Idempotent nima?
Idempotent operation - bir necha marta chaqirilsa ham natija buzilmaydi.
Odatda:
GET → idempotent
PUT → idempotent bo‘lishi mumkin
DELETE → odatda idempotent bo‘lishi mumkin
POST → ko‘pincha idempotent emasMasalan:
GET /currency/USD/UZSBuni retry qilish mumkin.
Lekin:
POST /payments/chargeretry qilinsa, mijozdan ikki marta pul yechilishi mumkin.
Bunday holatda idempotency key kerak bo‘ladi.
24. Spring Retry dependency
Gradle:
implementation 'org.springframework.retry:spring-retry'
implementation 'org.springframework.boot:spring-boot-starter-aop'Maven:
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>Enable:
import org.springframework.context.annotation.Configuration;
import org.springframework.retry.annotation.EnableRetry;
@Configuration
@EnableRetry
public class RetryConfig {
}25. @Retryable
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
@Service
public class CurrencyService {
private final CurrencyApiClient currencyApiClient;
public CurrencyService(CurrencyApiClient currencyApiClient) {
this.currencyApiClient = currencyApiClient;
}
@Retryable(
retryFor = ExternalServiceException.class,
maxAttempts = 3,
backoff = @Backoff(delay = 1000)
)
public CurrencyRateResponse getUsdToUzsRate() {
return currencyApiClient.getRate("USD", "UZS");
}
}Ma’nosi:
ExternalServiceException chiqsa retry qil
Maksimal 3 marta urinsin
Har urinish orasida 1 sekund kutadi@Recover
Agar retrylar ham muvaffaqiyatsiz bo‘lsa:
import org.springframework.retry.annotation.Recover;
@Recover
public CurrencyRateResponse recover(ExternalServiceException ex) {
throw new ExternalServiceException("Currency API vaqtincha ishlamayapti");
}Yoki fallback qiymat qaytarish mumkin, lekin ehtiyot bo‘ling:
@Recover
public CurrencyRateResponse recover(ExternalServiceException ex) {
return new CurrencyRateResponse(
"USD",
"UZS",
BigDecimal.valueOf(12600)
);
}Bu biznesga bog‘liq. Moliyaviy kurslarda eski qiymat qaytarish xavfli bo‘lishi mumkin.
26. Retry’ni haddan oshirmang
Yomon:
maxAttempts = 10
delay = 0Bu tashqi servisga yana ko‘proq bosim beradi.
Yaxshi:
maxAttempts = 2 yoki 3
delay = 500ms / 1s
exponential backoffRetry sababli umumiy request latency ham oshadi.
Masalan:
timeout 5s
retry 3 marta
jami kutish 15s+ bo‘lishi mumkinShuning uchun timeout va retry birga o‘ylanishi kerak.
27. External API response’ni ichki API response’dan ajrating
Tashqi API DTO’sini ichki response sifatida qaytarmang.
Yomon:
@GetMapping("/rate")
public CurrencyRateResponse getRate() {
return currencyClient.getUsdToUzsRate();
}Yaxshiroq:
public record ProductPriceResponse(
Long productId,
BigDecimal priceUsd,
BigDecimal priceUzs
) {
}Service:
public ProductPriceResponse getProductPriceInUzs(Long productId) {
Product product = productRepository.findById(productId)
.orElseThrow(() -> new NotFoundException("Product topilmadi"));
CurrencyRateResponse rate = currencyClient.getUsdToUzsRate();
BigDecimal priceUzs = product.getPriceUsd().multiply(rate.rate());
return new ProductPriceResponse(
product.getId(),
product.getPriceUsd(),
priceUzs
);
}Sabab:
External API o‘zgarsa, sizning public API buzilmasin.28. ExternalServiceException handler
Oldingi exception handling darsiga qo‘shamiz.
public class ExternalServiceException extends RuntimeException {
public ExternalServiceException(String message) {
super(message);
}
}Global handler:
@ExceptionHandler(ExternalServiceException.class)
public ResponseEntity<ErrorResponse> handleExternalService(
ExternalServiceException ex,
HttpServletRequest request
) {
ErrorResponse response = new ErrorResponse(
"EXTERNAL_SERVICE_ERROR",
ex.getMessage(),
HttpStatus.BAD_GATEWAY.value(),
request.getRequestURI(),
LocalDateTime.now(),
null
);
return ResponseEntity
.status(HttpStatus.BAD_GATEWAY)
.body(response);
}Nega 502 Bad Gateway?
Chunki sizning API ishlayapti, lekin upstream/tashqi service bilan muammo bor.
29. Logging
External call xatolarini log qilish kerak.
private static final Logger log =
LoggerFactory.getLogger(CurrencyClient.class);
public CurrencyRateResponse getUsdToUzsRate() {
try {
return restClient.get()
.uri("/currency/USD/UZS")
.retrieve()
.body(CurrencyRateResponse.class);
} catch (Exception ex) {
log.error("Currency API call failed", ex);
throw new ExternalServiceException("Currency API vaqtincha ishlamayapti");
}
}Lekin token, password, secretlarni log qilmang.
Yomon:
log.info("SMS token: {}", token);Mutlaqo qilinmaydi.
30. Observability uchun nima log qilish kerak?
Yaxshi log:
external service nomi
endpoint nomi
status code
duration
correlation id / trace idYomon log:
token
password
full card number
secret key
shaxsiy ma’lumotlar31. Product API bilan to‘liq misol
application.yml
external:
currency:
base-url: https://api.example.com
connect-timeout-ms: 2000
read-timeout-ms: 5000Properties
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;
@Validated
@ConfigurationProperties(prefix = "external.currency")
public record CurrencyApiProperties(
@NotBlank
String baseUrl,
@Min(100)
int connectTimeoutMs,
@Min(100)
int readTimeoutMs
) {
}RestClient config
@Configuration
@EnableConfigurationProperties(CurrencyApiProperties.class)
public class CurrencyClientConfig {
@Bean
public RestClient currencyRestClient(CurrencyApiProperties properties) {
return RestClient.builder()
.baseUrl(properties.baseUrl())
.build();
}
}Interface
public interface CurrencyClient {
CurrencyRateResponse getRate(String base, String target);
}Implementation
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestClient;
@Component
public class CurrencyRestClientImpl implements CurrencyClient {
private final RestClient restClient;
public CurrencyRestClientImpl(RestClient currencyRestClient) {
this.restClient = currencyRestClient;
}
@Override
public CurrencyRateResponse getRate(String base, String target) {
try {
return restClient.get()
.uri("/currency/{base}/{target}", base, target)
.retrieve()
.body(CurrencyRateResponse.class);
} catch (Exception ex) {
throw new ExternalServiceException("Currency API vaqtincha ishlamayapti");
}
}
}Product service
@Service
public class ProductPriceService {
private final ProductRepository productRepository;
private final CurrencyClient currencyClient;
public ProductPriceService(ProductRepository productRepository,
CurrencyClient currencyClient) {
this.productRepository = productRepository;
this.currencyClient = currencyClient;
}
@Transactional(readOnly = true)
public ProductPriceResponse getPriceInUzs(Long productId) {
Product product = productRepository.findById(productId)
.orElseThrow(() -> new NotFoundException("Product topilmadi"));
CurrencyRateResponse rate = currencyClient.getRate("USD", "UZS");
BigDecimal priceUzs = product.getPriceUsd().multiply(rate.rate());
return new ProductPriceResponse(
product.getId(),
product.getPriceUsd(),
priceUzs
);
}
}32. Testing: tashqi API’ni real chaqirmang
Unit testda haqiqiy SMS yoki currency API chaqirish yomon.
Nega?
Test sekinlashadi
Internet kerak bo‘ladi
External API limit tugashi mumkin
Pullik SMS ketib qolishi mumkin
Test beqaror bo‘ladiYaxshiroq:
CurrencyClient mock qilinadi
WireMock ishlatiladi
MockWebServer ishlatiladiBoshlanishiga kamida interface orqali ajratishni biling:
public interface CurrencyClient {
CurrencyRateResponse getRate(String base, String target);
}Testda fake implementation berish mumkin.
33. Ko‘p uchraydigan xatolar
Xato 1: Timeout qo‘ymaslik
Yomon:
new RestTemplate()Yaxshi:
builder
.connectTimeout(Duration.ofSeconds(2))
.readTimeout(Duration.ofSeconds(5))
.build()Xato 2: Tokenni hardcode qilish
Yomon:
.header("Authorization", "Bearer abc123")Yaxshi:
sms:
token: ${SMS_TOKEN}Xato 3: Har joyda client yaratish
Yomon:
public void send() {
RestTemplate restTemplate = new RestTemplate();
}Yaxshi:
@Bean
public RestTemplate restTemplate() {}Yoki:
@Bean
public RestClient restClient() {}Xato 4: External API DTO’sini domain modeli qilib ishlatish
Yomon:
ProductService external API response’ga to‘liq bog‘lanib qoladiYaxshi:
External DTO → Client layer
Internal DTO/domain → Service/API layerXato 5: Har bir xatoda retry qilish
Yomon:
400 Bad Request bo‘lsa ham retry
401 Unauthorized bo‘lsa ham retry
403 Forbidden bo‘lsa ham retryBular retry bilan tuzalmaydi.
Retry ko‘proq:
timeout
503
temporary network erroruchun.
Xato 6: Payment POST requestni retry qilish
Bu juda xavfli bo‘lishi mumkin.
1-request pul yechdi, lekin response kelmadi
retry yana pul yechdiBunday holatda idempotency key ishlatiladi.
34. Java developer uchun qisqa qoida
RestTemplate → legacy blocking client
RestClient → modern blocking client
WebClient → reactive/non-blocking client
HttpInterface → declarative client
Timeout → majburiy
Retry → faqat vaqtinchalik va xavfsiz xatolarda
ExternalServiceException → tashqi servis xatolari uchun
Config → base URL va token kodda emas35. Amaliy vazifa
Oldingi Product CRUD API loyihasiga external currency client qo‘shing.
Talablar
Config
external:
currency:
base-url: https://api.example.com
connect-timeout-ms: 2000
read-timeout-ms: 5000DTO
public record CurrencyRateResponse(
String base,
String target,
BigDecimal rate
) {
}public record ProductPriceResponse(
Long productId,
BigDecimal priceUsd,
BigDecimal priceUzs
) {
}Client
CurrencyClient interface
CurrencyRestClientImpl implementationService
ProductPriceService productni topadi
CurrencyClient orqali USD→UZS kurs oladi
priceUzs hisoblaydiException
ExternalServiceException
GlobalExceptionHandler → 502 Bad GatewayMuhim qoida
Currency API ishlamasa, clientga 500 emas, 502 qaytaring.
Timeout qo‘ying.
Base URL configdan olinsin.Xulosa
REST client & HTTP mavzusida Java developer quyidagilarni bilishi kerak:
HTTP methodlar va status codelar
RestTemplate
WebClient
RestClient
HttpInterface
GET / POST request yuborish
Header qo‘shish
Bearer token yuborish
Timeout config
Retry config
ExternalServiceException
502 Bad Gateway
External DTO va internal DTO ajratishEng muhim professional qoida:
Tashqi API chaqiruvi hech qachon timeout, error handling va configsiz yozilmaydi.