Refit - Thư viện thay thế tuyệt vời cho HttpClient trong C#
By Huy Nguyễn
474 views
0 likes
0 comments
0/10 rate
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
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
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}");
}
}
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.
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:
(Các bạn có thể kéo xuống và tải ví dụ source code về chạy thử nhé)
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
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.
[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().
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);
[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);
}
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
Đọ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);
}
Để 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);
}
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.