Refit is a library heavily inspired by Square's Retrofit library, and it turns your REST API into a live interface:
public interface IGitHubApi
{
[Get("/users/{user}")]
Task<User> GetUser(string user);
}
The RestService
class generates an implementation of IGitHubApi
that uses
HttpClient
to make its calls:
var gitHubApi = RestService.For<IGitHubApi>("https://api.g
8000
ithub.com");
var octocat = await gitHubApi.GetUser("octocat");
Refit currently supports the following platforms:
- Xamarin.Android
- Xamarin.Mac
- Desktop .NET 4.5
- Windows Phone 8
- Silverlight 5
Support for the following platforms is coming soon:
- Xamarin.iOS
- Windows Store (WinRT)
Every method must have an HTTP attribute that provides the request method and relative URL. There are five built-in annotations: Get, Post, Put, Delete, and Head. The relative URL of the resource is specified in the annotation.
[Get("/users/list")]
You can also specify query parameters in the URL:
[Get("/users/list?sort=desc")]
A request URL can be updated dynamically using replacement blocks and parameters on the method. A replacement block is an alphanumeric string surrounded by { and }.
If the name of your parameter doesn't match the name in the URL path, use the
AliasAs
attribute.
[Get("/group/{id}/users")]
Task<List<User>> GroupList([AliasAs("id")] int groupId);
Parameters that are not specified as a URL substitution will automatically be used as query parameters. This is different than Retrofit, where all parameters must be explicitly specified.
One thing that is important to note, is that URL paths must be lowercase.
URLs are case-insensitive in the HTTP spec, so we reflect that by being picky
about case
However, the comparison between parameter name and URL parameter is not
case-sensitive, so it will work correctly if you name your parameter groupId
in the path /group/{groupid}/show
for example.
[Get("/group/{id}/users")]
Task<List<User>> GroupList([AliasAs("id")] int groupId, [AliasAs("sort")] string sortOrder);
GroupList(4, "desc");
>>> "/group/4/users?sort=desc"
One of the parameters in your method can be used as the body, by using the Body attribute:
[Post("/users/new")]
Task CreateUser([Body] User user);
There are three possibilities for supplying the body data, depending on the type of the parameter:
- If the type is
Stream
, the content will be streamed viaStreamContent
- If the type is
string
, the string will be used directly as the content - For all other types, the object will be serialized as JSON.
You can set one or more static request headers for a request applying a Headers
attribute to the method:
[Headers("User-Agent: Awesome Octocat App")]
[Get("/users/{user}")]
Task<User> GetUser(string user);
Static headers can also be added to every request in the API by applying the
Headers
attribute to the interface:
[Headers("User-Agent: Awesome Octocat App")]
public interface IGitHubApi
{
[Get("/users/{user}")]
Task<User> GetUser(string user);
[Post("/users/new")]
Task CreateUser([Body] User user);
}
If the content of the header needs to be set at runtime, you can add a header
with a dynamic value to a request by applying a Header
attribute to a parameter:
[Get("/users/{user}")]
Task<User> GetUser(string user, [Header("Authorization")] string authorization);
// Will add the header "Authorization: token OAUTH-TOKEN" to the request
var user = await GetUser("octocat", "token OAUTH-TOKEN");
Unlike Retrofit, where headers do not overwrite each other and are all added to the request regardless of how many times the same header is defined, Refit takes a similar approach to the approach ASP.NET MVC takes with action filters — redefining a header will replace it, in the following order of precedence:
Headers
attribute on the interface (lowest priority)Headers
attribute on the methodHeader
attribute on a method parameter (highest priority)
[Headers("X-Emoji: :rocket:")]
public interface IGitHubApi
{
[Get("/users/list")]
Task<List> GetUsers();
[Get("/users/{user}")]
[Headers("X-Emoji: :smile_cat:")]
Task<User> GetUser(string user);
[Post("/users/new")]
[Headers("X-Emoji: :metal:")]
Task CreateUser([Body] User user, [Header("X-Emoji")] string emoji);
}
// X-Emoji: :rocket:
var users = await GetUsers();
// X-Emoji: :smile_cat:
var user = await GetUser("octocat");
// X-Emoji: :trollface:
await CreateUser(user, ":trollface:");
Headers defined on an interface or method can be removed by redefining
a static header without a value (i.e. without : <value>
) or passing null
for
a dynamic header. Empty strings will be included as empty headers.
[Headers("X-Emoji: :rocket:")]
public interface IGitHubApi
{
[Get("/users/list")]
[Headers("X-Emoji")] // Remove the X-Emoji header
Task<List> GetUsers();
[Get("/users/{user}")]
[Headers("X-Emoji:")] // Redefine the X-Emoji header as empty
Task<User> GetUser(string user);
[Post("/users/new")]
Task CreateUser([Body] User user, [Header("X-Emoji")] string emoji);
}
// No X-Emoji header
var users = await GetUsers();
// X-Emoji:
var user = await GetUser("octocat");
// No X-Emoji header
await CreateUser(user, null);
// X-Emoji:
await CreateUser(user, "");
Note that in Refit unlike in Retrofit, there is no option for a synchronous
network request - all requests must be async, either via Task
or via
IObservable
. There is also no option to create an async method via a Callback
parameter unlike Retrofit, because we live in the async/await future.
Similarly to how body content changes via the parameter type, the return type will determine the content returned.
Returning Task without a type parameter will discard the content and solely tell you whether or not the call succeeded:
[Post("/users/new")]
Task CreateUser([Body] User user);
// This will throw if the network call fails
await CreateUser(someUser);
If the type parameter is 'HttpResponseMessage' or 'string', the raw response message or the content as a string will be returned respectively.
// Returns the content as a string (i.e. the JSON data)
[Get("/users/{user}")]
Task<string> GetUser(string user);
// Returns the raw response, as an IObservable that can be used with the
// Reactive Extensions
[Get("/users/{user}")]
IObservable<HttpResponseMessage> GetUser(string user);
Currently Refit is missing the following features from Retrofit that are planned for a future release soon:
- Multipart requests (including file upload)
- Form posts