+1

Làm chủ HTTP Client: Quy tắc "Vàng" khi kế thừa BasePendingRequest để xây dựng API Wrapper chuyên nghiệp

Trong quá trình phát triển các hệ thống microservices hoặc tích hợp dịch vụ thứ ba (như Telegram, Stripe, hay nội bộ Hasaki), chúng ta thường sử dụng HTTP Client. Tuy nhiên, thay vì viết code "vương vãi" khắp nơi, việc đóng gói logic vào một class kế thừa từ BasePendingRequest là một bước đi thông minh để hướng tới sự gọn gàng và chuyên nghiệp.

Vậy quy tắc của nhóm class này là gì? Tại sao chúng ta không dùng Http::get() trực tiếp mà phải "bày vẽ" thêm class? Hãy cùng mình tìm hiểu qua bài viết này nhé!

1. BasePendingRequest là gì?

Trong hệ sinh thái Laravel (từ phiên bản 10.x trở đi), PendingRequest là trái tim của HTTP Client. Khi bạn muốn tạo ra một "phiên bản tùy chỉnh" cho một service cụ thể, bạn thường tạo ra một class Base để quản lý các cấu hình chung (Base URL, Headers, Timeout...).

Việc mở rộng từ một BasePendingRequest giúp bạn tạo ra các API Wrapper có tính nhất quán cao.

2. Quy tắc 1: Luôn giữ vững tính "Fluent Interface"

Đặc sản của Laravel HTTP Client là khả năng nối chuỗi (chaining methods). Khi bạn viết thêm các method mới trong class kế thừa, hãy đảm bảo chúng luôn trả về $this.

Tại sao? Vì nó cho phép người dùng sử dụng class của bạn một cách mượt mà:

$myService->withToken($token)->withVersion('v2')->get('/user');

Quy tắc: Mọi phương thức tùy chỉnh cấu hình request đều phải kết thúc bằng return $this;.

3. Quy tắc 2: Đóng gói cấu hình mặc định (Encapsulation)

Thay vì bắt người dùng phải nhớ Base URL hay các Header đặc thù, hãy đưa chúng vào hàm __construct hoặc phương thức khởi tạo của class kế thừa.

abstract class BasePendingRequest extends PendingRequest 
{
    public function __construct(Factory $factory)
    {
        parent::__construct($factory);
        
        $this->baseUrl('https://api.example.com')
             ->timeout(5)
             ->asJson()
             ->acceptJson();
    }
}

Việc này giúp tránh việc lặp lại code (DRY - Don't Repeat Yourself) và đảm bảo mọi request gửi đi từ class này đều tuân thủ đúng chuẩn của Provider.

4. Quy tắc 3: Tách biệt Logic Xác thực (Authentication)

Đừng để logic lấy Token nằm lẫn lộn trong Controller. Hãy biến nó thành một quy tắc trong class của bạn. Bạn có thể tạo các method như withInternalAuth() hay withPartnerToken() để ẩn đi sự phức tạp bên trong.

public function withHmacAuth(string $secret): self
{
    return $this->withHeaders([
        'X-Signature' => $this->calculateSignature($secret),
    ]);
}

5. Quy tắc 4: Xử lý Response tập trung

Một sai lầm phổ biến là để mỗi nơi tự xử lý lỗi HTTP 400, 500. Quy tắc của một class BasePendingRequest tốt là nó có thể định nghĩa cách xử lý lỗi chung hoặc tự động log dữ liệu khi có sự cố.

Bạn có thể tận dụng phương thức send() để override hoặc sử dụng Middleware của Guzzle (nằm dưới lớp vỏ của PendingRequest) để xử lý toàn cục.

6. Ví dụ thực tế: Xây dựng HasakiServiceRequest

Hãy tưởng tượng bạn đang viết một service để kết nối các module nội bộ:

class HasakiServiceRequest extends BasePendingRequest
{
    public function forWarehouse(int $id): self
    {
        return $this->withHeaders(['X-Warehouse-Id' => $id]);
    }

    public function getInventory(string $sku)
    {
        return $this->get("/inventory/{$sku}")->throw()->json();
    }
}

Kết luận

Việc tuân thủ các quy tắc khi mở rộng BasePendingRequest không chỉ giúp code của bạn "ngầu" hơn mà còn giúp hệ thống dễ bảo trì, dễ test và giảm thiểu sai sót khi tích hợp API.

Hãy nhớ:

  1. Fluent: Luôn trả về $this.
  2. Encapsulate: Giấu kín các cấu hình thô.
  3. Clean: Tách biệt logic Auth và xử lý lỗi.

Hy vọng bài viết này giúp ích cho các bạn trong quá trình refactor code Backend. Nếu thấy hay, đừng quên Upvote và Clip bài viết lại nhé!

Cảm ơn các bạn đã đọc!

Tác giả: Nguyễn Huy Hoàng


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí