เรียก HTTP API อย่างสะดวกด้วย Refit
Tags: refit, dotnet, aspnetcore, csharp
Published: 2022-07-22 10:26:29

<span>Photo by <a href="https://unsplash.com/@christianw?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Christian Wiediger</a> on <a href="https://unsplash.com/s/photos/computer?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></span>

## Refit คืออะไร ##

[Refit](https://github.com/reactiveui/refit) เป็น library ที่ทำให้เราสามารถเรียกใช้งาน HTTP API ได้ผ่าน interface ของ C# โดยที่เราไม่ต้องมาประกอบ URL/headers/body เองและ deserialize เองครับ 

## การใช้งาน ##

อันดับแรก ก็ติดตั้ง NuGet package ของ Refit ก่อนครับ เนื่องจากต้องการใช้แบบ HttpClientFactory เลยติดตั้งตัวนี้ตัวเดียวก็ได้

https://www.nuget.org/packages/Refit.HttpClientFactory/

เพื่อเป็นตัวอย่าง สมมติว่าผมจะเรียก [Northwind API](https://northwind.now.sh) โดยมี API endpoint ที่จะเรียกอยู่ 3 ตัว คือ

`GET /api/categories`
`GET /api/categories/{categoryId}`
`POST /api/categories` 

โดย [response](https://northwind.now.sh/api/categories) และ body ของ POST เป็น application/json มีรูปแบบประมาณนี้

```json
{
    name: "เครื่องดื่ม",
    description: "ชา กาแฟ น้ำผลไม้ น้ำอัดลม เบียร์ ฯลฯ",
}
```

จาก API ที่กำหนดมา เราต้องแปลงมันเป็น interface ก่อน ซึ่งก็จะคล้ายคลึงกับการเขียน method ใน Controller ของ ASP.NET Core อยู่มาก รูปแบบก็จะทำนองนี้ คิดว่าน่าจะพอจับรูปแบบได้ครับ

```csharp
public class Category 
{
    public string name { get; set; }
    public string description { get; set; }
}

public interface INorthwindApi
{
    [Get("/api/categories"]
    Task<List<Category>> GetCategoriesAsync();

    [Get("/api/categories/{categoryId}"]
    Task<Category> GetCategoryAsync(int categoryId);    

    [Post("/api/categories")]
    Task AddCategoryAsync(Category newCategory);
}
```

(การเขียน interface จะต้องใช้ method ที่ return `Task` หรือ `Task<T>` เสมอครับ เพราะ Refit ไม่รองรับการเรียก API แบบ synchoronous)

ถ้ามองจาก interface นี้ เราจะเห็นว่าถ้าเรามี interface นี้เราก็สามารถเรียกมันได้ตรง ๆ เลย และตัว Refit นี่เองก็จะเป็นตัวที่จัดการให้เราครับ ในที่นี้ จะใช้วิธีเรียกใช้โดยผ่าน DI ครับ ดังนั้นจึงต้องมีการ register service ก่อน โดยในส่วน `ConfigureServices` ใน `Startup` class ให้เพิ่มดังนี้ครับ

```csharp
services.AddRefitClient<INorthwindApi>()
        .ConfigureHttpClient(
            c => c.BaseAddress = new Uri("https://northwind.now.sh/")
        );
```

แค่นี้เราก็จะมี service `INorthwindApi` ที่สามารถ inject ไปใช้ใน Controller ได้แล้วครับ เช่นเราสามารถสร้าง Controller ประมาณนี้ได้

```csharp
[Route("api/categories"]
public class NorthwindCategoryController : ControllerBase
{
    private readonly INorthwindApi _api;
    public NorthwindCategoryController(INorthwindApi api) 
    {
        _api = api;
    }

    [HttpGet]
    public Task<List<Category>> GetCategoriesAsync() 
        => _api.GetCategoriesAsync();

    [HttpGet("{categoryId}"]
    Task<Category> GetCategoryAsync(int categoryId) 
        => _api.GetCategoryAsync();

    [HttpPost]
    Task AddCategoryAsync(Category newCategory) 
        => _api.AddCategoryAsync(newCategory);
}
```

จะเห็นว่าการใช้ Refit เราก็ไม่ต้องยุ่งยากกับการใช้ HttpClient รวมถึงไม่ต้องจัดการเรื่องการ serialization/deserialization อะไรเลย