71 / Rest.Fody

A multi-platform REST client powered by Fody, and heavily inspired by paulcbetts's Refit and canton7's RestEase.
MIT License
17 stars 0 forks source link

Rest.Fody

A Fody addin, heavily inspired by Refit and RestEase.
Thankfully, the source code for ReactiveUI.Fody was easy to understand, and greatly helped me.

Attention

This project is no longer maintained, and is not expected to work.

Basic syntax

[ServiceFor("http://example.com/api/v2")]
public class API
{
    [Get("/user")]
    public extern async Task<MyUser> GetUser();
}

Source structure

Rest.Fody

All the weaving code.

Rest.Fody.Portable

All the attributes.

Rest.Fody.Tests

Some tests, with Shouldly.

Shared

Code shared between Rest.Fody.Portable and Rest.Fody.

API

Basic anatomy of a class

[ServiceFor("http://example.com/api/v1")]
public class API
{
    public API()
    {
    }

    [Get("/user")]
    public extern Task<User> GetUser();
}

A class will be recognized and modified by Rest.Fody if either:

In the first case, a private HttpClient field will be created, and be used internally.
With the [ServiceFor(URL)] attribute, it is also possible to specify custom headers, via the [Header(name, value)] attribute.

Making requests

[Get("/")]
public extern Task CheckInternetConnection();

A request must be marked extern, and return either a Task, a Task<T>, or a IObservable<T> (in which case System.Reactive.Linq must be referenced).
On failure, a request will throw a RestException, which contains a HttpResponseMessage.

Deserialization / Serialization

To free itself of any dependency besides Fody and Mono.Cecil, Rest.Fody will not be able to deserialize or serialize types besides all numeric types, Stream, string and byte[]. When deserializing, HttpResponseMessage is also accepted.

To add your own (de)serialization, declare one of those methods with the [RestSerialize] or [RestDeserialize] attribute:

Valid implementations:

[Service]
public class API
{
    [RestDeserializer] private T Deserialize<T>(string str) { ... }
    [RestDeserializer] private T Deserialize<T>(byte[] buf) { ... }
    [RestSerializer] private string Serialize(object o) { ... }
    [RestSerializer] private byte[] Serialize(object o) { ... }
}

or

public class Utils
{
    [RestDeserializer] public static T Deserialize<T>(string str) { ... }
    [RestDeserializer] public static T Deserialize<T>(byte[] buf) { ... }
    [RestSerializer] public static string Serialize(object o) { ... }
    [RestSerializer] public static byte[] Serialize(object o) { ... }
}

The instance methods will be chosen in priority, but will fallback to the static methods if needed.

Query, dynamic url

[Get("/todo")]
public extern Task<List<Todo>> GetTodos([Query] int offset, [Query] int count);

[Post("/todo/{todoId}")]
public extern Task<Todo> SaveTodo(string todoId);

[Delete("/todo/{todoId}")]
public extern Task<Todo> DeleteTodo([Alias("todoId")] string id, [Query] string @if);

Four ways to specify query parameters:

If the name of the query starts with a '@', it will be removed.

Two ways to change a dynamic url:

Body

[Put("/todo/{todoId}")]
public extern Task<Todo> UpdateTodo(string todoId, [Body] Todo todo);

The body of the request must be unique, and marked with the [Body] attribute.

Headers

[ServiceFor("http://example.com/api/v1")]
[Header("Authorization", "Bearer xxx")]
public class API
{
    [Header("Authorization")]
    public extern Task DoSomethingWithoutAuthenticating([Header("X-Client")] string client);
}

Headers can be specified on both classes, methods and parameters:

Note: Default headers specified on a class will be ignored if the class provides its own HttpClient.
Note: A [Headers] attribute is valid on parameters that implements IDictionary<string, string>.

Options

In FodyWeaver.xml, options can be passed to Rest.Fody via XML (sorry, those options have very long name).

Misc