# Tiny.RestClient: a REST client for consuming your APIs

> I'm very happy to present a project I've been working on for a while: a fully fluid .NetStandard compatible REST client. The code is available on GitHub. And the…

- Author: Jérôme Giacomini
- Language: en
- Canonical URL: [Tiny.RestClient: a REST client for consuming your APIs](https://jeromegiacomini.net/articles/2018/09/15/tiny-restclient-a-rest-client-for-consuming-your-apis)
- Published: 2018-09-15
- Last modified: 2026-09-05
- Topics: .NET, C#, Xamarin, Windows

I'm very happy to present a project I've been working on for a while: a fully fluid .NetStandard compatible REST client.

The code is available on [GitHub](https://github.com/jgiacomini/Tiny.RestClient).

And the software library is on [Noise](https://www.nuget.org/packages/Tiny.RestClient/).

You want to talk about it? [Gitter](https://gitter.im/Tiny-RestClient/Lobby)

The main client objective is to have an optimized and easy to use REST client.

It is compatible with .NET Standard 1.3 and 2.0 which means that it can be used on .NET Framework 4.5 or higher, Xamarin and .NET Core and UWP.

Functionalities:

\* A modern client for making asynchronous calls for REST APIs

\* Support for the main HTTP verbs: GET, POST, PUT, DELETE, PATCH

\* Support for personalized verbs

\* Support for cancellation token on all types of requests

\* Automatic detection of the deserializer to be used

\* Support for serialization/deserialization of JSON and XML

\* Customized serial/deserial support

\* Support for multi-part form (sending multiple files)

\* Optimized HTTP calls

\* Easier to interpret typed exceptions

\* Provides a simple way to log all queries: the answers that fail, the response time...

\* Possibility to set the timeout overall or by request

\* LAPIAPI launches a Timeout exception when requests are in timeout (default HttpClient API launches an OperationCancelledException exception which makes it impossible to differentiate between a token cancellation and a timeout)

\* Allows to export requests as a Postman collection

## Creating a customer

```csharp

using Tiny.RestClient;
var client = new TinyRestClient("http://MyAPI.com/api", new HttpClient());

```


## Headers

### Add a default header for all requests

```csharp

// Add default header for each calls
client.Settings.DefaultHeaders.Add("Token", "MYTOKEN");

```


### Add a header for the current request

```csharp

// Add header for this request only
client.GetRequest("City/All").
      AddHeader("Token", "MYTOKEN").
      ExecuteAsync();

```


### Read the answer headers

```csharp

await client.GetRequest("City/All").
             FillResponseHeaders(out headersOfResponse Headers).
             ExecuteAsync();
foreach(var header in headersOfResponse)
{
    Debug.WriteLine($"{current.Key}");
    foreach (var item in current.Value)
    {
        Debug.WriteLine(item);
    }
}

```


## Creating a GET query

```csharp

var cities = client.GetRequest("City/All").ExecuteAsync<List<City>>();
// GET http://MyAPI.com/api/City/All an deserialize automaticaly the content

// Add a query parameter
var cities = client.
    GetRequest("City").
    AddQueryParameter("id", 2).
    AddQueryParameter("country", "France").
    ExecuteAsync<City>> ();
// GET http://MyAPI.com/api/City?id=2&country=France and deserialize automaticaly the content

```


## Creating a POST query

```csharp

// POST
 var city = new City() { Name = "Paris" , Country = "France"};

// With content
var response = await client.PostRequest("City", city).
                ExecuteAsync<bool>();
// POST http://MyAPI.com/api/City with city as content

// With form url encoded data
var response = await client.
                PostRequest("City/Add").
                AddFormParameter("country", "France").
                AddFormParameter("name", "Paris").
                ExecuteAsync<Response>();
// POST http://MyAPI.com/api/City/Add with from url encoded content


var fileInfo = new FileInfo("myTextFile.txt");
var response = await client.
                PostRequest("City/Image/Add").
                AddFileContent(fileInfo, "text/plain").
                ExecuteAsync<Response>();
// POST text file at http://MyAPI.com/api/City/Add 

```


## Create a query with a custom HTTP verb

```csharp

 await client.
       NewRequest(new System.Net.Http.HttpMethod("HEAD"), "City").
       ExecuteAsync();

```


## Defining the timeout

### Defining a global timeout

```csharp

client.Settings.DefaultTimeout = TimeSpan.FromSeconds(100);

```


### Set the timeout for a request

```csharp

request.WithTimeout(TimeSpan.FromSeconds(100));

```


## Downloading a file

```csharp

string filePath = "c:\map.pdf";
FileInfo fileInfo = await client.
                GetRequest("City/map.pdf").
                DownloadFileAsync("c:\map.pdf");
// GET http://MyAPI.com/api/City/map.pdf 

```


## How to recover an HttpResponse message

```csharp

var response = await client.
                PostRequest("City/Add").
                AddFormParameter("country", "France").
                AddFormParameter("name", "Paris").
                ExecuteAsHttpResponseMessageAsync();
// POST http://MyAPI.com/api/City/Add with from url encoded content

```


## Read an answer as String

```csharp

string response = await client.
                GetRequest("City/All").
                ExecuteAsStringAsync();

```


## Making multi-part requests

Creating multiparty queries is very simple with the RestClient.

```csharp

// With 2 json content
var city1 = new City() { Name = "Paris" , Country = "France"};
var city2 = new City() { Name = "Ajaccio" , Country = "France"};
var response = await client.NewRequest(HttpVerb.Post, "City").
await client.PostRequest("MultiPart/Test").
              AsMultiPartFromDataRequest().
              AddContent<City>(city1, "city1", "city1.json").
              AddContent<City>(city2, "city2", "city2.json").
              ExecuteAsync();


// With 2 byte array content
byte[] byteArray1 = ...
byte[] byteArray2 = ...           
              
await client.PostRequest("MultiPart/Test").
              AsMultiPartFromDataRequest().
              AddByteArray(byteArray1, "request", "request2.bin").
              AddByteArray(byteArray2, "request", "request2.bin")
              ExecuteAsync();
  

// With 2 streams content        
Stream1 stream1 = ...
Stream stream2 = ...         
await client.PostRequest("MultiPart/Test").
              AsMultiPartFromDataRequest().
              AddStream(stream1, "request", "request2.bin").
              AddStream(stream2, "request", "request2.bin")
              ExecuteAsync();
              
              
// With 2 files content           

var fileInfo1 = new FileInfo("myTextFile1.txt");
var fileInfo2 = new FileInfo("myTextFile2.txt");

var response = await client.
                PostRequest("City/Image/Add").
                AsMultiPartFromDataRequest().
                AddFileContent(fileInfo1, "text/plain").
                AddFileContent(fileInfo2, "text/plain").
                ExecuteAsync<Response>();

// With mixed content                  
await client.PostRequest("MultiPart/Test").
              AsMultiPartFromDataRequest().
              AddContent<City>(city1, "city1", "city1.json").
              AddByteArray(byteArray1, "request", "request2.bin").
              AddStream(stream2, "request", "request2.bin")
              ExecuteAsync();

```


## Streams and byte[]

You can make queries with content like a stream or bytes array.

If you use these methods, no serialisers will be used.

### Streams

**Read a stream-like response:**

```csharp

// Read stream response
 Stream stream = await client.
              GetRequest("File").
              ExecuteAsStreamAsync();

```


**Send content of the Stream type:**

```csharp

// Post Stream as content
await client.PostRequest("File/Add").
            AddStreamContent(stream).
            ExecuteAsync();

```


### byte[]

**Read a byte response[]:**

```csharp

// Read byte array response         
byte[] byteArray = await client.
              GetRequest("File").
              ExecuteAsByteArrayAsync();

<em><strong>Envoyer un contenu du type byte[] :</strong></em>

// Send bytes array as content
await client.
            PostRequest("File/Add").
            AddByteArrayContent(byteArray).
            ExecuteAsync();

```


## Error management:

Applications may introduce four types of exemptions:

 \* ConnectionException: Launched when the request cannot reach the server

 \* HttpException: Launched when the request reached the server but the StatusCode is invalid (404, 500...)

 \* SerializeException: Launched when the serializer cannot serialize content

 \* DeserializeException: Launched when the deserializer cannot deserialize the response

 \* TimeoutException: Launched when the request takes too long to execute

### Catch up with a specific Status code

```csharp

string cityName = "Paris";
try
{ 
   var response = await client.
     GetRequest("City").
     AddQueryParameter("Name", cityName).
     ExecuteAsync<City>();
}
catch (HttpException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
{
   throw new CityNotFoundException(cityName);
}
catch (HttpException ex) when (ex.StatusCode == System.Net.HttpStatusCode.InternalServerError)
{
   throw new ServerErrorException($"{ex.Message} {ex.ReasonPhrase}");
}

```


## Trainer

By default:

- The Json trainer is used by default.

- An Xml trainer is added to the list of supported trainers

Each trainer has a list of "Supported Media Types". This allows the RestClient to detect which trainer will be used.

If no trainer matches, he will use the default trainer.

### Adding a new trainer

**Add a trainer as the default trainer.**

```csharp

bool isDefaultFormatter = true;
var customFormatter = new CustomFormatter();
client.Settings.Formatters.Add(customFormatter, isDefaultFormatter);

```


**Removing a trainer:**

```csharp

var lastFormatter = client.Settings.Formatters.Where( f=> f is XmlSerializer>).First();
client.Remove(lastFormatter);

```


### Define a serial for the current request:

```csharp

IFormatter serializer = new XmlFormatter();
 var response = await client.
     PostRequest("City", city, serializer).
     ExecuteAsync();

```


### Define a deserializer for the current request:

```csharp

IFormatter deserializer = new XmlFormatter();

 var response = await client.
     GetRequest("City").
     AddQueryParameter("Name", cityName).
     ExecuteAsync<City>(deserializer);

```


### Personalised trainer:

You can create your own trainer by implementing the IFormatter interface.

For example, the XMLFormatter implementation is as follows:

```csharp

public class XmlFormatter : IFormatter
{

   public string DefaultMediaType => "application/xml";

   public IEnumerable<string> SupportedMediaTypes
   {
      get
      {
         yield return "application/xml";
         yield return "text/xml";
      }
   }

   public T Deserialize<T>(Stream stream, Encoding encoding)
   {
      using (var reader = new StreamReader(stream, encoding))
      {
         var serializer = new XmlSerializer(typeof(T));
         return (T)serializer.Deserialize(reader);
      }
   }

   public string Serialize<T>(T data, Encoding encoding)
   {
         if (data == default)
         {
             return null;
         }

         var serializer = new XmlSerializer(data.GetType());
         using (var stringWriter = new DynamicEncodingStringWriter(encoding))
         {
            serializer.Serialize(stringWriter, data);
            return stringWriter.ToString();
         }
      }
   }

```


## Listener

You can simply add an IListener to "listen" to any queries/answers/exceptions received.

### Debug listener

A Debug listener comes with the software library.

To add it, just call the AddDebug method on the property **Listener**.

```csharp

client.Settings.Listeners.AddDebug();

```


You can also create your own listener by implementing the IListener interface.

### Postman listener

To add a Postman listener you have to call the AddPostman method on the Listeners property

```csharp

PostManListerner listener = client.Settings.Listeners.AddPostman("nameOfCollection");

```


You can save the Postman collection by calling SaveAsync from the Postman listener.

```csharp

await listener.SaveAsync(new FileInfo("postManCollection.json");

```


If you only want the Json of the collection, you can call the GetCollectionJson method

```csharp

await listener.GetCollectionJson();

```


### Adding a custom listener

```csharp

IListener myCustomListerner = ..
client.Settings.Listeners.Add(myCustomListerner);

```


## To go further:

- The page [GitHub](https://github.com/jgiacomini/Tiny.RestClient).

- The page [Noise](https://www.nuget.org/packages/Tiny.RestClient/).

- The Cat [Gitter](https://gitter.im/Tiny-RestClient/Lobby)
