Pull to refresh

Инструменты создания API клиента для .NET

Level of difficultyEasy
Reading time8 min
Views8.8K

В начале разработки нового проекта у команды всегда появлялся вопрос, какую библиотеку выбрать для межсервисного взаимодействия? А какую использовать для походов в сторонние сервисы? А что там с тестированием? В этой статье я постарался вкратце осветить различные обёртки над HttpClient.

Flurl

Начнём с Flurl, данная библиотека предоставляет хорошее сочетание URL конструктора и API клиента, посредствам Fluent Interface синтаксиса.

Например, отправка POST запроса с параметрами, авторизацией и телом, а также десереализация результата в конкретный тип будет выглядеть так:

var person = await "https://api.com"
    .AppendPathSegment("person")
    .SetQueryParams(new { a = 1, b = 2 })
    .WithOAuthBearerToken("my_oauth_token")
    .PostJsonAsync(new
    {
        first_name = "Claire",
        last_name = "Underwood"
    })
    .ReceiveJson<Person>();

Библиотека особенно удобна при тестировании. Например, можно удобно разобрать URL на составляющие:

var url = new Url("https://user:pass@www.mysite.com:1234/with/path?x=1&y=2#foo");
Assert.AreEqual("https", url.Scheme);
Assert.AreEqual("user:pass", url.UserInfo);
Assert.AreEqual("www.mysite.com", url.Host);
Assert.AreEqual(1234, url.Port);
Assert.AreEqual("user:pass@www.mysite.com:1234", url.Authority);
Assert.AreEqual("https://user:pass@www.mysite.com:1234", url.Root);
Assert.AreEqual("/with/path", url.Path);
Assert.AreEqual("x=1&y=2", url.Query);
Assert.AreEqual("foo", url.Fragment);

Или сделать тест в привычным AAA стиле:

using (var httpTest = new HttpTest()) {
    // arrange
    httpTest.RespondWith("OK", 200);
    // act
    await sut.CreatePersonAsync();
    // assert
    httpTest.ShouldHaveCalled("https://api.com/*")
        .WithVerb(HttpMethod.Post)
        .WithContentType("application/json");
}

Можно подстроить тест под нужные условия с помощью различных фильтров:

using var httpTest = new HttpTest();

httpTest
    .ForCallsTo("*.api.com*", "*.test-api.com*") // multiple allowed, wildcard supported
    .WithVerb("put", "PATCH") // or HttpMethod.Put, HttpMethod.Patch
    .WithQueryParam("x", "a*") // value optional, wildcard supported
    .WithQueryParams(new { y = 2, z = 3 })
    .WithAnyQueryParam("a", "b", "c")
    .WithoutQueryParam("d")
    .WithHeader("h1", "f*o") // value optional, wildcard supported
    .WithoutHeader("h2")
    .WithRequestBody("*something*") // wildcard supported
    .WithRequestJson(new { a = "*", b = "hi" }) // wildcard supported in sting values
    .With(call => true) // check anything on the FlurlCall
    .Without(call => false) // check anything on the FlurlCall
    .RespondWith("all conditions met!", 200);

Конфигурация представляет из себя иерархию уровней, начиная от глобальной, заканчивая конкретным экземпляром запроса. Соответственно каждый уровень наследует и переопределяет настройки предыдущего уровня.

Всего настроек не так много, но в наличие все основные: Timeout, AllowedHttpStatusRange, JsonSerializer, UrlEncodedSerializer, Redirects, BeforeCall, AfterCall, OnError, OnRedirect.

 

Данная библиотека показалась мне удобной как и в качестве инструмента для тестирования запросов, так и в качестве основы для API клиента.

 

NSwagStudio

Думаю, много кто сталкивался с NSwagStudio. На этот инструмент написано достаточно много материала, поэтому буду краток.

С помощью NSwagStudio можно:

Всё перечисленное делает из NSwagStudio действительно удобный инструмент для создания API клиентов, но есть и большой минус, он генерирует только то, что умеет. Например, если у вас в проекте используется хитрая система обработки ошибок, то скорей всего этот инструмент вам не подойдёт.

RestSharp

RestSharp позиционирует себя как легковесная REST API Client библиотека поддерживаемая AWS.

Типичный сервис с использованием RestSharp будет выглядеть так:

public class TwitterClient : ITwitterClient, IDisposable {
    readonly RestClient _client;

    public TwitterClient(string apiKey, string apiKeySecret) {
        var options = new RestClientOptions("https://api.twitter.com/2");

        _client = new RestClient(options) {
            Authenticator = new TwitterAuthenticator("https://api.twitter.com", apiKey, apiKeySecret)
        };
    }

    public async Task<TwitterUser> GetUser(string user) {
        var response = await _client.GetJsonAsync<TwitterSingleObject<TwitterUser>>(
            "users/by/username/{user}",
            new { user }
        );
        return response!.Data;
    }

    record TwitterSingleObject<T>(T Data);

    public void Dispose() {
        _client?.Dispose();
    }
}

 Запрос достаточно просто обогатить параметрами, телом, Cookie, заголовками и т.д.

var request = new RestRequest("beavers");
request.AddParameter("status", 1);
request.AddHeader("name", "value");

 Так же для всего RestClient можно добавить параметры, заголовки и т. д. по умолчанию.

RestClient client = new("https://localhost:44376/");
client.AddDefaultHeader("MyHeader", "default");

Вызов метода можно осуществить двумя способами:

Например, вызвав ExecuteGetAsync или GetAsync. В первом случае мы получим промежуточный объект RestResponse, из которого можно вытащить дополнительную информацию, полученную в ответе (например заголовки). Кроме того, вызовы с префиксом Execute не генерируют исключения, его вам придётся считать из соответствующего поля.

 

RestSharp отличный инструмент для создания REST API клиента, который позволяет детально настроить запрос и получить подробный ответ. Из минусов я могу отметить отсутствие системы обработки ошибок из коробки, вам придётся написать её самому.

 

Refit

Refit – REST библиотека которая упрощает создания Api клиента до минимума, при этом предоставляя обширный функционал кастомизации.

Создание клиента происходит по средствам описания интерфейса:

public interface IGitHubApi
{
    [Get("/users/{user}")]
    Task<User> GetUser(string user);
}

Получить API клиент можно через метод RestService.For<T>:

var gitHubApi = RestService.For<IGitHubApi>("https://api.github.com");
var octocat = await gitHubApi.GetUser("octocat");

Или зарегистрировать его в IoC, через Refit.HttpClientFactory:

services
    .AddRefitClient<IGitHubApi>()
    .ConfigureHttpClient(c => c.BaseAddress = new Uri("https://api.github.com"));

 Функционал библиотеки позволяет детально настроить поведение.

var gitHubApi = RestService.For<IGitHubApi>("https://api.github.com",
    new RefitSettings {
        ContentSerializer = new NewtonsoftJsonContentSerializer(
            new JsonSerializerSettings {
                ContractResolver = new SnakeCasePropertyNamesContractResolver()
            }
        )});

var otherApi = RestService.For<IOtherApi>("https://api.example.com",
    new RefitSettings {
        ContentSerializer = new NewtonsoftJsonContentSerializer(
            new JsonSerializerSettings {
                ContractResolver = new CamelCasePropertyNamesContractResolver()
            }
        )});

 

В библиотеке реализован механизм обработки исключений. По умолчанию доступны классы исключений ValidationApiException и ApiException. Создать свои объекты для исключений и поменять поведение можно через параметр ExceptionFactory.

 

Стоит упомянуть полезный инструмент Refitter, который активно развивается и обновляется. Refitter — это небольшая утилита для генерации Refit клиента из OpenApi спецификации. Это может быть очень полезно, если вам захотелось мигрировать на Refit, но на создание клиентов не хватает ресурсов.

Ещё одна библиотека под названием Generate AspNetCore Client позволяет генерировать Refit клиенты из готового приложения без необходимости OpenApi спецификации. Достигается это путём запуска приложения и вытягивания информации о контроллерах. Библиотека подойдёт не всем, так как сделана довольно криво, особенно если ваш проект не так просто запустить без танцев с бубном.

 

RestEase

На первый взгляд RestEase мало чем отличается от Refit, и, более того, почти все API клиенты, которые вы напишете на Refit, запустятся и на RestEase. Но по мимо схожего синтаксиса, библиотека предоставляет свои фичи.

Например, мне понравилась возможность передать с помощью атрибута [QueryMap] словарь параметров, это удобный способ передать данные, которые заранее не известны или формируются динамически.

Другая не менее интересная фича это Query Properties. Всё просто, необходимо добавить свойство и отметить его атрибутом [Query].

public interface ISomeApi
{
    [Query("foo")]
    string Foo { get; set; }

    [Get("thing")]
    Task ThingAsync([Query] string foo);
}

var api = RestClient.For<ISomeApi>("https://api.example.com");
api.Foo = "bar";

// Requests https://api.example.com?foo=baz&foo=bar
await api.ThingAsync("baz");

 

Ещё одним преимуществом данной библиотеки является Source Generator, который позволит обнаружить ошибки на этапе компиляции и немного ускорить выполнение.

Сгенерировать клиент из OpenApi спецификации можно с помощью расширения для Visual Studio - RestEase Client Generator. Библиотека не обновлялась с конца 2019 года

Библиотека имеет множество интересных возможностей, если вы достаточно наигрались с Refit, рекомендую присмотреться к данной библиотеке.

  

Kiota

Kiota – это набор из библиотек и утилиты командной строки для генерации Api клиентов на основе OpenApi спецификации. Библиотека активно развивается под крылом Microsoft.

После небольшой команды

kiota generate -l CSharp -c PostsClient -n KiotaPosts.Client -d ./posts-api.yml -o ./Client

Получаем готовый набор API клиента.

Вызов методы выглядит примерно так:

var authProvider = new AnonymousAuthenticationProvider();
var adapter = new HttpClientRequestAdapter(authProvider);
adapter.BaseUrl = "https://localhost:44376";

var client = new BeaversServer_Kiota(adapter);

var bernarId = await client.Beavers.PostAsync(
    new RefClient.ApiClients.Kiota.Models.CreateBeaver
    {
        Eat = 10,
        Name = "Bernar"
    });

 

Библиотека предоставляет богатую настройку запроса, но вот из ответа вытащить дополнительные параметры так просто у вас не получится.

Kiota выглядит перспективной библиотекой, тем более поддерживает не только C#, но и Go, Java, PHP, Python, Ruby, Swift, TypeScript/JavaScript, но на текущий момент предоставляет довольно мало возможностей.

 

REST API Client Code Generator

Стоить упомянуть удобный плагин для Visual Studio, который позволяет генерировать различные клиенты через графический интерфейс.

Сводная таблица

Flurl

NSwag

RestSharp

Refit

RestEase

Kiota

HttpClient

+

+

v107+

+

+

+

Реализация Api клиента

Явная

Явная

Явная

Неявная

Неявная

Явная

Генерация из OpenApi спецификации

-

+

+

+

+

+

⭐⭐⭐

3.6k

5.9k

9.1k

7.3k

~1k

~800

Заключение

В заключение хочется сказать, что представленные инструменты отлично справляются с поставленной задачей. У меня не получилось выявить явного лидера, поэтому выбирать инструмент следует из потребностей проекта и личного предпочтения. Если вам нужен максимальный контроль над реализацией, то следует обратить внимание на Flurl, NSwag, RestSharp, если вы придерживаетесь минимализма и не хотите перегружать свой код деталями реализации вам больше подойдёт Refit или RestEase.

На этом у меня всё, возможно вы знаете ещё парочку интересных библиотек, которые я не упомянул, с радостью прочитаю о них в комментариях

Only registered users can participate in poll. Log in, please.
Что Вы предпочитаете?
2.56% Flurl3
9.4% NSwag11
14.53% RestSharp17
12.82% Refit15
0% RestEase0
0% Kiota0
50.43% Чистый HttpClient59
10.26% Другое12
117 users voted. 31 users abstained.
Tags:
Hubs:
Total votes 7: ↑7 and ↓0+7
Comments7

Articles