Easy-to-use typesafe REST API client library for .NET Standard 1.1 and .NET Framework 4.5 and higher, which is simple and customisable. Inspired by Refit
Easy-to-use typesafe REST API client library for .NET Standard 1.1 and .NET Framework 4.5 and higher, which is simple and customisable. Inspired by Refit
RestEase is a little type-safe REST API client library for .NET Framework 4.5.2 and higher and .NET Platform Standard 1.1 and higher, which aims to make interacting with remote REST endpoints easy, without adding unnecessary complexity.
To use it, you define an interface which represents the endpoint you wish to communicate with (more on that in a bit), where methods on that interface correspond to requests that can be made on it. RestEase will then generate an implementation of that interface for you, and by calling the methods you defined, the appropriate requests will be made. See Installation and Quick Start to get up and running!
Almost every aspect of RestEase can be overridden and customized, leading to a large level of flexibility.
It also works on platforms which don't support runtime code generation, such as .NET Native and iOS, if you reference RestEase.SourceGenerator. See Using RestEase.SourceGenerator for more information.
RestEase is built on top of HttpClient and is deliberately a "leaky abstraction": it is easy to gain access to the full capabilities of HttpClient, giving you control and flexibility, when you need it.
RestEase is inspired by Anaïs Betts' Refit, which in turn is inspired by Retrofit.
RestEase is available on NuGet. See that page for installation instructions.
If you're using C# 9 or .NET 5 (or higher), reference RestEase.SourceGenerator as well to get compile-time errors and faster execution. See Using RestEase.SourceGenerator for more information. If you're targetting iOS or .NET Native, you will need to do this, as runtime code generation isn't available.
If you're using ASP.NET Core, take a look at Using HttpClientFactory. For failure handling and retries using Polly, see Using RestEase with Polly.
To start, first create an public interface which represents the endpoint you wish to make requests to. Please note that it does have to be public, or you must add RestEase as a friend assembly, see Interface Accessibility below.
…
See the [Get("path")] attribute used above? That's how you mark that method as being a GET request. There are a number of other attributes you can use here - in fact, there's one for each type of request: [Get("path")], [Post("path")], [Put("path")], [Delete("path")], [Head("path")], [Options("path")], [Trace("path"))], [Patch("path")]. Use whichever one you need to.
The argument to [Get] (or [Post], or whatever) is typically a relative path, and will be relative to the base uri that you provide to RestClient.For. (You can specify an absolute path here if you need to, in which case the base uri will be ignored). Also see the section on Paths.
Your interface methods may return one of the following types:
Task: This method does not return any data, but the task will complete when the request has completedTask (where T is not one of the types listed below): This method will deserialize the response into an object of type T, using Json.NET (or a custom deserializer, see Controlling Serialization and Deserialization below).Task: This method returns the raw response, as a string (although this can be customised, see here).Task: This method returns the raw HttpResponseMessage resulting from the request. It does not do any deserialiation. You must dispose this object after use.Task>: This method returns a Response. A Response contains both the deserialied response (of type T), but also the HttpResponseMessage. Use this when you want to have both the deserialized response, and access to things like the response headers. You must dispose this object after use.Task: This method returns a Stream containing the response. Use this to e.g. download a file and stream it to disk. You must dispose this object after use.Non-async methods are not supported (use .Wait() or .Result as appropriate if you do want to make your request synchronous).
If you return a Task or a Task, then HttpCompletionOption.ResponseHeadersRead is used, so that you can choose whether or not the response body should be fetched (or report its download progress, etc). If however you return a Task, Task, or Task>, then HttpCompletionOption.ResponseContentRead is used, meaning that any CancellationToken that you pass will cancel the body download.
If you return a Task, then the response body isn't fetched, unless an ApiException is thrown.
It is very common to want to include query parameters in your request (e.g. /foo?key=value), and RestEase makes this easy.
The most basic type of query parameter is a constant - the value never changes. For these, simply put the query parameter as part of the URL:
public interface IGitHubApi
{
[Get("users/list?sort=desc")]
Task> GetUsersAsync();
}
Any parameters to a method which are:
[Query] attribute, orwill be interpreted as query parameters.
The name of the parameter will be used as the key, unless an argument is passed to [Query("key")], in which case that will be used instead.
For example:
public interface IGitHubApi
{
[Get("user")]
Task FetchUserAsync(int userid);
// Is the same as:
[Get("user")]
Task FetchUserAsync([Query] int userid);
// Is the same as:
// (Note the casing of the parameter name)
[Get("user")]
Task FetchUserAsync([Query("userid")] int userId);
}
IGithubApi api = RestClient.For("http://api.github.com");
// Requests http://api.github.com/user?userid=3
await api.FetchUserAsync(3);
You can have duplicate keys if you want:
public interface ISomeApi
{
[Get("search")]
Task SearchAsync([Query("filter")] string filter1, [Query("filter")] string filter2);
}
ISomeApi api = RestClient.For("https://api.example.com");
// Requests http://somenedpoint.com/search?filter=foo&filter=bar
await api.SearchAsync("foo", "bar");
You can also have an array of query parameters:
public interface ISomeApi
{
// You can use IEnumerable, or any type which implements IEnumerable
[Get("search")]
Task SearchAsync([Query("filter")] IEnumerable filters);
}
ISomeApi api = RestClient.For("https://api.example.com");
// Requests http://api.exapmle.com/search?filter=foo&filter=bar&filter=baz
await api.SearchAsync(new[] { "foo", "bar", "baz" });
If you specify a key that is null, i.e. [Query(null)], then the name of the key is not used, and the value is inserted into the query string. If you specify a key that is an empty string "", then then query key will be left empty.
public interface ISomeApi
{
[Get("foo")]
Task Fo
No open issues yet, or sync has not completed.