Pages

Sunday, July 28, 2019

RestSharp Client in C#

My team has been doing a lot of REST API consumption, and our favorite tool to use for that is the terrific RestSharp Library. Our latest project is very large and uses lots of different APIs, and so we needed a way to create many different client classes that could consume many different APIs but still use common features like caching and error logging.

And so, I present to you, the Ultimate RestSharp Client!

In this post, we'll build a RestSharp client which can serve as a base class to many other clients who need to consume RESTful APIs. Said base class will need to implement common functionality (e.g. the aforementioned caching and error logging) and be able to deserialize known objects in which the APIs return. Come along with me as I condense a week's worth of research and coding into one 1700-word blog post and build the Ultimate RestSharp Client!

Requirements

The awesome RestSharp client that we're going to build needs to support the following scenarios:

  • Deserialize objects using an injected serializer.
  • Store into and retrieve from a cache.
  • Check for timeouts.
  • Use custom injected error logging.
 Prerequisites

Before we build our ultimate RestSharp client, let's first define the three items which need to be injected into the client.

Cache Service

Here's our simple cache service interface and implementation:

 

using System;
using System.Runtime.Caching;
 
public interface ICacheService
{
    T Get<T>(string cacheKey) where T : class;
    void Set(string cacheKey, object item, int minutes);
}
 
public class InMemoryCache : ICacheService
{
    public T Get<T>(string cacheKey) where T : class
    {
        return MemoryCache.Default.Get(cacheKey) as T;
    }
    public void Set(string cacheKey, object item)
    {
        if (item != null)
        {
            MemoryCache.Default.Add(cacheKey, item, DateTime.Now.AddMinutes(30));
        }
    }
}

 

 

Deserializer

As of recently, RestSharp no longer requires JSON.NET as its default serializer (see this readme). So, we must inject a serializer into our ultimate client, which will use JSON.NET anyway because that's what I like. I stole the following class from this fantastic blog post and it's worked pretty well for us.

 
using Newtonsoft.Json;
using RestSharp.Deserializers;
using RestSharp.Serializers;
using System.IO;
 
public class JsonSerializer : ISerializer, IDeserializer
{
    private readonly Newtonsoft.Json.JsonSerializer _serializer;
 
    public JsonSerializer()
    {
        ContentType = "application/json";
        _serializer = new Newtonsoft.Json.JsonSerializer
        {
            MissingMemberHandling = MissingMemberHandling.Ignore,
            NullValueHandling = NullValueHandling.Include,
            DefaultValueHandling = DefaultValueHandling.Include,
            DateFormatHandling=DateFormatHandling.IsoDateFormat,
            DateTimeZoneHandling=DateTimeZoneHandling.Unspecified
        };
    }
 
    public JsonSerializer(Newtonsoft.Json.JsonSerializer serializer)
    {
        ContentType = "application/json";
        _serializer = serializer;
    }
 
    public string Serialize(object obj)
    {
        using (var stringWriter = new StringWriter())
        {
            using (var jsonTextWriter = new JsonTextWriter(stringWriter))
            {
                jsonTextWriter.Formatting = Formatting.Indented;
                jsonTextWriter.QuoteChar = '"';
 
                _serializer.Serialize(jsonTextWriter, obj);
 
                var result = stringWriter.ToString();
                return result;
            }
        }
    }
 
    public string DateFormat { get; set; }
    public string RootElement { get; set; }
    public string Namespace { get; set; }
    public string ContentType { get; set; }
 
    public T Deserialize<T>(RestSharp.IRestResponse response)
    {
        var content = response.Content;
 
        using (var stringReader = new StringReader(content))
        {
            using (var jsonTextReader = new JsonTextReader(stringReader))
            {
                return _serializer.Deserialize<T>(jsonTextReader);
            }
        }
    }
}
 
 

Error Logger

This is the simplest of the three injection prerequisites. You will want to replace this implementation with one of your own design. Here's a very basic error logger interface and class:

public interface IErrorLogger
{
    void LogError(Exception ex, string infoMessage);
}
 
public class ErrorLogger : IErrorLogger
{
    public void LogError(Exception ex, string infoMessage)
    {
        //Log the error to your error database
    }
}
 
 

With all three prerequisite items in place, we can now start to code our ultimate RestSharp client!

Class and Constructor

In order to build our ultimate RestSharp client, we'll first need a class that inherits from RestSharp's RestClient class:

We also need a constructor to inject our three prerequisite classes into this client, as well as a base URL for which each child client will be using for their APIs. Such a constructor looks like this:

using RestSharp;
using RestSharp.Deserializers;
using System;
using System.Linq;
 
public class BaseClient : RestSharp.RestClient
{
    protected ICacheService _cache;
    protected IErrorLogger _errorLogger;
    public BaseClient(ICacheService cache, IDeserializer serializer, IErrorLogger errorLogger, string baseUrl)
    {
        _cache = cache;
        _errorLogger = errorLogger;
        AddHandler("application/json", serializer);
        AddHandler("text/json", serializer);
        AddHandler("text/x-json", serializer);
        BaseUrl = new Uri(baseUrl);
    }
}
 
 

Note that we are using JSON for this ultimate client. You can use other formats (e.g. XML), but I prefer JSON for this kind of client so that's what we'll use here.

Once we've got the constructor build, it's time to start coding the guts of our ultimate client, starting with...

Error Logging

We have two different kinds of error logging in this system: the generic error logging and the more specific Timeout checks. We implemented timeout checks in an earlier post, go check that out if you want more details. For now, here's some code to accomplish each of these.

 

private void LogError(Uri BaseUrl, IRestRequest request, IRestResponse response)
{
    //Get the values of the parameters passed to the API
    string parameters = string.Join(", ", request.Parameters.Select(x => x.Name.ToString() + "=" + ((x.Value == null) ? "NULL" : x.Value)).ToArray());
 
    //Set up the information message with the URL, the status code, and the parameters.
    string info = "Request to " + BaseUrl.AbsoluteUri + request.Resource + " failed with status code " + response.StatusCode + ", parameters: "
        + parameters + ", and content: " + response.Content;
 
    //Acquire the actual exception
    Exception ex;
    if (response != null && response.ErrorException != null)
    {
        ex = response.ErrorException;
    }
    else
    {
        ex = new Exception(info);
        info = string.Empty;
    }
 
    //Log the exception and info message
    _errorLogger.LogError(ex,info);
}
 
private void TimeoutCheck(IRestRequest request, IRestResponse response)
{
    if (response.StatusCode == 0)
    {
        LogError(BaseUrl, request, response);
    }
}

 

 

Get and Execute

Once we've got the error logging in place, we can now discuss the kinds of operations we want this client to be able to perform. In our client, there are three possible actions that the client can perform against the API:

A basic EXECUTE operation, which will return an IRestResponse, which is the generic Response interface defined by RestSharp.

GET operation, which will call the EXECUTE operations and return a deserialized class.

 GET FROM CACHE operation, which will check the cache service for a matching object, return that object if it exists, or call the EXECUTE operations if it doesn't.

EXECUTE

Because the GET operations will call the EXECUTE operations and will return deserialized objects, we need both generic and non-generic versions of the EXECUTE operations. Here's those EXECUTE operations, which override the underlying RestSharp methods and call the TimeoutCheck() method from earlier:

 

public override IRestResponse Execute(IRestRequest request)
{
    var response = base.Execute(request);
    TimeoutCheck(request, response);
    return response;
}
public override IRestResponse<T> Execute<T>(IRestRequest request)
{
    var response = base.Execute<T>(request);
    TimeoutCheck(request, response);
    return response;

}

 

 

GET

The GET operation is just a tiny bit more complex. It must check the status code of the response, and if that code is 200 OK it will deserialize an object; otherwise, it must log an error and return a default instance of the class it is trying to deserialize. We can do this via a generic method, like so:

public T Get<T>(IRestRequest request) where T : new()
{
    var response = Execute<T>(request);
    if (response.StatusCode == System.Net.HttpStatusCode.OK)
    {
        return response.Data;
    }
    else
    {
        LogError(BaseUrl,request,response);
        return default(T);
    }
}
 
 

GET FROM CACHE

The GET FROM CACHE methods are more complex still, though they really just add an extra step onto the GET operations:

public T GetFromCache<T>(IRestRequest request, string cacheKey) where T : class, new()
{
    var item = _cache.Get<T>(cacheKey);
    if (item == null) //If the cache doesn't have the item
    {
        var response = Execute<T>(request); //Get the item from the API call
        if (response.StatusCode == System.Net.HttpStatusCode.OK)
        {
            _cache.Set(cacheKey, response.Data); //Set that item into the cache so we can get it next time
            item = response.Data;
        }
        else
        {
            LogError(BaseUrl, request, response);
            return default(T);
        }
    }
    return item;
}
 
 

The Complete Client

For posterity's sake, here's the complete code for the ultimate RestSharp client:

 

using RestSharp;
using RestSharp.Deserializers;
using System;
using System.Linq;
 
public class BaseClient : RestSharp.RestClient
{
    protected ICacheService _cache;
    protected IErrorLogger _errorLogger;
    public BaseClient(ICacheService cache, IDeserializer serializer, IErrorLogger errorLogger, string baseUrl)
    {
        _cache = cache;
        _errorLogger = errorLogger;
        AddHandler("application/json", serializer);
        AddHandler("text/json", serializer);
        AddHandler("text/x-json", serializer);
        BaseUrl = new Uri(baseUrl);
    }
 
    private void TimeoutCheck(IRestRequest request, IRestResponse response)
    {
        if (response.StatusCode == 0)
        {
            LogError(BaseUrl, request, response);
        }
    }
 
    public override IRestResponse Execute(IRestRequest request)
    {
        var response = base.Execute(request);
        TimeoutCheck(request, response);
        return response;
    }
    public override IRestResponse<T> Execute<T>(IRestRequest request)
    {
        var response = base.Execute<T>(request);
        TimeoutCheck(request, response);
        return response;
    }
 
    public T Get<T>(IRestRequest request) where T : new()
    {
        var response = Execute<T>(request);
        if (response.StatusCode == System.Net.HttpStatusCode.OK)
        {
            return response.Data;
        }
        else
        {
            LogError(BaseUrl,request,response);
            return default(T);
        }
    }
 
    public T GetFromCache<T>(IRestRequest request, string cacheKey) where T : class, new()
    {
        var item = _cache.Get<T>(cacheKey);
        if (item == null)
        {
            var response = Execute<T>(request);
            if (response.StatusCode == System.Net.HttpStatusCode.OK)
            {
                _cache.Set(cacheKey, response.Data);
                item = response.Data;
            }
            else
            {
                LogError(BaseUrl, request, response);
                return default(T);
            }
        }
        return item;
    }
 
    private void LogError(Uri BaseUrl, IRestRequest request, IRestResponse response)
    {
        //Get the values of the parameters passed to the API
        string parameters = string.Join(", ", request.Parameters.Select(x => x.Name.ToString() + "=" + ((x.Value == null) ? "NULL" : x.Value)).ToArray());
 
        //Set up the information message with the URL, the status code, and the parameters.
        string info = "Request to " + BaseUrl.AbsoluteUri + request.Resource + " failed with status code " + response.StatusCode + ", parameters: "
        + parameters + ", and content: " + response.Content;
 
        //Acquire the actual exception
        Exception ex;
        if (response != null && response.ErrorException != null)
        {
            ex = response.ErrorException;
        }
        else
        {
            ex = new Exception(info);
            info = string.Empty;
        }
 
        //Log the exception and info message
        _errorLogger.LogError(ex,info);
    }
}

 

Yooo, Now Restsharp client is ready for you

Using the Awesome RestSharp Client

To use this client, we treat it as a base class for other clients. Let's imagine that we have the following object which will be returned by our API:

public class User
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int UserID { get; set; }
    public DateTime DateOfBirth { get; set; }
}

We also have APIs at the following URLs which will return this object or a collection of this object:

/users/{id}

/users/all

We can build a client for this situation very easily with our Ultimate RestSharp Client. First, we need to inherit from our BaseClient class and implement the constructor:

public class UsersClient : BaseClient
{
    public UsersClient (ICacheService cache, IDeserializer serializer, IErrorLogger errorLogger)
        : base(cache, serializer, errorLogger, "http://baseUrl.com") { }
}
 
 

Then we need to create two methods, one which will get the users by ID and one which will get all the users. Let's add a further requirement and say that the method which will get the users by ID will have its results be cached. The complete UsersClient now looks like this:

 
public class UsersClient : BaseClient
{
    public UsersClient (ICacheService cache, IDeserializer serializer, IErrorLogger errorLogger)
        : base(cache, serializer, errorLogger, "http://yourBaseUrl.com") { }
 
    public Contact GetByID(int id)
    {
        RestRequest request = new RestRequest("users/{id}", Method.GET);
        request.AddUrlSegment("id", id.ToString());
        return GetFromCache<User>(request, "User" + id.ToString());
    }
 
    public List<Contact> GetByID(int id)
    {
        RestRequest request = new RestRequest("users/all", Method.GET);
        return Get<List<User>>(request);
    }
}
 
 

That's all there is to it! Now if you have many client classes, they can all share the same base class and easily implement error logging and caching!

Summary

This Ultimate RestSharp client is already in use in one of our biggest projects, and it's saved us so much time and effort once we got it together. I hope it helps you all out as well, dear reader!

As always, I welcome suggestions on how to improve the code in this post. Let me know if you've got any ideas in the comments!

Happy Coding!

 

Monday, August 21, 2017

Types of Testing To Know For Microservices Architectures

Software testing is critical to evaluating and improving the performance, security, and overall success of an application. It requires the dedicated support of skilled testing professionals who know what to look for and what the resulting data means. Because it’s tailored to the application itself, testing is rarely a one-size-fits-all thing—but this is especially true when it comes to complex, microservice-based architectures.

In our Intro to Software Testing article, we broke down some common types of testing in the software testing cycle. But what types of testing should you be implementing specifically if you’re using microservices? What different types of traffic, bottlenecks, failures, and performance issues do you need to consider?

Previously, we discussed the unique challenges microservices present in terms of security. Here, we’ll take a look at some things to consider when performance testing a microservices environment.

Performance Testing for Peace of Mind

Performance testing is designed to generate data and key performance indicators (KPIs) regarding an app’s stability, reliability, speed, and resource usage that can help you correct bugs, errors, and bottlenecks before it goes into production. However, performance testing, like most other things, needs to be adjusted when you’re working with distributed systems.

With microservices, it’s inherent that the environment will always be evolving—that’s the beauty of being able to deploy different services independently from one another, and to scale up where and when you need to. But this means that different, difficult-to-predict failures among the various interconnected services is inevitable. So how can you be as confident as possible that your app won’t suffer major lags or outages once it’s in production? How can you test to account for the added choreography of microservices?

“We need testing strategies that will provide confidence that a service artifact can proceed to the production environment. Testing a large distributed system is hard, and continuous delivery makes this harder since the shared runtime environment is changing constantly. We are seeing promising results with contract testing, which is one of the various microservices testing strategies we’ve considered.”

-Stratis Karamanlakis, Chief Technology Officer, Upwork, from Upwork Modernization: An Overview.

There are a few types of performance test that can tell you different things about the way your app will behave in production. Here are a few to know and what questions they should try and answer.

1. Base testing

Does the software work? This first, most basic question—does the software work—can be answered with base testing. The goal here is to determine the overall functionality of the app and the “steady state” of the distributed system. Similar to the way you’d map out microservices to make it easier to identify any security aberrations, establishing this baseline behavior lets you know what output indicates the system is functioning properly before adding any extra stress onto the system. (It’s also helpful for planning and finding ways the system can evolve.)

End-to-end testing ensures the system as a whole is meeting its business requirements without getting into testing on a microservice-based level. Similarly, capacity testing will let you know if your app can handle the baseline amount of traffic it was designed to handle.

Browse top functional testing freelancers on Upwork.

2. Unit testing

Does each component work well on its own? It would make sense that unit testing would be a part of any granular microservices testing strategy. In a monolith architecture, there are also benefits to unit testing—isolating the smallest usable components of an app and testing them one by one to ensure everything works on its own before integrating with the app as a whole. But with microservices, it’s a bit easier to do as all of those components are already separated out.

Learn more about different types of unit tests and how to run unit tests with external services here and browse top unit testing freelancers on Upwork.

3. Integration testing

Are the communication pathways between services functioning properly? With microservices, it’s important to recognize that you’re not just testing software like you would with a tiered monolith—you’re testing functionality as a whole, each microservice on its own, and the calls between microservices, or “the plumbing.” The interfaces where the services communicate with one another (typically APIs) are critical to the functionality of the app, so it’s just as important to test the components themselves as it is the communication pathways between them.

Test the interfaces between them for defects that might interrupt the interactions between services. Many microservices environments rely on RESTful HTTP or other messaging protocols to interface with one another. Running gateway integration tests will locate any errors at the protocol level, and integration contract testing will ensure that each component is behaving the way it should when contacted. The API interfaces between the components should also be tested, to verify they’re delivering the right information in the correct format.

4. Load testing

How does the app handle day-to-day transactions? A big difference between microservices and a monolith is the network calls that occur between services—and it’s there that bottlenecks will likely occur. Test how the app and network perform and hold up to a high number of concurrent calls, and test for different “pathways” of calls depending on what a user is requesting from the app with the end goal of finding any bottlenecks and correcting them.

Be sure to mix up what you put your app through in testing with different events and combinations of events that might threaten the stability of your app. Test for a high volume of calls. Create tests to see how the app as a whole performs when one microservice needs to transfer a large amount of data to another. Test for big events, or smaller, day-to-day events. The idea here is to identify any weaknesses like aberrant behaviors, outages from too much downstream traffic, response time latencies, or the effects of too many “retries” if timeouts aren’t properly set up.

The network will often be the bottleneck and this type of testing will uncover what parts of the app aren’t scalable enough to keep the app from melting down under a high volume of traffic.

Browse top load testing freelancers on Upwork to get started.

5. Stress testing

How does the app perform under extreme volume? Stress testing takes load testing and pushes your app to the limits to see how it handles an extremely heavy load. This type of testing will give you a good idea of your app’s ceiling, or break point. It’s not an everyday scenario, but it should give you pretty good indicators of what will cause your system to get overloaded. (Other types of stress testing include spike tests and soak tests, which let you know about sudden increases in traffic vs. more long, sustained increases in traffic, respectively.)

6. Resiliency testing

“Our experience with the cloud has not been without glitches, a couple of which have been major. Cloud infrastructure can be volatile and can fail in ways dissimilar to the ones we had learned to expect using a data center. The important takeaway is that failure is a given and we need to plan and design around it. Resiliency must be baked into our designs and plans.”

-Stratis Karamanlakis, Chief Technology Officer, Upwork, from Upwork Modernization: An Overview.

How does the app stand up to failures? Failures are inevitable in an environment that’s always evolving. A good measure of how well your app can stand up to isolated failures is through some rigorous resiliency testing.

A primary goal in microservice testing is to prevent cascading failures once your app is in production. There are levels to the kinds of failures a microservices application can face depending on your architecture, and they can sometimes be isolated to a component, have a ripple out effect, or cause a complete outage altogether.

For example, individual servers that are running part of a specific service may crash or become unavailable. Or, parts of the network may fail, making it impossible for certain services to communicate with one another. That’s not all they need to communicate with, though—if an external data center goes down, that needs to be accounted for, too.

A New Discipline: Chaos Engineering

All of the above fall under the bigger picture of performance testing—tests designed to gauge the stability, scalability, speed, and responsiveness of a distributed system.

Related to that is chaos engineering, a way to observe the behavior of microservices in controlled environments to see how they perform when parts of the system fail. It’s slightly different from typical resilience testing, deliberately causing failures to reveal any “systemic uncertainty in distributed systems.”

“The principles of Chaos Engineering and tools such as the Netflix Chaos Monkey emphasize the practice of proactively stressing a system in order to understand its limits and build confidence in its expected availability and resiliency. We are not there yet, but we will soon start to adopt some of these approaches.”

-Stratis Karamanlakis, Chief Technology Officer, Upwork, from Upwork Modernization: An Overview.

Why “chaos”? Well, these systems can get a little chaotic—especially when you take into account that things are always evolving, each service needs to be functioning properly on its own, external services and databases can fail, and all of the boundaries and gateways internally and externally need to be tightly controlled. If one instance goes down, what do components that rely on it fall back on?

The key is to set up the right redundancies so systems can tolerate failures without causing service interruptions. Learn more about this tool from Netflix here and check it out on Github here.


Read more at http://www.business2community.com/brandviews/upwork/6-types-testing-know-microservices-architectures-01902537#pyIzrb3bibILxi66.99