### Install Refit.Testing package Source: https://reactiveui.github.io/refit/docs/testing.html Adds the Refit.Testing NuGet package to the test project. The package targets the same frameworks as Refit itself. ```shell dotnet add package Refit.Testing ``` -------------------------------- ### Define a Refit interface for testing Source: https://reactiveui.github.io/refit/docs/testing.html Example Refit interface with Get and Post endpoints. The route templates in the attributes are used by Refit.Testing to match requests. ```csharp public interface IGitHubApi { [Get("/users/{id}")] Task GetUser(int id); [Post("/users")] Task CreateUser([Body] NewUser user); } ``` -------------------------------- ### Implement AuthHeaderHandler and register with Dependency Injection Source: https://reactiveui.github.io/refit Create a custom DelegatingHandler to inject headers, then register it in Startup.cs using AddRefitClient and AddHttpMessageHandler. The example also shows how to consume the resulting API in business logic. ```csharp //Custom delegating handler for adding Auth headers to outbound requests class AuthHeaderHandler : DelegatingHandler { private readonly ITenantProvider tenantProvider; private readonly IAuthTokenStore authTokenStore; public AuthHeaderHandler(ITenantProvider tenantProvider, IAuthTokenStore authTokenStore) { this.tenantProvider = tenantProvider ?? throw new ArgumentNullException(nameof(tenantProvider)); this.authTokenStore = authTokenStore ?? throw new ArgumentNullException(nameof(authTokenStore)); // InnerHandler must be left as null when using DI, but must be assigned a value when // using RestService.For // InnerHandler = new HttpClientHandler(); } protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { var token = await authTokenStore.GetToken(); //potentially refresh token here if it has expired etc. request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token); request.Headers.Add("X-Tenant-Id", tenantProvider.GetTenantId()); return await base.SendAsync(request, cancellationToken).ConfigureAwait(false); } } //Startup.cs public void ConfigureServices(IServiceCollection services) { services.AddTransient(); services.AddTransient(); services.AddTransient(); //this will add our refit api implementation with an HttpClient //that is configured to add auth headers to all requests //note: AddRefitClient requires a reference to Refit.HttpClientFactory //note: the order of delegating handlers is important and they run in the order they are added! services.AddRefitClient() .ConfigureHttpClient(c => c.BaseAddress = new Uri("https://api.example.com")) .AddHttpMessageHandler(); //you could add Polly here to handle HTTP 429 / HTTP 503 etc } //Your application code public class SomeImportantBusinessLogic { private ISomeThirdPartyApi thirdPartyApi; public SomeImportantBusinessLogic(ISomeThirdPartyApi thirdPartyApi) { this.thirdPartyApi = thirdPartyApi; } public async Task DoStuffWithUser(string username) { var user = await thirdPartyApi.GetUser(username); //do your thing } } ``` -------------------------------- ### Define Derived Refit Interfaces Source: https://reactiveui.github.io/refit Example of a base interface IGetAPI and derived interfaces IUsersAPI and IOrdersAPI. ```csharp public interface IGetAPI { [Get("/{key}")] Task Get(long key); } public interface IUsersAPI : IGetAPI { } public interface IOrdersAPI : IGetAPI { } ``` -------------------------------- ### UrlResolutionMode.Rfc3986 behavior examples Source: https://reactiveui.github.io/refit Demonstrates how trailing slashes on the base address affect relative path resolution under RFC 3986 mode. ```csharp // base address "https://api.example.com/api/v1/" [Get("values")] // -> https://api.example.com/api/v1/values (appended) [Get("/values")] // -> https://api.example.com/values (leading slash replaces the base path) ``` -------------------------------- ### Test retry policy with sequenced one-shot routes in StubHttp Source: https://reactiveui.github.io/refit/docs/testing.html Sets up StubHttp with two routes in declared order: first returns ServiceUnavailable, second returns a success. This exercises a retry handler (e.g., Polly) stacked above the stub. The example asserts that both attempts were made. ```csharp 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("https://api.test", settings); var user = await api.GetUser(7); // succeeded on the retry Assert.Equal(2, http.Requests.Count); // both attempts were made ``` -------------------------------- ### Assert on request URI and method Source: https://reactiveui.github.io/refit/docs/testing.html Use the Requests collection on StubHttp to assert on the request URI and HTTP method after making a call. The example shows a GET request to /users/7. ```csharp await api.GetUser(7); Assert.Equal("/users/7", http.Requests[0].RequestUri!.AbsolutePath); Assert.Equal(HttpMethod.Get, http.Requests[0].Method); ``` -------------------------------- ### Define sequenced routes for the same endpoint Source: https://reactiveui.github.io/refit/docs/testing.html Multiple routes for the same endpoint are consumed in declared order, so successive calls get successive responses. The example shows two GET /status routes returning different JSON responses. ```csharp var http = new StubHttp { { Route.Get("/status"), Reply.Json("{\"state\":\"pending\"}") }, { Route.Get("/status"), Reply.Json("{\"state\":\"done\"}") }, }; // first GET /status -> pending, second GET /status -> done ``` -------------------------------- ### Test a Refit client with StubHttp Source: https://reactiveui.github.io/refit/docs/testing.html Creates a StubHttp with a route table, creates a client, calls the API, asserts the response, and verifies all expected calls were made. Reply.With serializes the User with the client's serializer, and Route.Get matches the URL produced by the attribute. ```csharp using Refit.Testing; [Test] public async Task GetsAUser() { var http = new StubHttp { { Route.Get("/users/{id}"), Reply.With(new User(7, "octocat")) }, }; var api = http.CreateClient("https://api.github.com"); var user = await api.GetUser(7); Assert.Equal("octocat", user.Login); await http.VerifyAllCalledAsync(); } ``` -------------------------------- ### Initializing RefitSettings with NewtonsoftJsonContentSerializer Source: https://reactiveui.github.io/refit Create a new RefitSettings instance using the Newtonsoft.Json serializer. ```csharp var settings = new RefitSettings(new NewtonsoftJsonContentSerializer()); ``` -------------------------------- ### Define HTTP Get Attribute Source: https://reactiveui.github.io/refit Basic usage of the Get attribute to define a request method and relative URL. ```csharp [Get("/users/list")] ``` -------------------------------- ### Create a Refit client with CreateClient Source: https://reactiveui.github.io/refit/docs/testing.html Use CreateClient to create a Refit client that uses StubHttp as the HttpMessageHandler. This is shorthand for RestService.For with the stub's settings. ```csharp var api = http.CreateClient("https://api.github.com"); ``` -------------------------------- ### Define Get Attribute with Query Parameters Source: https://reactiveui.github.io/refit Specifying query parameters directly within the URL string of the Get attribute. ```csharp [Get("/users/list?sort=desc")] ``` -------------------------------- ### Manual HttpClient configuration for DelegatingHandler Source: https://reactiveui.github.io/refit Use this approach if you are not using dependency injection to manually instantiate the HttpClient with the custom handler. ```csharp var api = RestService.For(new HttpClient(new AuthHeaderHandler(tenantProvider, authTokenStore)) { BaseAddress = new Uri("https://api.example.com") } ); var user = await thirdPartyApi.GetUser(username); //do your thing ``` -------------------------------- ### Create a Refit client with CreateGeneratedClient Source: https://reactiveui.github.io/refit/docs/testing.html Use CreateGeneratedClient for trim- or AOT-compiled test hosts. This uses the source-generated client instead of the reflection path, and a generated implementation must be registered for the interface. ```csharp var api = http.CreateGeneratedClient("https://api.github.com"); ``` -------------------------------- ### Create a Refit client with custom RefitSettings Source: https://reactiveui.github.io/refit/docs/testing.html Pass custom RefitSettings to CreateClient to keep a custom serializer or other settings while routing through the stub handler. ```csharp var settings = new RefitSettings(new SystemTextJsonContentSerializer(myOptions)); var api = http.CreateClient("https://api.github.com", settings); ``` -------------------------------- ### Capture method arguments with RefitSettings Source: https://reactiveui.github.io/refit Set CaptureMethodArguments to true to access call arguments in a DelegatingHandler. This example demonstrates how to retrieve and log arguments using HttpRequestMessageOptions. ```csharp var settings = new RefitSettings { CaptureMethodArguments = true }; class ArgumentLoggingHandler : DelegatingHandler { public ArgumentLoggingHandler(HttpMessageHandler innerHandler = null) : base(innerHandler ?? new HttpClientHandler()) {} protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { if (request.Options.TryGetValue(new HttpRequestOptionsKey(HttpRequestMessageOptions.MethodArguments), out var arguments)) { // Reflection path: pair the values with RestMethodInfo.MethodInfo.GetParameters() to recover parameter names. if (request.Options.TryGetValue(new HttpRequestOptionsKey(HttpRequestMessageOptions.RestMethodInfo), out var restMethodInfo)) { var parameters = restMethodInfo.MethodInfo.GetParameters(); for (var i = 0; i < arguments.Length; i++) { Console.WriteLine($"{parameters[i].Name} = {arguments[i]}"); } } } return await base.SendAsync(request, cancellationToken).ConfigureAwait(false); } } ``` -------------------------------- ### Construct a RouteMatcher directly Source: https://reactiveui.github.io/refit/docs/testing.html For finer matching, construct a RouteMatcher and set only the properties you need. Query and Headers are partial matches: listed pairs must be present, extras are allowed. ```csharp new RouteMatcher { Method = HttpMethod.Get, Template = "/search", Query = [("q", "refit")], // partial: these pairs must be present, others allowed Headers = [("X-Trace", "abc")], // request or content headers } ``` -------------------------------- ### Create generated client with RestService.For Source: https://reactiveui.github.io/refit Instantiate a generated client using the standard RestService.For API. ```csharp var api = RestService.For("https://api.github.com"); ``` -------------------------------- ### Catch exceptions when returning Task Source: https://reactiveui.github.io/refit Use this pattern when your Refit method returns Task. Refit throws ApiRequestException for request failures, ApiException for response/deserialization failures, and ApiExceptionBase to catch both. The example shows catching ApiRequestException, ApiException, and ApiExceptionBase in that order. ```csharp // ... try { var result = await awesomeApi.GetFooAsync("bar"); } catch (ApiRequestException exception) { //exception handling for when a response was not received from the server } catch (ApiException exception) { //exception handling for when a response was received from the server } // Or to not distinguish between request/response exceptions catch (ApiExceptionBase exception) { //exception handling for when an error occurs during the request/response } // ... ``` -------------------------------- ### Create and use a Refit service Source: https://reactiveui.github.io/refit Use the RestService class to generate an implementation of the API interface and execute a request. ```csharp var gitHubApi = RestService.For("https://api.github.com"); var octocat = await gitHubApi.GetUser("octocat"); ``` -------------------------------- ### URL-encoded bodies flatten nested objects, dictionaries, and collections Source: https://reactiveui.github.io/refit/docs/breaking-changes.html Shows that a [Body(BodySerializationMethod.UrlEncoded)] model now flattens nested objects, dictionaries, and collections into key-value pairs instead of using ToString(). The example demonstrates the new flattened output versus the old type-name output. This is a behavior change to previously unusable output. ```csharp public sealed class SignupForm { public string Name { get; set; } public Address Detail { get; set; } public Dictionary Extra { get; set; } } Submit(new SignupForm { Name = "ada", Detail = new() { City = "Wien" }, Extra = new() { ["k"] = "v" } }) >>> "Name=ada&Detail.City=Wien&Extra.k=v" // V14: flattened >>> "Name=ada&Detail=RefitGeneratorTest.Address&Extra=System..." // pre-V14: ToString ``` -------------------------------- ### Register Refit client with HttpClientFactory Source: https://reactiveui.github.io/refit Configure a Refit client using .NET's HttpClientFactory dependency injection pattern. ```csharp services .AddRefitClient() .ConfigureHttpClient(c => c.BaseAddress = new Uri("https://api.github.com")); ``` -------------------------------- ### Flattened query-object keys honor content serializer property names Source: https://reactiveui.github.io/refit/docs/breaking-changes.html Shows how a query object's property keys now use the content serializer's property name (e.g., [JsonPropertyName]) instead of the CLR name. The example shows the generated path and the reflection path both resolving to the serializer field name in V14, while V13 used the CLR name. To keep pre-V14 behavior, set RefitSettings.HonorContentSerializerPropertyNamesInQuery = false. ```csharp public sealed class Filter { [JsonPropertyName("created_after")] public DateTimeOffset? CreatedAfter { get; set; } } [Get("/search")] Task Search([Query] Filter filter); Search(new Filter { CreatedAfter = ... }) >>> "/search?created_after=..." // V14: serializer field name >>> "/search?CreatedAfter=..." // V13: CLR name ``` -------------------------------- ### Register generated client with HttpClientFactory Source: https://reactiveui.github.io/refit Configure a generated client using the Refit.HttpClientFactory extension method. ```csharp services .AddRefitClient() .ConfigureHttpClient(c => c.BaseAddress = new Uri("https://api.github.com")); ``` -------------------------------- ### Configure Refit with RefitSettings Source: https://reactiveui.github.io/refit Pass a RefitSettings object or a provider delegate to AddRefitClient to customize Refit behavior. ```csharp var settings = new RefitSettings(); // Configure refit settings here services.AddRefitClient(settings) .ConfigureHttpClient(c => c.BaseAddress = new Uri("https://api.example.com")); // Add additional IHttpClientBuilder chained methods as required here: // .AddHttpMessageHandler() // .SetHandlerLifetime(TimeSpan.FromMinutes(2)); // or injected from the container services.AddRefitClient(provider => new RefitSettings() { /* configure settings */ }) .ConfigureHttpClient(c => c.BaseAddress = new Uri("https://api.example.com")); // Add additional IHttpClientBuilder chained methods as required here: // .AddHttpMessageHandler() // .SetHandlerLifetime(TimeSpan.FromMinutes(2)); ``` -------------------------------- ### Setting advanced compression options Source: https://reactiveui.github.io/refit Use RequestCompressionOptions to configure specific parameters like window size or quality for compression algorithms. This requires .NET 9.0 or later. ```csharp var settings = new RefitSettings { RequestCompression = RequestCompression.Brotli, RequestCompressionOptions = new() { Brotli = new() { Quality = 9, WindowLog2 = 22 }, GZip = new() { CompressionLevel = 6 }, }, }; ``` -------------------------------- ### Build common routes with Route factory Source: https://reactiveui.github.io/refit/docs/testing.html Use the Route factory to create route matchers for common HTTP verbs. Route.Any matches any HTTP method, and Route.For allows a custom method. ```csharp Route.Get("/users/{id}") Route.Post("/users") Route.Delete("/users/{id}") Route.Any("/health") // matches any HTTP method Route.For(new HttpMethod("PATCH"), "/users/{id}") ``` -------------------------------- ### Post Request with Query Parameters Source: https://reactiveui.github.io/refit Shows how to use the Query attribute to flatten parameters in non-GET requests. ```csharp [Post("/statuses/update.json")] Task PostTweet([Query]TweetParams params); ``` -------------------------------- ### Use [PathPrefix] on an interface Source: https://reactiveui.github.io/refit Applies a common route prefix to all methods within an interface. ```csharp [PathPrefix("/api/v2")] public interface IUsersApi { [Get("/users")] // -> /api/v2/users Task> GetAll(); [Get("/users/{id}")] // -> /api/v2/users/{id} Task Get(int id); [Get("/search")] // -> /api/v2/search?query=... Task> Search(string query); } ``` -------------------------------- ### Instantiate a generic Refit API Source: https://reactiveui.github.io/refit Use the generic interface with a specific type and base URL. ```csharp // The "/users" part here is kind of important if you want it to work for more // than one type (unless you have a different domain for each type) var api = RestService.For>("http://api.example.com/users"); ``` -------------------------------- ### Catch-all Route Parameters Source: https://reactiveui.github.io/refit Using double-asterisk syntax to capture segments including forward slashes. ```csharp [Get("/search/{**page}")] Task> Search(string page); Search("admin/products"); >>> "/search/admin/products" ``` -------------------------------- ### Access MethodName and RelativePathTemplate for Telemetry Source: https://reactiveui.github.io/refit Uses lightweight string options for low-cardinality telemetry tracking without reflection. ```csharp class TelemetryHandler : DelegatingHandler { public TelemetryHandler(HttpMessageHandler innerHandler = null) : base(innerHandler ?? new HttpClientHandler()) {} protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { if (request.Options.TryGetValue(new HttpRequestOptionsKey(HttpRequestMessageOptions.MethodName), out var methodName) && request.Options.TryGetValue(new HttpRequestOptionsKey(HttpRequestMessageOptions.RelativePathTemplate), out var routeTemplate)) { // routeTemplate is "/users/{id}" (low cardinality), not the filled "/users/123" using var activity = ActivitySource.StartActivity($"{methodName} {routeTemplate}"); } return await base.SendAsync(request, cancellationToken).ConfigureAwait(false); } } ``` -------------------------------- ### Use [Url] for absolute URLs Source: https://reactiveui.github.io/refit Marks a parameter with [Url] to override the client's base address with an absolute URI. The route template must be empty. ```csharp public interface IFileApi { [Get("")] Task Download([Url] string absoluteUrl); } // base address "https://api.example.com" is ignored: api.Download("https://cdn.example.com/files/report.pdf"); >>> GET https://cdn.example.com/files/report.pdf ``` -------------------------------- ### Expose HttpClient on the API interface Source: https://reactiveui.github.io/refit Expose the HttpClient on the API interface to allow Refit to populate it, enabling configuration while still using Refit settings. ```csharp interface ISomeApi { // This will automagically be populated by Refit if the property exists HttpClient Client { get; } [Headers("Authorization: Bearer")] [Get("/endpoint")] Task SomeApiEndpoint(); } ``` -------------------------------- ### Configure NetworkBehavior with StubHttp for deterministic latency and failures Source: https://reactiveui.github.io/refit/docs/testing.html Creates a NetworkBehavior with a seed for reproducible delays and failures, and passes it to StubHttp. Set FailureFactory to control the exception thrown for simulated transport failures. ```csharp var behavior = new NetworkBehavior(seed: 1) { Delay = TimeSpan.FromMilliseconds(200), // base latency per call Variance = 0.5, // +/- jitter as a fraction of Delay FailurePercent = 0.1, // chance of a thrown transport failure ErrorPercent = 0.2, // chance of an HTTP error response ErrorStatusCode = HttpStatusCode.InternalServerError, }; var http = new StubHttp(behavior) { { Route.Get("/users/{id}"), Reply.With(new User(7, "octocat")) }, }; ``` -------------------------------- ### Configure scoped authorization tokens with DI Source: https://reactiveui.github.io/refit Use AddAuthorizationHeaderValueProvider when registering a client to resolve tokens from dependency injection per request. ```csharp services.AddRefitClient() .ConfigureHttpClient(c => c.BaseAddress = new Uri("https://api.example.com")) .AddAuthorizationHeaderValueProvider((serviceProvider, request, cancellationToken) => { var tokenService = serviceProvider.GetRequiredService(); return new ValueTask(tokenService.GetTokenForCurrentRequest()); }); ``` -------------------------------- ### Configure HttpClient directly using UniqueName Source: https://reactiveui.github.io/refit Use UniqueName.ForType() to compute the deterministic name used by Refit to register the client with IHttpClientFactory. ```csharp services.AddHttpClient(UniqueName.ForType()) .ConfigureHttpClient(c => c.BaseAddress = new Uri("https://api.example.com")); ``` -------------------------------- ### Configure UrlResolutionMode.Rfc3986 Source: https://reactiveui.github.io/refit Sets the RefitSettings to use RFC 3986 URL resolution, matching HttpClient and System.Uri behavior. ```csharp var settings = new RefitSettings { UrlResolution = UrlResolutionMode.Rfc3986 }; var api = RestService.For("https://api.example.com/api/v1/", settings); ``` -------------------------------- ### Implement IApiClient for Runtimes without DIM Support Source: https://reactiveui.github.io/refit Provides an alternative implementation for .NET Core 2.x and .NET Standard 2.0 and below using an internal interface and a wrapper class. ```csharp internal interface IApiClientInternal { [Get("/get")] Task Get(); } public interface IApiClient { public Task Get(); } internal class ApiClient : IApiClient { private readonly IApiClientInternal client; public ApiClient(IApiClientInternal client) => this.client = client; public async Task Get() => FormatResponse(await client.Get()); private static String FormatResponse(string response) => $"The response is: {response}"; } ``` -------------------------------- ### Dynamic Querystring Parameters with Objects Source: https://reactiveui.github.io/refit Demonstrates using a POCO with AliasAs attributes to define querystring parameters, including flattening with the Query attribute. ```csharp public class MyQueryParams { [AliasAs("order")] public string SortOrder { get; set; } public int Limit { get; set; } public KindOptions Kind { get; set; } } public enum KindOptions { Foo, [EnumMember(Value = "bar")] Bar } [Get("/group/{id}/users")] Task> GroupList([AliasAs("id")] int groupId, MyQueryParams params); [Get("/group/{id}/users")] Task> GroupListWithAttribute([AliasAs("id")] int groupId, [Query(".","search")] MyQueryParams params); params.SortOrder = "desc"; params.Limit = 10; params.Kind = KindOptions.Bar; GroupList(4, params) >>> "/group/4/users?order=desc&Limit=10&Kind=bar" GroupListWithAttribute(4, params) >>> "/group/4/users?search.order=desc&search.Limit=10&search.Kind=bar" ``` -------------------------------- ### Access RestMethodInfo in DelegatingHandler Source: https://reactiveui.github.io/refit Demonstrates retrieving full RestMethodInfo from request options to access reflection-based method details. ```csharp class RequestPropertyHandler : DelegatingHandler { public RequestPropertyHandler(HttpMessageHandler innerHandler = null) : base(innerHandler ?? new HttpClientHandler()) {} protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { // Get the method info if (request.Options.TryGetValue(new HttpRequestOptionsKey(HttpRequestMessageOptions.RestMethodInfo), out RestMethodInfo restMethodInfo)) { var builder = new UriBuilder(request.RequestUri); // Alter the Path in some way based on the method info or an attribute on it builder.Path = $"/{restMethodInfo.MethodInfo.Name}{builder.Path}"; // Set the new Uri on the outgoing message request.RequestUri = builder.Uri; } return await base.SendAsync(request, cancellationToken).ConfigureAwait(false); } } ``` -------------------------------- ### Implementing custom query keys with IQueryConverter Source: https://reactiveui.github.io/refit Implement IQueryConverter for full control over query key generation, such as creating nested bracket keys. This is a source-generator-only feature. ```csharp public sealed class SortOrderQueryConverter : IQueryConverter> { public void Flatten( IDictionary value, string keyPrefix, // the resolved [Query(Prefix)] for the parameter, or an empty string ref GeneratedQueryStringBuilder builder, RefitSettings settings) { foreach (var entry in value) { // AddPreEscapedKey appends the key verbatim, so the brackets stay literal while the value is // still escaped. Use builder.Add(key, value, false) to percent-encode the key as well. builder.AddPreEscapedKey($"{keyPrefix}order[{entry.Key}]", entry.Value, false); } } } [Get("/items")] Task> GetItems( [QueryConverter(typeof(SortOrderQueryConverter))] IDictionary order); GetItems(new Dictionary { ["createdAt"] = "desc", ["priority"] = "asc" }); >>> "/items?order[createdAt]=desc&order[priority]=asc" ``` -------------------------------- ### Configure Refit with IHttpClientFactory Source: https://reactiveui.github.io/refit Use the AddRefitClient extension method in ConfigureServices to register a Refit interface with IHttpClientFactory. ```csharp services.AddRefitClient() .ConfigureHttpClient(c => c.BaseAddress = new Uri("https://api.example.com")); // Add additional IHttpClientBuilder chained methods as required here: // .AddHttpMessageHandler() // .SetHandlerLifetime(TimeSpan.FromMinutes(2)); ``` -------------------------------- ### Defining per-property key prefix and delimiter Source: https://reactiveui.github.io/refit Use the Query attribute with prefix and delimiter arguments to customize the generated key format. ```csharp public class Form { [Query("-", "dontlog")] public string Password { get; set; } } // => ?dontlog-Password=... ``` -------------------------------- ### Configuring naming convention presets Source: https://reactiveui.github.io/refit Use RefitSettings presets to apply consistent naming conventions for query keys, form fields, and JSON request bodies. ```csharp var api = RestService.For("https://api.example.com", RefitSettings.SnakeCase()); // also available: RefitSettings.KebabCase() and RefitSettings.CamelCase() ``` -------------------------------- ### Configure System.Text.Json source generator Source: https://reactiveui.github.io/refit Use SystemTextJsonContentSerializer with a custom RefitSettings instance to leverage .NET 6 JSON source generators. ```csharp var gitHubApi = RestService.For("https://api.github.com", new RefitSettings { ContentSerializer = new SystemTextJsonContentSerializer(MyJsonSerializerContext.Default.Options) }); ``` -------------------------------- ### Allow Unmatched Route Parameters Source: https://reactiveui.github.io/refit Configuring RefitSettings to allow route placeholders that do not have matching method arguments. ```csharp var settings = new RefitSettings { AllowUnmatchedRouteParameters = true }; var api = RestService.For("https://api.example.com", settings); // [Get("/api/{version:apiVersion}/values")] // the {version:apiVersion} token is left in the path for a DelegatingHandler to replace. ``` -------------------------------- ### Applying global naming conventions with RefitSettings Source: https://reactiveui.github.io/refit Use the UrlParameterKeyFormatter property to apply a consistent naming convention like CamelCase across all query keys. AliasAs attributes still override these global settings. ```csharp public class MyQueryParams { public string SortOrder { get; set; } [AliasAs("queryLimit")] public int Limit { get; set; } } [Get("/group/users")] Task> GroupList([Query] MyQueryParams params); params.SortOrder = "desc"; params.Limit = 10; ``` ```text /group/users?sortOrder=desc&queryLimit=10 ``` -------------------------------- ### Form post with IDictionary Source: https://reactiveui.github.io/refit Use BodySerializationMethod.UrlEncoded to send form data as an IDictionary. ```csharp public interface IMeasurementProtocolApi { [Post("/collect")] Task Collect([Body(BodySerializationMethod.UrlEncoded)] Dictionary data); } var data = new Dictionary { {"v", 1}, {"tid", "UA-1234-5"}, {"cid", new Guid("d1e9ea6b-2e8b-4699-93e0-0bcbd26c206c")}, {"t", "event"}, }; // Serialized as: v=1&tid=UA-1234-5&cid=d1e9ea6b-2e8b-4699-93e0-0bcbd26c206c&t=event await api.Collect(data); ``` -------------------------------- ### Collections as Querystring Parameters Source: https://reactiveui.github.io/refit Configures how collections are formatted in the query string using CollectionFormat.Multi or CollectionFormat.Csv. ```csharp [Get("/users/list")] Task Search([Query(CollectionFormat.Multi)]int[] ages); Search(new [] {10, 20, 30}) >>> "/users/list?ages=10&ages=20&ages=30" [Get("/users/list")] Task Search([Query(CollectionFormat.Csv)]int[] ages); Search(new [] {10, 20, 30}) >>> "/users/list?ages=10%2C20%2C30" ```