A lightweight library that is inpired in Angular 2+ Http Client built on top of .NET Http Client that help programmers to make asynchronous http requests.
If you are using Package Manager:
Install-Package Rx.Http -Version 2.1.0
If you are using .NET CLI
dotnet add package Rx.Http --version 2.1.0
using Rx.Http;
using System.Reactive.Linq;
public class Program
{
public static async void Main()
{
//Initialize the RxHttpClient
var http = RxHttpClient.Create();
//Retrieve a list of To-Do item and print the title of each element asynchronously
http.Get<List<Todo>>("https://jsonplaceholder.typicode.com/todos/").Subscribe(items => {
items.ForEach(item => Console.WriteLine(item.title));
});
//Making the same request using await
List<Todo> response = await http.Get<List<Todo>>("https://jsonplaceholder.typicode.com/todos/");
}
}
You can setup your request by using options. It make possible you set query strings, headers and your custom serializer and deserializer for your request and response.
http.Get<List<Todo>>("https://jsonplaceholder.typicode.com/todos/", options =>
{
options.AddHeader(new {
Authorization = "Bearer <token>"
Accept = "application/json"
})
.AddQueryString(new {
name = "John Doe",
index = 1
});
});
The media type represents a interface that is used to translate a mime type to a object (serializing and deserializing).
It's called when you provide a type when you call a request like that:
var url = "https://myapi.com/names/";
var parameters = new
{
Name = "Lucas"
};
http.Post<List<string>>(url, parameters)
// ^^^^^^^^^^^^^
In this example, you're sending an object which contains a property "Name" and a value "Lucas" and you're expecting to receive a List<string>
from server.
Suppose that the server only does accept XML and reply the request using the CSV format. So, we have to convert the "parameter" object to XML and convert the server reply to List<string>
right? That's why we have the interfaces IHttpMediaTypeSerializer and IHttpMediaTypeDeserializer.
You could create XmlHttpMediaType and CsvHttpMediaType which implements IHttpMediaTypeSerializer and IHttpMediaTypeDeserializer to solve this issue. The final code would be like that.
var url = "https://myapi.com/names/";
var parameters = new
{
Name = "Lucas"
};
http.Get<List<string>>(url, parameters, options =>
{
options.SetRequestMediaType(new XmlHttpMediaType())
.SetResponseMediaType(new CsvHttpMediaType())
});
RequestMediaType is used to serialize your body content when you are making a request. It's only called when you provide type on Generic. By default is used JsonHttpMediaType
ResponseMediaType is used to deserialize the response from server. By default is used JsonHttpMediaType
A consumer is defined as a service that have common behavior for the requests. You can encapsulate the logic of all those requests in a easy way. The main advantage of using consumers is to abstract the HTTP request and its implementation details, and only work with the results from it. The concept is very similar to FeignClient interface from Spring Cloud
The interceptors are a pre-processing unit that changes the request before it happens. It can be usefull set a standard for all requests.
In this example, we need to provide the api key for all requests to The Movie Database API
The code above shows how to use Consumers and Interceptors.
public class TheMovieDatabaseConsumer : RxHttpClient
{
public TheMovieDatabaseConsumer(HttpClient httpClient): base(httpClient, null)
{
httpClient.BaseAddress = new Uri(@"https://api.themoviedb.org/3/");
RequestInterceptors.Add(new TheMovieDatabaseInterceptor());
}
public IObservable<Movies> ListMovies() => Get<Movies>("movie/popular");
}
internal class TheMovieDatabaseInterceptor : RxRequestInterceptor
{
public void Intercept(RxHttpRequestOptions request)
{
request.AddQueryString("api_key", "eb7b25db28349bd4eef1498a5be9842f");
}
}
This exception is threw when the server reply with a HTTP Status different of 2xx. There's two ways to handle this exception:
var url = @"https://jsonplaceholder.typicode.com/this_page_dont_exist_hehehe/";
//With traditional try-catch block
try
{
var todos = await http.Get<List<Todo>>(url);
}
catch(RxHttpRequestException ex)
{
HttpResponseMessage response = ex.Response;
//...
}
//Or using reactive way
http.Get<List<Todo>>(url).Subscribe(response =>
{
//...
}, exception =>
{
HttpResponseMessage response = (exception as RxHttpRequestException)?.Response;
//...
})
Is strongly recommended to use DI (Dependency Injection) with Rx.Http, because of HttpClientFactory, that improves HttpClient performance. For that, you must do the following:
public void ConfigureServices(ServiceCollection services)
{
services.UseRxHttp();
services.AddSingleton<JsonPlaceHolderConsumer>();
}
You can implement your own custom logging mechanism by implementing the interface RxHttpLogger. We provide a built-in logging mechanism called "RxHttpDefaultLogger". In case you don't have Microsoft.Extensions.Logging added on your project you can use RxHttpConsoleLogger
Here is a example that show how to use RxHttpDefaultLogger mechanism. If you have a custom logging mechanism you must replace RxHttpDefaultLogging for your class implementation.
private static void ConfigureServices(ServiceCollection services)
{
services.AddRxHttpLogger<RxHttpDefaultLogger>();
}
You can setup default settings by setting the RxHttp.Default
like the example below:
RxHttp.Default.RequestMediaType = new JsonHttpMediaType(new NewtonsoftJsonSerializer());
RxHttp.Default.ResponseMediaType = new JsonHttpMediaType(new NewtonsoftJsonSerializer())
You can also download a file using Rx.Http with a code like that:
var fileName = $"mysql-installer-web-community-8.0.22.0.msi";
var directory = Directory.GetCurrentDirectory();
var path = Path.Combine(directory, fileName);
await http.Get($@"https://dev.mysql.com/get/Downloads/MySQLInstaller/{fileName}")
.ToFile(path); // Save response to path (download)
The navigator works like a RxHttpClient, but it manage cookies automatically. This is useful when you want to keep session information when you make your requests. Suppose that mysite handles the session with cookies and you want to keep your session, so you can do like that:
var navigator = RxNavigator.Create();
navigator.Post("https://www.mysite.com/login", new
{
Login = "myLogin",
Password = "myPass"
});
navigator.Post("https://www.mysite.com/create/task", new
{
Title = "myTitle",
Description = "myDescription",
Date = DateTime.Now
});
Note that you did nothing about cookies but could call the endpoint to create a new task using this session.
Suppose that you need to get a specific value from a HTML Element (for example, a label, a link or a div). Rx.Http is integrated with HtmlAgilityPack.NetCore. It can be done by calling the Extension Method called AsHtmlDocument()
.
π₯ Pro Tip: It can be used with RxNavigator
to automatize some tasks!
- Reactive GET, POST, PUT DELETE Http Methods
- Built-in JSON serializing and deserializing (using Newtonsoft.Json)
- Support for custom serializing and deserializing
- Logging
- Consumers and Interceptors implementation
- Error handling
- Native support for x-www-form-urlencoded and form-data
- Global settings
- ASP.Net Core native integration (Dependency Injection)
- Save response to file (download)
- Provide a alternative for built-in Json serializer: System.Text.Json.JsonSerializer of .NET Core 3
- Implement cancellation token funcionality