0
0
Lập trình
Thaycacac
Thaycacac thaycacac

Làm chủ RestTemplate trong Spring Boot

Đăng vào 2 tháng trước

• 8 phút đọc

Giới thiệu

Khi xây dựng các ứng dụng microservices hoặc ứng dụng dựa trên REST, việc thực hiện các cuộc gọi HTTP từ dịch vụ này sang dịch vụ khác là rất phổ biến. Trong Spring Boot, phương pháp truyền thống và phổ biến cho việc này (trước khi WebClient ra đời) là RestTemplate. Nó cung cấp một cách đơn giản, đồng bộ để thực hiện các yêu cầu HTTP như GET, POST, PUT, DELETE và nhiều hơn nữa. Trong bài viết này, chúng ta sẽ khám phá các ví dụ thực tiễn về cách sử dụng RestTemplate, bao gồm việc gửi tiêu đề tùy chỉnh, xử lý phản hồi và thực hiện các thao tác CRUD giữa các dịch vụ như Dịch vụ Khách Hàng và Dịch vụ Tài Khoản.

RestTemplate là gì?

RestTemplate là một client đồng bộ được cung cấp bởi Spring Framework để thực hiện các yêu cầu HTTP tới các dịch vụ web RESTful. Nó xử lý giao tiếp HTTP và phụ thuộc vào java.net.HttpURLConnection, đơn giản hóa việc tương tác với các dịch vụ bên ngoài. Nó cung cấp sự tiện lợi, linh hoạt và tích hợp liền mạch cho nhiều hoạt động HTTP. Hãy nghĩ về nó như một client Java cho phép ứng dụng Spring Boot của bạn dễ dàng tiêu thụ các REST API.

Tính năng chính của RestTemplate

1. Cuộc gọi HTTP đồng bộ

RestTemplate thực hiện các yêu cầu HTTP một cách đồng bộ, có nghĩa là luồng sẽ chờ phản hồi trước khi tiếp tục. Điều này rất đơn giản và phổ biến cho giao tiếp giữa các dịch vụ.

2. Hỗ trợ nhiều phương thức HTTP

RestTemplate hỗ trợ các phương thức GET, POST, PUT, PATCH, DELETE và OPTIONS, giúp dễ dàng thực hiện các thao tác CRUD đầy đủ.

3. Xử lý phản hồi linh hoạt

Bạn có thể lấy chỉ thân phản hồi (getForObject()), truy cập đầy đủ phản hồi với trạng thái và tiêu đề (getForEntity()), hoặc sử dụng exchange() để kiểm soát hoàn toàn các yêu cầu và phản hồi.

4. Hỗ trợ mẫu URI

Sử dụng các dấu chấm hỏi ({id}) trong URL để chèn các giá trị động.

5. Xử lý nội dung yêu cầu

Gửi các đối tượng Java dưới dạng JSON hoặc XML và tự động ánh xạ phản hồi về các đối tượng Java.

6. Tiêu đề tùy chỉnh

Thêm các tiêu đề bằng cách sử dụng HttpEntity và HttpHeaders cho xác thực, theo dõi hoặc siêu dữ liệu bổ sung.

7. Xử lý ngoại lệ

RestTemplate ném ra các ngoại lệ cho các lỗi HTTP (4xx, 5xx) mà bạn có thể bắt hoặc tùy chỉnh với ResponseErrorHandler.

8. Bộ lọc và nhà máy yêu cầu

Sử dụng ClientHttpRequestInterceptor cho việc ghi log hoặc xác thực. Thay thế client HTTP bên dưới thông qua ClientHttpRequestFactory cho các cài đặt về thời gian chờ, pooling, hoặc cấu hình nâng cao.

Cài đặt RestTemplate trong Spring Boot

Thêm phụ thuộc

Nếu bạn đang sử dụng Spring Boot Starter Web, RestTemplate đã được bao gồm. Nếu không, hãy thêm đoạn mã sau vào pom.xml của bạn:

xml Copy
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

Định nghĩa RestTemplate như một Bean

java Copy
@Configuration
public class RestTemplateConfig {
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

Tiêm RestTemplate

java Copy
@Service
public class AccountService {
    private RestTemplate restTemplate;

    public AccountService(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }
}

Các phương thức phổ biến của RestTemplate

1. Các thao tác GET

- getForObject():

Lấy một tài nguyên dưới dạng một đối tượng.

Dịch vụ:

java Copy
public CustomerDto getAccountWithCustomer(Long customerId) {
    String CUSTOMER_SERVICE_URL = "http://localhost:8081/customers";
    return restTemplate.getForObject(CUSTOMER_SERVICE_URL + "/{id}", CustomerDto.class, customerId);
}

Controller:

java Copy
@GetMapping("/{id}")
public CustomerDto getAccountWithCustomer(@PathVariable Long id) {
    return accountService.getAccountWithCustomer(id);
}

cURL:

bash Copy
curl --location 'http://localhost:8082/accounts/5' --data ''

- getForEntity():

Lấy một tài nguyên với trạng thái và tiêu đề HTTP.

Dịch vụ:

java Copy
public CustomerDto getCustomerWithGetForEntity(Long customerId) {
    String CUSTOMER_SERVICE_URL = "http://localhost:8081/customers";
    ResponseEntity<CustomerDto> response = restTemplate.getForEntity(CUSTOMER_SERVICE_URL + "/{id}", CustomerDto.class, customerId);
    System.out.println("Status Code: " + response.getStatusCode());
    System.out.println("Headers: " + response.getHeaders());
    return response.getBody();
}

Controller:

java Copy
@GetMapping("/cust/{id}")
public CustomerDto getCustomerWithGetForEntity(@PathVariable Long id) {
    return accountService.getCustomerWithGetForEntity(id);
}

cURL:

bash Copy
curl --location 'http://localhost:8082/accounts/cust/5'

- exchange():

Phương thức linh hoạt cho bất kỳ phương thức HTTP nào với tiêu đề, nội dung và trạng thái.

Dịch vụ:

java Copy
public CustomerDto getCustomerWithExchange(Long customerId) {
    HttpHeaders headers = new HttpHeaders();
    headers.set("Custom-Header", "customer");
    HttpEntity<Void> entity = new HttpEntity<>(headers);

    ResponseEntity<CustomerDto> response = restTemplate.exchange(CUSTOMER_SERVICE_URL + "/{id}", HttpMethod.GET, entity, CustomerDto.class, customerId);
    System.out.println("Status Code: " + response.getStatusCode());
    System.out.println("Headers: " + response.getHeaders());
    return response.getBody();
}

Controller:

java Copy
@GetMapping("/exchange/{id}")
public CustomerDto getCustomerWithExchange(@PathVariable Long id) {
    return accountService.getCustomerWithExchange(id);
}

cURL:

bash Copy
curl --location 'http://localhost:8082/accounts/exchange/2' --header 'Custom-Header: customer' --data ''

2. Các thao tác POST

- postForObject():

Gửi một yêu cầu POST và lấy thân phản hồi.

Dịch vụ:

java Copy
public CustomerDto createCustomerWithPostForObject(CustomerDto customer) {
    String CUSTOMER_SERVICE_URL = "http://localhost:8081/customers/create";
    return restTemplate.postForObject(CUSTOMER_SERVICE_URL, customer, CustomerDto.class);
}

Controller:

java Copy
@PostMapping("/cust")
public CustomerDto createCustomerWithPostForObject(@RequestBody CustomerDto customer) {
    return accountService.createCustomerWithPostForObject(customer);
}

cURL:

bash Copy
curl --location 'http://localhost:8082/accounts/cust' --header 'Content-Type: application/json' --data-raw '{"firstName": "Lara", "lastName": "Jean", "email": "lara@example.com", "mobileNo": "09873455672"}'

- postForEntity():

Gửi một yêu cầu POST và trả về ResponseEntity bao gồm trạng thái, tiêu đề và thân.

Dịch vụ:

java Copy
public CustomerDto createCustomerWithPostForEntity(CustomerDto customer) {
    String CUSTOMER_SERVICE_URL = "http://localhost:8081/customers/create";
    ResponseEntity<CustomerDto> response = restTemplate.postForEntity(CUSTOMER_SERVICE_URL, customer, CustomerDto.class);
    System.out.println("Status Code: " + response.getStatusCode());
    return response.getBody();
}

Controller:

java Copy
@PostMapping("/createCustomer")
public CustomerDto createCustomerWithPostForEntity(@RequestBody CustomerDto customer) {
    return accountService.createCustomerWithPostForEntity(customer);
}

cURL:

bash Copy
curl --location 'http://localhost:8082/accounts/customer' --header 'Content-Type: application/json' --data-raw '{ "firstName": "Martin", "lastName": "Wood", "email": "wood@example.com", "mobileNo": "9876655443" }'

3. Thao tác PUT

- put():

Cập nhật một tài nguyên hiện có.

Dịch vụ:

java Copy
public String updateCustomer(CustomerDto updatedCustomer) {
    String CUSTOMER_SERVICE_URL = "http://localhost:8081/customers/update";
    restTemplate.put(CUSTOMER_SERVICE_URL, updatedCustomer);
    return "Customer Updated Successfully...!";
}

Controller:

java Copy
@PutMapping("/customer")
public void updateCustomer(@RequestBody CustomerDto customer) {
    accountService.updateCustomer(customer);
}

cURL:

bash Copy
curl --location --request PUT 'http://localhost:8082/accounts/customer' --header 'Content-Type: application/json' --data-raw '{ "customerId": "2", "firstName": "Sam", "lastName": "Smith", "email": "sam@gmail.com", "mobileNo": "9876556651" }'

4. Thao tác DELETE

- delete():

Xóa một tài nguyên.

Dịch vụ:

java Copy
public String deleteCustomer(Long customerId) {
    String CUSTOMER_SERVICE_URL = "http://localhost:8081/customers/delete";
    restTemplate.delete(CUSTOMER_SERVICE_URL + "/{id}", customerId);
    return "Customer Deleted Successfully...!";
}

Controller:

java Copy
@DeleteMapping("/customer/{id}")
public String deleteCustomer(@PathVariable Long id) {
    return accountService.deleteCustomer(id);
}

cURL:

bash Copy
curl --location --request DELETE 'http://localhost:8082/accounts/customer/11' --data ''

Xử lý ngoại lệ với RestTemplate

Khi gọi các API REST bên ngoài bằng RestTemplate, mọi thứ không phải lúc nào cũng diễn ra như mong đợi. Các vấn đề mạng, dịch vụ không khả dụng, hoặc lỗi máy chủ có thể xảy ra, dẫn đến các ngoại lệ. Xử lý ngoại lệ đúng cách đảm bảo rằng ứng dụng của bạn có thể xử lý lỗi một cách thanh lịch, cung cấp thông điệp có ý nghĩa, và ngăn ngừa sự cố. Trong Spring Boot, bạn có thể xử lý ngoại lệ cục bộ trong các phương thức dịch vụ hoặc toàn cục bằng cách sử dụng @ControllerAdvice và @ExceptionHandler.

Các ngoại lệ thường gặp

  • HttpClientErrorException: Lỗi HTTP 4xx (ví dụ: 404 Not Found, 400 Bad Request)
  • HttpServerErrorException: Lỗi HTTP 5xx (ví dụ: 500 Internal Server Error)
  • ResourceAccessException: Lỗi mạng, thời gian chờ, hoặc không thể truy cập host
  • RestClientException: Lỗi tổng quát nếu không có trường hợp nào ở trên khớp

Xử lý ngoại lệ cục bộ

Bọc các cuộc gọi RestTemplate của bạn trong một khối try-catch:

java Copy
public CustomerDto getCustomerById(Long customerId) {
    String CUSTOMER_SERVICE_URL = "http://localhost:8081/customers/{id}";
    try {
        return restTemplate.getForObject(CUSTOMER_SERVICE_URL, CustomerDto.class, customerId);
    } catch (HttpClientErrorException.NotFound e) {
        throw new RuntimeException("Customer not found with ID: " + customerId);
    } catch (HttpClientErrorException e) {
        throw new RuntimeException("Client Error: " + e.getStatusCode());
    } catch (HttpServerErrorException e) {
        throw new RuntimeException("Server Error: " + e.getStatusCode());
    } catch (ResourceAccessException e) {
        throw new RuntimeException("Service unreachable: " + e.getMessage());
    }
}

Xử lý ngoại lệ toàn cục với @ControllerAdvice

Bạn cũng có thể xử lý ngoại lệ toàn cục trong ứng dụng Spring Boot của mình.

java Copy
@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(HttpClientErrorException.NotFound.class)
    public ResponseEntity<String> handleNotFound(HttpClientErrorException.NotFound ex) {
        return new ResponseEntity<>("Resource not found: " + ex.getMessage(), HttpStatus.NOT_FOUND);
    }

    @ExceptionHandler(HttpClientErrorException.class)
    public ResponseEntity<String> handleClientError(HttpClientErrorException ex) {
        return new ResponseEntity<>("Client Error: " + ex.getStatusCode(), HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler(HttpServerErrorException.class)
    public ResponseEntity<String> handleServerError(HttpServerErrorException ex) {
        return new ResponseEntity<>("Server Error: " + ex.getStatusCode(), HttpStatus.INTERNAL_SERVER_ERROR);
    }

    @ExceptionHandler(ResourceAccessException.class)
    public ResponseEntity<String> handleResourceAccess(ResourceAccessException ex) {
        return new ResponseEntity<>("Service unreachable: " + ex.getMessage(), HttpStatus.SERVICE_UNAVAILABLE);
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity<String> handleGenericException(Exception ex) {
        return new ResponseEntity<>("Error: " + ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

Cách hoạt động:

  • Khi AccountService gọi CustomerService thông qua RestTemplate, nó có thể ném ra các ngoại lệ như HttpClientErrorException hoặc ResourceAccessException.
  • Spring tự động phát hiện những ngoại lệ này trong cùng một ngữ cảnh ứng dụng (AccountService) và chuyển hướng chúng đến GlobalExceptionHandler của bạn.
  • Bạn không cần phải viết try-catch trong mỗi phương thức, trừ khi bạn muốn xử lý cục bộ tùy chỉnh.

Lưu ý về việc Ngừng hỗ trợ

RestTemplate đã được nhóm Spring chính thức ngừng hỗ trợ và không còn được phát triển tích cực. Giải pháp thay thế được khuyến nghị là WebClient, cung cấp giao tiếp HTTP không đồng bộ, phản ứng và có thể mở rộng hơn.

Tuy nhiên, RestTemplate vẫn được sử dụng rộng rãi trong các dự án thực tế và thường thấy trong các mã nguồn sản xuất. Việc hiểu RestTemplate là rất quan trọng nếu bạn đang bảo trì các ứng dụng hiện có hoặc làm việc trong các môi trường chưa hoàn toàn chuyển sang WebClient.

Kết luận

Chúng ta đã khám phá RestTemplate một cách sâu sắc — từ các tính năng và phương thức linh hoạt đến các ví dụ CRUD thực tiễn, xử lý các tiêu đề tùy chỉnh và quản lý ngoại lệ. RestTemplate đã lâu là công cụ ưa thích cho giao tiếp HTTP đồng bộ trong Spring Boot, giúp dễ dàng kết nối các dịch vụ trong kiến trúc microservice.

Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào