Refit Documentation

repository·main·Indexed 27 days ago

https://github.com/reactiveui/refit

A type-safe REST library for .NET that transforms interface definitions into functional API clients using HttpClient. Refit supports Roslyn source generation for high-performance, Native AOT, and trimmed applications. It provides features for defining API endpoints via HTTP attributes, custom query string serialization, URL replacement blocks, and integration with .NET's HttpClientFactory. Compatible with .NET 8/9/10/11, .NET Framework 4.6.2+, WinUI, Blazor, and Uno Platform.

Tokens
21.4K
Snippets
71
Records
88
Agent score
93%

What's inside Refit

  1. Overview of Refit

    main
    Refit is an automatic type-safe REST library for modern .NET. It turns your REST API definitions into live interfaces by generating implementations of your interfaces that use HttpClient to make network calls.
  2. Configure Refit with IHttpClientFactory

    main

    Refit has first-class support for IHttpClientFactory. To use it, add a reference to Refit.HttpClientFactory and use the AddRefitClient<T> extension method in your service configuration.

    // Basic configuration
    services.AddRefitClient<IWebApi>()
            .ConfigureHttpClient(c => c.BaseAddress = new Uri("https://api.example.com"));
    
    // Configuration with RefitSettings
    var settings = new RefitSettings();
    // Configure refit settings here
    
    services.AddRefitClient<IWebApi>(settings)
            .ConfigureHttpClient(c => c.BaseAddress = new Uri("https://api.example.com"))
            .AddHttpMessageHandler<MyHandler>();
  3. Use Newtonsoft.Json as the content serializer

    main

    To use Newtonsoft.Json instead of the default System.Text.Json, add the Refit.Newtonsoft.Json package and pass a NewtonsoftJsonContentSerializer to RefitSettings. You can customize behavior by providing JsonSerializerSettings to the constructor or by setting global JsonConvert.DefaultSettings.

    // Basic usage
    var settings = new RefitSettings(new NewtonsoftJsonContentSerializer());
    
    // Custom settings per API
    var gitHubApi = RestService.For<IGitHubApi>("https://api.github.com",
        new RefitSettings {
            ContentSerializer = new NewtonsoftJsonContentSerializer(
                new JsonSerializerSettings {
                    ContractResolver = new SnakeCasePropertyNamesContractResolver()
                })
        });
  4. Configure scoped authorization tokens with Dependency Injection

    main

    When using Refit.HttpClientFactory, you can resolve tokens from the DI container using AddAuthorizationHeaderValueProvider. This is useful for per-request isolation. The provider receives an IServiceProvider, the HttpRequestMessage, and a CancellationToken.

    services.AddRefitClient<IMyApi>()
        .ConfigureHttpClient(c => c.BaseAddress = new Uri("https://api.example.com"))
        .AddAuthorizationHeaderValueProvider((serviceProvider, request, cancellationToken) =>
        {
            var tokenService = serviceProvider.GetRequiredService<IMyTokenService>();
            return new ValueTask<string>(tokenService.GetTokenForCurrentRequest());
        });
  5. Register generated Refit clients with IHttpClientFactory

    main

    To avoid reflection-based registration in Native AoT or trimmed applications, use AddRefitGeneratedClient<T> from Refit.HttpClientFactory. This ensures the application uses RestService.ForGenerated<T> and throws an InvalidOperationException if a generated client is missing, rather than silently falling back to reflection.

    // Standard registration
    services.AddRefitGeneratedClient<IWebApi>()
            .ConfigureHttpClient(c => c.BaseAddress = new Uri("https://api.example.com"));
    
    // Registration with settings and custom name
    services.AddRefitGeneratedClient<IWebApi>(settings, "my-named-client");
  6. Configure sequenced and reusable routes

    main

    Routes in a StubHttp are consumed in the order they are declared. This allows you to simulate a sequence of responses for the same endpoint.

    Use Reusable = true in a RouteMatcher for routes that should match any number of requests without being required by VerifyAllCalled. One-shot routes always take priority over reusable ones.

    // Sequenced responses
    var http = new StubHttp
    {
        { Route.Get("/status"), Reply.Json("{\"state\":\"pending\"}") },
        { Route.Get("/status"), Reply.Json("{\"state\":\"done\"}") },
    };
    
    // Reusable catch-all route
    var http = new StubHttp
    {
        { new RouteMatcher { Template = "*", Reusable = true }, Reply.Status(HttpStatusCode.OK) },
    };
  7. Compose multiple APIs into one client using interface inheritance

    main

    You can split a large API into focused interfaces and aggregate them into a single client by using interface inheritance. The generator walks the full hierarchy to expose all methods.

    Header Inheritance

    When using inheritance, header attributes are passed along. The inner-most attribute has precedence. If an interface inherits from multiple interfaces, the precedence follows the order in which they are declared.

    public interface IUsersApi
    {
        [Get("/users/{user}")]
        Task<User> GetUser(string user);
    }
    
    public interface IReposApi
    {
        [Get("/users/{user}/repos")]
        Task<List<Repo>> GetRepos(string user);
    }
    
    // The aggregate client composes the two APIs
    public interface IGitHubApi : IUsersApi, IReposApi;
    
    // Usage:
    var api = RestService.For<IGitHubApi>("https://api.github.com");
    var user = await api.GetUser("octocat");
    var repos = await api.GetRepos("octocat");
  8. Install Refit.Testing

    main

    To test your Refit clients without a live server or a general-purpose mocking library, install the Refit.Testing package. This package allows you to define a route table of expected requests and their corresponding replies.

    dotnet add package Refit.Testing
  9. Send URL-encoded form posts

    main

    To send data as application/x-www-form-urlencoded, use the [Body(BodySerializationMethod.UrlEncoded)] attribute. You can pass an IDictionary<string, object> or a plain object. If using an object, all public, readable properties will be serialized as form fields. Use [AliasAs("name")] to map property names to specific form field keys.

    public interface IMeasurementProtocolApi
    {
        [Post("/collect")]
        Task Collect([Body(BodySerializationMethod.UrlEncoded)] Measurement measurement);
    }
    
    public class Measurement
    {
        public int v { get { return 1; } }
    
        [AliasAs("tid")]
        public string WebPropertyId { get; set; }
    
        [AliasAs("cid")]
        public Guid ClientId { get; set; }
    
        [AliasAs("t")]
        public string Type { get; set; }
    }
  10. Test retries and timeouts with Refit.Testing

    main

    Testing Retries

    To test retry policies (like Polly handlers or DelegatingHandler), use sequenced one-shot routes in StubHttp. The routes are matched in the order they are declared. A transient fault (e.g., HttpStatusCode.ServiceUnavailable) followed by a successful response allows you to verify that the retry logic correctly triggers.

    To simulate a transport fault (rather than a status code error), use Reply.From to throw an exception: Reply.From(HttpResponseMessage (_) => throw new HttpRequestException("boom")).

    Testing Timeouts

    Use NetworkBehavior.Delay to inject latency into the StubHttp. If the injected delay exceeds the HttpClient.Timeout, the request will be aborted. Refit surfaces this as an ApiRequestException where the InnerException is a TaskCanceledException.

    // Testing Retries
    var http = new StubHttp
    {
        { Route.Get("/users/{id}"), Reply.Status(HttpStatusCode.ServiceUnavailable) },  // first attempt
        { Route.Get("/users/{id}"), Reply.With(new User(7, "octocat")) },               // the retry
    };
    
    var settings = new RefitSettings { HttpMessageHandlerFactory = () => new MyRetryHandler(http) };
    var api = RestService.For<IUserApi>("https://api.test", settings);
    
    var user = await api.GetUser(7);        // succeeded on the retry
    Assert.Equal(2, http.Requests.Count);   // both attempts were made
    
    // Testing Timeouts
    var http = new StubHttp(new NetworkBehavior { Delay = TimeSpan.FromSeconds(10), Variance = 0 })
    {
        { Route.Get("/users/{id}"), Reply.With(new User(7, "octocat")) },
    };
    
    using var client = new HttpClient(http)
    {
        BaseAddress = new Uri("https://api.test"),
        Timeout = TimeSpan.FromMilliseconds(50),
    };
    
    await Assert.ThrowsAsync<ApiRequestException>(() => RestService.For<IUserApi>(client).GetUser(7));
  11. Use the generate-publicapi script

    main

    Run the generate-publicapi script to update the public API baseline files. You can run it for all tracked libraries or provide an optional case-sensitive substring to filter by project path (e.g., to target a specific library).

    When to run:

    • After changing any public (or protected on a public type) API.
    • After adding a new target framework to a tracked library.
    • After bumping an analyzer package that changes how the public surface is rendered.
    ### Linux / macOS
    ```bash
    tools/generate-publicapi.sh                 # all tracked libraries, all TFMs
    tools/generate-publicapi.sh HttpClient      # only projects whose path contains 'HttpClient'
    tools/generate-publicapi.sh Refit.Xml

    Windows (PowerShell)

    ./tools/generate-publicapi.ps1                     # all tracked libraries
    ./tools/generate-publicapi.ps1 -Filter HttpClient  # path filter