Tìm kiếm


    Refit - Thư viện thay thế tuyệt vời cho HttpClient trong C#


    Chắc hẳn các bạn đã từng sử dụng qua HttpClient hoặc RestSharp để thực hiện call tới một endpoint nào đó rồi đúng không?

    Mình thì hay sử dụng RestSharp nhưng có lẽ từ bây giờ mình sẽ không sử dụng HttpClient và RestSharp nữa vì mình đã tìm được giải pháp thay thế gọn nhẹ hơn nhiều. Đó là Refit

    Refit là gì?

    Thực ra nói thay thế HttpClient cũng không chính xác lắm bởi vì Refit được build dựa trên HttpClient. Nó thừa hưởng các ưu nhược điểm của HttpClient nhưng được tinh chỉnh để việc sử dụng trở nên dễ dàng hơn.

    Refit là một thư viện cho phép chúng ta tương tác với các API dựa trên HTTP một cách an toàn và dễ dàng hơn. Thay vì sử dụng HttpClient trực tiếp, chúng ta có thể định nghĩa một giao diện đại diện cho API và sử dụng Refit để tạo ra các phương thức tương ứng.

    Trước khi đi vào chi tiết bài viết mình sẽ đưa ra 2 ví dụ đơn giản để các bạn so sánh code giữa việc sử dụng HttpClient và Refit

    HttpClient

    public abstract class BaseApiClient
    {
        private readonly HttpClient _httpClient;
    
        protected BaseApiClient(HttpClient httpClient)
        {
            _httpClient = httpClient;
        }
    
        protected async Task GetAsync(string endpoint)
        {
            var response = await _httpClient.GetAsync(endpoint);
            response.EnsureSuccessStatusCode();
            return await response.Content.ReadAsAsync();
        }
    }
    public class GitHubApiClient : BaseApiClient
    {
        public GitHubApiClient(HttpClient httpClient) : base(httpClient)
        {
        }
    
        public async Task GetUser(string username)
        {
            return await GetAsync($"/users/{username}");
        }
    }
    
    class Program
    {
        static async Task Main()
        {
            var octocat = await GitHubApiClient.GetUser("octocat");
    
            Console.WriteLine($"Login: {octocat.Login}, Name: {octocat.Name}");
        }
    }

    Sử dụng Refit

    public interface IGitHubApi
    {
        [Get("/users/{user}")]
        Task GetUser(string user);
    }
    
    class Program
    {
        static async Task Main()
        {
            var gitHubApi = RestService.For("https://api.github.com");
            var octocat = await gitHubApi.GetUser("octocat");
    
            Console.WriteLine($"Login: {octocat.Login}, Name: {octocat.Name}");
        }
    }

    Chúng ta có thể thấy sử dụng Refit giúp code ngắn ngọn hơn. Nhiều bạn sẽ nói HttpClient khi build ra một class helper cũng giải quyết được vấn đề. Đúng, điều đó không sai nhưng mình muốn code ngắn gọn, tiết kiệm thời gian hơn nên lựa chọn Refit có lẽ sẽ tốt hơn.

    Nuget Package

    Tại bài viết này mình sử dụng .NET Core 8 để làm ví dụ. Chúng ta cần cài các nuget packages sau để có thể sử dụng Refit:

    • Refit
    • Refit.HttpClientFactory
    • Refit.Newtonsoft.Json

    (Các bạn có thể kéo xuống và tải ví dụ source code về chạy thử nhé)

    Basic Components of a Refit Client

    Trước khi đi vào cách sử dụng chúng ta cùng tìm hiểu qua về một vài Component trong Refit

    HTTP Method

    Bất cứ khi nào chúng ta tương tác với API qua HTTP. Refit cung cấp một tập hợp các thuộc tính cho phép chúng ta sử dụng các HTTP Method một cách dễ dàng trên interface:

    [Get("/users/search")]
    Task SearchUserAysnc(UserSearchParams searchParams);

    Bằng cách sử dụng [Get("/users/search")], chúng ta cho Refit biết đây là phương thức HTTP GET với endpoint là /users/search.

    Route Parameters

    [Get("/users/{userId}")]
    Task GetUserAysnc(string userId);

    Khi làm việc với các API RESTful tuân theo các quy ước định tuyến tốt, chúng ta thường thấy một điểm cuối như /users/1, mà chúng ta mong đợi sẽ trả về cho chúng ta một người dùng có id là 1.

    Refit sử dụng định tuyến thuộc tính, giống như ASP.NET Core, cho phép chúng ta dễ dàng xác định các tuyến có chứa tham số:

    Bằng cách thêm { và } vào id trong tuyến đường, chúng ta cho Refit biết rằng đây là tham số động đến từ tham số id trong phương thức GetUser().

    Request Json Body

    Bằng cách sử dụng [Body] bạn có thể gửi một json object tới endpoint chỉ định

    [Post("/users/add")]
    Task AddNewUserAysnc([Body] UserModel user);

    Header Collection

    [HeaderCollection] cung cấp cho bạn chức năng gửi kèm các header mong muốn tới endpoint như Authorizetion Bearer

    [Post("/users/add")]
    Task AddNewUserAysnc([HeaderCollection] IDictionary headers = null);

    Sau đó call:

    public async Task Add(UserModel request)
    {
        var loginResponse = await _dummyJsonApiService.LoginAysnc(new AuthLoginModel()
        {
            ExpiresInMins = 60,
            Username = "emilys",
            Password = "emilyspass"
        });
    
        var headers = new Dictionary()
        {
            { "Authorization", $"Bearer {loginResponse.Token}" }
        };
    
        var data = await _dummyJsonApiService.AddNewUserAysnc(headers);
    
        return new JsonResult(data);
    }

    Multipart

    Bạn cũng có thể gửi file bằng cách sử dụng như sau:

    public interface IApi
    {
        [Multipart]
        [Post("/api/story/{id}/upload-images")]
        Task UploadImages(int id, [AliasAs("files")] IEnumerable streams);
    }

    Sau đó gọi như sau:

    var files = new List()
    {
        new StreamPart(fileStream, "photo.jpg", "image/jpeg"),
        new StreamPart(fileStream2, "photo2.jpg", "image/jpeg")
    };
    
    await _api.UploadImages(1, files);

    Xem thêm tại: https://github.com/reactiveui/refit

    Sử dụng Refit

    Đọc 2 ví dụ nhanh ở trên thì chúng ta thấy rằng Refit sử dụng interface để làm đại diện cho API mà chúng ta muốn tương tác. Chúng ta có thể sử dụng các headers, http method trên interface. Khi chương trình biên dịch nó sẽ tạo ra các implement method cho chúng ta.

    Mình sẽ tạo 1 project và sử dụng endpoint là https://dummyjson.com

    Trước tiên là tạo ra interface có tên IDummyJsonApiService

    namespace RefitSample.ApiServices
    {
        public class UserSearchParams
        {
            [AliasAs("q")]
            public string SearchText { get; set; } = string.Empty;
            [AliasAs("limit")]
            public int Limit { get; set; }
            [AliasAs("skip")]
            public int Skip { get; set; }
        }
    
        [Headers(
            "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36",
            "Referer: https://progcoder.com/"
            )]
        public interface IBaseApiService { }
    
        public interface IDummyJsonApiService : IBaseApiService
        {
            [Get("/users/search")]
            Task SearchUserAysnc(UserSearchParams searchParams);
    
            [Get("/users/{userId}")]
            Task GetUserAysnc(string userId);
    
            [Post("/users/add")]
            Task AddNewUserAysnc([Body] UserModel user, [HeaderCollection] IDictionary headers = null);
    
            [Post("/auth/login")]
            Task LoginAysnc([Body] AuthLoginModel login);
        }
    }

    Tạo dependency cho IDummyJsonApiService trong file Program.cs (.NET 8 >), Startup.cs (.NET 6 <)

    builder.Services
        .AddRefitClient()
        .ConfigureHttpClient(c => c.BaseAddress = new Uri("https://dummyjson.com"));

    Đơn giản vậy thôi. Giờ chúng ta chỉ cần gọi IDummyJsonApiService ra và sử dụng. Ví dụ

    private readonly IDummyJsonApiService _dummyJsonApiService;
    
    public UserController(IDummyJsonApiService dummyJsonApiService)
    { 
        _dummyJsonApiService = dummyJsonApiService; 
    }
    
    [Route("search")]
    [HttpGet]
    public async Task Search(string searchText, int limit = 5, int skip = 0) 
    {
        var data = await _dummyJsonApiService.SearchUserAysnc(new UserSearchParams()
        {
            SearchText = searchText,
            Limit = limit,
            Skip = skip
        });
    
        return new JsonResult(data);
    }

    Hanlding Exception

    Để thực hiện bắt các lỗi trả về từ api như 400, 404, 500,.. chúng ta chỉ cần try cach với exception là ApiException 

    try
    {
        await _dummyJsonApiService.GetUserAysnc(userId);
    }
    catch (ApiException ex)
    {
        var content = ex.GetContentAs>();
        Debug.WriteLine(ex.Message);
    }

    Kết

    Refit là một giải pháp tối ưu giúp bạn dễ dàng gọi các API HTTP bằng cách sử dụng giao diện. Nó giảm bớt mã nguồn và tăng cường khả năng bảo trì so với HttpClient và RestSharp.

    Với Refit, bạn có thể định nghĩa API qua giao diện và thực hiện các cuộc gọi HTTP một cách nhanh chóng và hiệu quả.

    Để tìm hiểu thêm về Refit, bạn có thể truy cập https://github.com/reactiveui/refit.


    Tiểu sử
    Are you one or zero?


    Bình luận