Access your WebAPI fluently using Bolt.RestClient

Bolt.RestClient library allow you to access web api fluently. Here are some of the features

  • Fluent interface
  • Async support
  • Retry on failure
  • Can report time taken for each request

Lets see some code that illustrates how to use RestClient. Most probably you want to use restclient in your ioc container. Make sure this instance setup as singleton in your DI framework.

var client = RestClientBuilder.New()
                .WithSerializer(new JsonSerializer())
                .Build();

RestClient does not provide any serializer but an interface. So you need to implement an instance of ISerializer and pass that as “WithSerializer” of RestClientBuilder. Alternatively you can use Bolt.Serializers.Json nuget package that has an implementation of ISerializer using Json.Net Library.
Now you have an instance of IRestClient and you can now use this to do get/post/put/delete requests.

Lets say I have an webapi that returns list of books. Here is an example how you can get books list from that Api in your application.

Example of Get Request


var response = client.For("http://localhost:8090/api/v1/books")
					.AcceptJson()
					.Get<IEnumerable<Book>>();

if(response.Succeed)
{
	var books = response.Output;
}
else
{
	logger.Error("Response failed with statuscode {0}", response.StatusCode);
}

Example of Async Get Request

Every HttpMethod has an equivalant async version also. e.g GetAsync, PostAsync, PutAsync, DeleteAsync.


var response = await client.For("http://localhost:8090/api/v1/books")
					.AcceptJson()
					.GetAsync<IEnumerable<Book>>();

if(response.Succeed)
{
	var books = response.Output;
}
else
{
	logger.Error("Response failed with statuscode {0}", response.StatusCode);
}

Example of Post Request

var response = await client.For("http://localhost:8090/api/v1/books")
				.AcceptJson()
				.PostAsync(new Book
				{
				    Title = "Test Books"
				});

if(response.Succeed)
{
	var location = response.Location;
}
else
{
	logger.Error("Response failed with statuscode {0}", response.StatusCode);
}

Example of post request that return data

var response = await client.For("http://localhost:8090/api/v1/books")
				.AcceptJson()
				.PostAsync<Book,int>(
                    new Book
                    {
                        Title = "Test Books"
                    });

if(response.Succeed){
	var newBookId = response.Output;
}
else{
	logger.Error("Response failed with statuscode {0}", response.StatusCode);
}

You can define how many times it should retry on failure and timeout of request as below:

var response = await client.For("http://localhost:8090/api/v1/books/{0}", 1001)
				.AcceptJson()
				.RetryOnFailure(3)
				.Timeout(TimeSpan.FromMilliseconds(500))
				.Get<Book>();

Get Errors on bad request

RestClient will provide the list of errors but that depends on how you return you errors from api. For example if your api return errors on bad request as below, then you don’t need to do anything. you will get your errors by response.Errors property.

{
	errors: [
		{
			propertName: "Email",
			errorMessage: "Email is required",
			errorCode: 1001 // not mandatory
		},
		{
			propertName: "Password",
			errorMessage: "password cannot be less than 6 characters long",
			errorCode: 1002 // not mandatory
		},
		{
			propertName: "Password",
			errorMessage: "password is not strong enough",
			errorCode: 1002 // not mandatory
		}
	]
}

You can then get all errors when bad request returned as below:

var response = await client.For("http://localhost:8090/api/v1/books")
				.AcceptJson()
				.PostAsync(
                    new Book
                    {
                        Title = "Test Books"
                    });

if(!response.Succeed){
	var errors = response.Errors;
}

But if you are returning errors in different format then you have to implement your own error loader as below or you can just implement IErrorLoader. For example your api returning error as a list of string.


public class SampleErrorLoader : ErrorLoader
{
    public SampleErrorLoader(ILogger logger) :base(logger)
    {
        
    }

    protected override IEnumerable<Error> DeserializeError<TRestResponse>(TRestResponse restResponse, ISerializer serializer)
    {
        var errors = serializer.Deserialize<List<string>>(restResponse.RawBody);

        return errors.Select(x => new Error
        {
            ErrorMessage = x
        });
    }
}

// How to use custom error loader.
var client = RestClientBuilder.New()
                .WithSerializer(new JsonSerializer())
                .WithErrorLoader(new SampleErrorLoader(new NullLogger()))
                .Build();

Build complex request URL with proper encoding

The library also includes a utility named “UrlBuilder” that can be used to build complex url with proper encoding of values. Lets see an example


var url = UrlBuilder.Host("http://www.testapi.com.au/")
                .Route("/members/{0}/state?", "test name")
                .QueryParam("active", true)
                .QueryParam("max", 1)
                .QueryParam("q", "j+k l&?,");

// result url: http://www.testapi.com.au/members/test%20name/state?active=True&max=1&q=j%2Bk%20l%26%3F%2C

var response = await client.For(url)
				.AcceptJson().Get<Data>();

Log/Monitor how long each requests took

You can also log/monitor how long each of your requests took. To do this you need to implement IReportTimeTaken interface and add that in restclient declaration. Here’s an example:


public class NlogReportTimeTaken : IReportTimeTaken
{
    private readonly ILogger _logger; // Pass your own ILogger

    public NlogReportTimeTaken(ILogger logger)
    {
        _logger = logger;
    }

    public void Notify(RestRequest request, TimeSpan timeTaken)
    {
        _logger.Trace("{0} : {1} took {2}ms", request.Method, request.Url, timeTaken.TotalMilliseconds);
    }
}

// Set this in RestClient as below
RestClientBuilder.New()
                .WithSerializer(new JsonSerializer())
                .WithTimeTakenNotifier(new NlogReportTimeTaken(LoggerFactory.Create<NlogReportTimeTaken>()))
                .Build()

That means now if you set your log level to trace all requests will be logged with time taken to execute as below:

// Sample log output
2015-02-08 22:02:52.8206 Bolt.RestClient.IntegrationTests.Helpers.NlogReportTimeTaken GET : http://localhost:8090/api/v1/books took 2.8772ms
2015-02-08 22:02:52.8206 Bolt.RestClient.IntegrationTests.Helpers.NlogReportTimeTaken GET : http://localhost:8090/api/v1/books took 2.9174ms
2015-02-08 22:02:52.8366 Bolt.RestClient.IntegrationTests.Helpers.NlogReportTimeTaken GET : http://localhost:8090/api/v1/books/9999 took 6.653ms

Hope you find this library useful.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s