restsharp

A Simple Caching Scheme for Web API using Dependency Injection

I use Dependency Injection (DI) quite a bit in my ASP.NET projects, particularly in Web API and MVC web applications. Recently, I had a need to implement a caching layer in one of my MVC apps, and such a layer would be best used if it could be injected into my clients layer (e.g. the layer that called an API and handled responses from the same). The solution I came up with seemed to be pretty simple, so I wanted to share it here.

In this post, we're going to set up a simple MVC project that consumes a Web API and implements a caching layer.

Requirements

For this project, I'm using two of my favorite NuGet packages:

The sample project, which is over on GitHub, is a fully-functional implementation of the strategy described in this post. Feel free to check it out, branch it, download it, whatever!

Setting Up the API

First, let's take a look at our sample API. Here's the class DateNumberObject, which represents a response returned from the API to the web project:

public class DateNumberObject  
{
    public DateTime CurrentDate { get; set; }
    public int RandomNumber { get; set; }

    public DateNumberObject()
    {
        CurrentDate = DateTime.Now;
        Random rand = new Random();
        RandomNumber = rand.Next(1, 200);
    }
}

As you can see, all this class does is return the current date and time and a random number.

We can now build our API, which is also rather simple. Here's a snippet from the API's controller:

[RoutePrefix("samples")]
public class SampleController : ApiController  
{

    [HttpGet]
    [Route("date")]
    public IHttpActionResult GetDateAndNumber()
    {
        return Ok(new DateNumberObject());
    }
}

That's all our API will do: return the current date and a random number. Since there's no caching taking place at the service layer, we will need to implement caching at the web layer.

Speaking of the web layer, in our sample solution it is an MVC5 project, and we will be using my favorite Dependency Injection tool, StructureMap.

Setting up StructureMap

If you've never set up StructureMap in your MVC projects, you'll want to read this section; otherwise, skip to the next section.

The first thing we need to do is download the StructureMap.MVC5 NuGet package, which will add a DependencyInjection folder and a StructuremapMvc file to our app:

Inside the Dependency Resolution folder will be a file called DefaultRegistry.cs, which will initially look something like this:

namespace WebApiCacheDemo.Mvc.DependencyResolution {  
    using Caching;
    using StructureMap.Configuration.DSL;
    using StructureMap.Graph;

    public class DefaultRegistry : Registry {
        #region Constructors and Destructors
        public DefaultRegistry() {
            Scan(
                scan => {
                    scan.TheCallingAssembly();
                    scan.WithDefaultConventions();
                    scan.With(new ControllerConvention());
                });
        }
        #endregion
    }
}

The way StructureMap works is that it keeps instances of classes that need to be "injected" into other classes in a Container class, and uses these instances any time a particular interface is called for in another class. You'll see exactly what this means in the next section.

The Uncached Example

Let's build the uncached example first, and we can begin by setting up the MVC client. For this project we'll be using RestSharp (which I have also written about before) to consume the API responses, so be sure to download the NuGet package.

Because we're using Dependency Injection, we need our clients to be injectable into our controllers. For this reason, we will make both an interface and a class for our client, like so:

public interface ISampleClient : IRestClient  
{
    DateNumberObject GetSampleDateAndNumberUncached();
}

public class SampleClient : RestClient, ISampleClient  
{
    public SampleClient()
    {
        BaseUrl = new Uri("http://localhost:58566/");
    }

    public DateNumberObject GetSampleDateAndNumberUncached()
    {
        RestRequest request = new RestRequest("samples/date", Method.GET);
        var response = Execute<DateNumberObject>(request);
        return response.Data;
    }
}

Note that we don't need to register this client with StructureMap because the naming follows the standard conventions (ISampleClient maps to SampleClient).

Now we need our controller...

[RoutePrefix("Home")]
public class HomeController : Controller  
{
    private ISampleClient _sampleClient;

    public HomeController(ISampleClient sampleClient)
    {
        _sampleClient = sampleClient;
    }

    [HttpGet]
    [Route("Uncached")]
    [Route("")]
    [Route("~/")]
    public ActionResult Uncached()
    {
        var model = _sampleClient.GetSampleDateAndNumberUncached();
        return View(model);
    }
}

...which returns a view:

@model WebApiCacheDemo.Contracts.Samples.DateNumberObject
@{
    ViewBag.Title = "Uncached Date Sample";
}

<h2>Uncached Results</h2>  
<div class="row">  
    <span><strong>Current Date:</strong></span>
    @Model.CurrentDate
</div>  
<div class="row">  
    <span><strong>Random Number:</strong></span>
    @Model.RandomNumber
</div>  

When we run this, we can see that the view returns the newest date and time, as shown in this gif:

That's exactly what we would expect to see, of course. Now, we can get down to implementing a cache at the web layer for this.

The Cached Example

The first thing we need is our CacheService interface and implementation, which I totally stole from this StackOverflow answer:

public class InMemoryCache : ICacheService  
{
    public T GetOrSet<T>(string cacheKey, Func<T> getItemCallback) where T : class
    {
        T item = MemoryCache.Default.Get(cacheKey) as T;
        if (item == null)
        {
            item = getItemCallback();
            MemoryCache.Default.Add(cacheKey, item, DateTime.Now.AddMinutes(30));
        }
        return item;
    }
}

public interface ICacheService  
{
    T GetOrSet<T>(string cacheKey, Func<T> getItemCallback) where T : class;
}

We also need our SampleClient class updated to use this cache service, like so:

public class SampleClient : RestClient, ISampleClient  
{
    private ICacheService _cache;
    public SampleClient(ICacheService cache)
    {
        _cache = cache;
        BaseUrl = new Uri("http://localhost:58566/");
    }

    public DateNumberObject GetSampleDateAndNumber()
    {
        return _cache.GetOrSet("SampleDateAndNumber", () => GetSampleDateAndNumberUncached());
    }

    public DateNumberObject GetSampleDateAndNumberUncached()
    {
        RestRequest request = new RestRequest("samples/date", Method.GET);
        var response = Execute<DateNumberObject>(request);
        return response.Data;
    }
}

NOTE: In my structure, I am intentionally implementing the cached and uncached operations as separate methods in the same client, so that either one can be used. This may or may not be the correct usage in your application.

Further, we need to update our controller:

[RoutePrefix("Home")]
public class HomeController : Controller  
{
    ...

    [HttpGet]
    [Route("Cached")]
    public ActionResult Cached()
    {
        var model = _sampleClient.GetSampleDateAndNumber();
        return View(model);
    }
}

The last step is to register the ICacheService implementing in StructureMap's DefaultRegistry.cs class:

namespace WebApiCacheDemo.Mvc.DependencyResolution {  
    ...

    public class DefaultRegistry : Registry {
        public DefaultRegistry() {
            ...
            var inMemoryCache = new InMemoryCache();
            For<ICacheService>().Use(inMemoryCache);
        }
    }
}

Now, when we run this sample, we will see the cached values for the date and number, as shown in the following gif:

Now we've successfully implemented our cache AND used Dependency Injection in the process!

Summary

With this structure, we've successfully implemented a caching layer into our MVC application, all while using StructureMap to provide Dependency Injection. This structure allows us to potentially swap cache providers (in case we, say, move to a database-located cache for server farms) without too much trouble, as well as injecting the cache service into our existing clients.

Don't forget to check out the sample project over on GitHub, and feel free to point out how you've used this implemention or where it can be improved in the comments.

Happy Coding!

A Simple Organization for ASP.NET Web API Producer/Consumer Apps

My team regularly writes ASP.NET Web API projects which have multiple consumers, and oftentimes we also write at least one of said consumers. To accomplish this, we often invoke a project layout and architecture that doesn't seem terribly obvious to newcomers to my group. I hope to better explain myself as to why this project organization works in this post (so I can just say, "Hey, new guy, lookie here!" and point them at this post.)

Sample Architecture

So, let's get the spoilers out of the way first: this architecture works because both the API and the MVC app use a common library of DTO objects. In this way, whenever an object being returned from the API is deserialized from its JSON representation, the MVC app knows what object to deserialize it to. This is directly a benefit of being in charge of both producer (the API) and consumer (the MVC web project).

In the sample app available on GitHub, the project organization looks like this:

The Common project holds what are known as the DTOs (Data Transfer Objects). The DTOs do nothing but move data from one place to another; they will not ever have any methods or logic contained within them.

Once we have a place to put the DTOs, we can begin building the API which uses them.

Building the API

Let's now see how we might build both the DTO objects and the API which serializes them to JSON. First, here's a sample User class:

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

For our API, let's pretend we have a ridiculously simple scenario: we need an API which returns all Users in our data store, and an MVC web application which displays these users to our end-user.

For this simple demo, let's pretend that our API Controller is returning a list of users, like so:

[RoutePrefix("users")]
public class UsersController : ApiController  
{
    [HttpGet]
    [Route("all")]
    public IHttpActionResult GetAll()
    {
        var users = new List<User>();

        users.Add(new User()
        {
            FirstName = "Marco",
            LastName = "Polo",
            DateOfBirth = new DateTime(1254, 1, 14),
            Username = "greatadventurer"
        });

        users.Add(new User()
        {
            FirstName = "Kublai",
            LastName = "Khan",
            DateOfBirth = new DateTime(1215, 9, 23),
            Username = "khanofkhans"
        });

        users.Add(new User()
        {
            FirstName = "Kokachin",
            LastName = "",
            DateOfBirth = new DateTime(1255, 8, 17),
            Username = "blueprincess"
        });

        return Ok(users);
    }
}

Now, to prove this works, we can call this action using something like Postman:

As we would expect, calling the path /users/all results in us getting back the list of users.

Now let's see how we might build an MVC web app to consume this API call.

Building the MVC API Client

The MVC app will use the brilliant NuGet package RestSharp to simplify our calls to the API. Once we've downloaded the package, we can create a class which derives from that package's RestClient like so:

public class ApiClient : RestClient  
{
    private static readonly string BaseURL = "http://localhost:55675/";

    public ApiClient() : base(BaseURL) { }

    public List<User> GetAllUsers()
    {
        RestRequest request = new RestRequest("users/all");
        RestResponse response = base.Execute(request);
        return JsonConvert.DeserializeObject<List<User>>(response.Content);
    }
}

Let's walk through each of the three lines involved in the method GetAllUsers().

The first line is

RestRequest request = new RestRequest("users/all");  

This line creates a RestRequest object which is pointed at the url http://localhost:55675/users/all (the root of the URL is initialized in the ApiClient's constructor).

The second line is

var response = base.Execute(request);  

This line calls the API at the specified location and returns an object of type RestResponse.

The third line is the most complex:

return JsonConvert.DeserializeObject<List<User>>(response.Content);  

Let's break down the key pieces of this line:

  • response.Content is the actual content from the API call; in this case, it's the JSON representation of the list of Users.
  • JsonConvert.DeserializeObject<T>() deserializes a string into an object (in our particular case, into a List<User>.
  • The List<User> is then returned to the calling method.

With this step in place, we can finish the MVC app.

Finishing the MVC App

Our MVC controller will need to create an instance of ApiClient so that it can retrieve the users from the API:

[RoutePrefix("users")]
public class UsersController : Controller  
{

    [HttpGet]
    [Route("index")]
    public ActionResult Index()
    {
        ApiClient client = new ApiClient();
        return View(client.GetAllUsers());
    }
}

Finally, all we have to do is run the MVC app and we'll see that the users are returned and displayed exactly as we thought they would be:

Summary

This organization allows both producer and consumer to use the same Data Transfer Objects (DTOs) when serializing to or deserializing from JSON objects. Consequently, both "sides" of the relationship can use the same objects without needing to write and maintain their own definitions.

I understand that this organization is only useful in situations where the developers control both sides of the producer/consumer relationship; but in those situations, it's been invaluable for me and my team.

Don't forget to check out the sample project on GitHub, and feel free to submit suggestions for improvements there or in the comments below.

Happy Coding!

Consuming Web API Custom Validation in MVC using RestSharp

I previously wrote a post called Custom Validation in ASP.NET Web API with FluentValidation, in which I showed how my group set up a validation framework over WebAPI using the FluentValidation NuGet package.

In this post, we'll go over how to consume those responses in an ASP.NET MVC app, as well as how to take the error messages from the API response and automatically add them to the MVC ModelState.

I have updated the sample project on GitHub with the code from this post, so it may be easier for you to pull that down first and then follow along. Whichever way you like to learn, let's get started!

Goals and Dependencies

In building this system, we have two major goals in mind:

  1. All validation will be done on the Web API side, but the consuming MVC app will need to display the errors to the user.
  2. In order to make the first goal easier, we want the errors from the Web API project automatically imported into the MVC ModelState.

For this solution, we are using a couple of packages from NuGet:

  • JSON.NET (Allows simple serializing and deserializing to and from JSON).
  • RestSharp (Enables simpler calling of API clients)

Finally, we are using a pattern called POST-REDIRECT-GET in our MVC app which enables us to pass ModelStates from one action to another (check out that post for more details).

Remember the Alamo Models!

First, let's remind ourselves what the models and package looked like from the previous post.

public class User  
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime BirthDate { get; set; }
    public string Username { get; set; }
}

public class UserValidator : AbstractValidator<User>  
{
    public UserValidator()
    {
        RuleFor(x => x.FirstName).NotEmpty().WithMessage("The First Name cannot be blank.")
                                    .Length(0, 100).WithMessage("The First Name cannot be more than 100 characters.");

        RuleFor(x => x.LastName).NotEmpty().WithMessage("The Last Name cannot be blank.");

        RuleFor(x => x.BirthDate).LessThan(DateTime.Today).WithMessage("You cannot enter a birth date in the future.");

        RuleFor(x => x.Username).Length(8, 999).WithMessage("The user name must be at least 8 characters long.");
    }
}

public class ResponsePackage  
{
    public List<string> Errors { get; set; }
    public object Result { get; set; }
    public bool HasErrors
    {
        get
        {
            if (Errors != null)
            {
                return Errors.Any();
            }
            return false;
        }
    }
    public ResponsePackage(object result, List<string> errors)
    {
        Errors = errors;
        Result = result;
    }
}

Recall that in this system, any response at all will be wrapped by a ResponsePackage, so one of the things the consumer system will have to do is extract the JSON from the package and deserialize it to an object.

Creating the Clients

In order to call the API using RestSharp, we can use "client" classes that implement methods which call API functions. In our case, we decided to create a "base" client class which all other clients would inherit from. The base client needs three things:

  1. A constructor which passes in the current ModelState (so that validation errors from the API can be inserted into it).
  2. An Execute() method which executes the current request.
  3. An Execute<T>() method which executes the current request and automatically extracts the content of the ResponsePackage to the specified type.

Here's the code for the base client:

public class ClientBase : RestClient  
{
    private ModelStateDictionary _modelstate;

    public ClientBase(ModelStateDictionary modelstate) : base(Constants.ApiUrl)
    {
        _modelstate = modelstate;
    }

    //This method executes the request and does not attempt to deserialize the response.  We use this for updates, deletes, etc.
    public new void Execute(IRestRequest request)
    {
        var response = base.Execute(request);
        var parsedObj = JObject.Parse(response.Content);
        var apiResponse = JsonConvert.DeserializeObject<ResponsePackage>(parsedObj.ToString());
        if (apiResponse.HasErrors)
        {
            AddErrors(apiResponse);
        }
    }

    //This method expects the Result property of the response to be a JSON object that can be deserialized into an object of type T
    public new T Execute<T>(IRestRequest request) where T : new()
    {
        var response = base.Execute(request);
        var parsedObj = JObject.Parse(response.Content);
        var apiResponse = JsonConvert.DeserializeObject<ResponsePackage>(parsedObj.ToString());
        if (apiResponse.HasErrors)
        {
            AddErrors(apiResponse);
            return default(T);
        }
        return response.Extract<T>();
    }

    private void AddErrors(ResponsePackage response)
    {
        List<string> listMessagesAdded = new List<string>();
        for (int i = 0; i < response.Errors.Count; i++)
        {
            if (listMessagesAdded.Contains(response.Errors[i])) continue;
            _modelstate.AddModelError("error" + i.ToString(), response.Errors[i]);
            listMessagesAdded.Add(response.Errors[i]);
        }
    }
}

Take particular note of the AddErrors() method. This method is key to the whole operation: it is what takes the error messages out of the ResponsePackage and inserts them into the API.

Now that we've got the base client, let's create the derivative UserClient. Recall from the previous post that we have two methods in the API: a method to get all the users and a method to add an additional one. We need corresponding methods in our UserClient, like so:

public class UserClient : ClientBase  
{
    public UserClient(ModelStateDictionary modelstate) : base(modelstate)
    {
    }

    public List<User> GetAll()
    {
        RestRequest request = new RestRequest("users/all");
        return Execute<List<User>>(request);
    }

    public void Add(User user)
    {
        RestRequest request = new RestRequest("users/add", Method.POST);
        request.AddJsonBody(user);
        Execute(request);
    }
}

There's still a piece missing. Exactly what does the Extract<T>() method above do? It reads the Result property of the ResponsePackage and deserializes it into an object of type T. We implement that in an extension class, like so:

public static class RestResponseExtensions  
{
    private static string ResultPropertyName = "Result";

    public static T Extract<T>(this IRestResponse response) where T : new()
    {
        var parsedObj = JObject.Parse(response.Content);
        return JsonConvert.DeserializeObject<T>(parsedObj[ResultPropertyName].ToString());
    }
}

NOTE: IRestResponse is an interface implemented in RestSharp

We're on our way! Now that we've got the clients defined, let's set up the MVC app.

Chaos Control(ler)

Let's do the code first. Here's the Controller:

[RoutePrefix("users")]
public class UserController : Controller  
{
    [HttpGet]
    [Route("all")]
    [Route("")]
    [Route("~/")]
    public ActionResult Index()
    {
        UserClient client = new UserClient(ModelState);
        var users = client.GetAll();
        return View(users);
    }

    [HttpGet]
    [Route("add")]
    [ImportModelState]
    public ActionResult Add()
    {
        var user = new User();
        return View(user);
    }

    [HttpPost]
    [Route("add")]
    [ExportModelState]
    public ActionResult Add(User user)
    {
        UserClient client = new UserClient(ModelState);
        client.Add(user);
        if(!ModelState.IsValid)
        {
            return RedirectToAction("Add");
        }
        return RedirectToAction("Index");
    }
}

The [ExportModelState] and [ImportModelState] attributes are part of the POST-REDIRECT-GET pattern that I've written about before. For now, just remember that those attributes allow the ModelState to get passed from one action to another (otherwise it gets lost on redirects).

We also need to set up the Add view (If you want the code for the Index view, take a look at the GitHub project)

@model WebApiValidationDemo.Mvc.Lib.Models.User

@{
    ViewBag.Title = "Add a User";
}

@Html.ActionLink("Back to Index", "Index")

<h2>Add a User</h2>

@using (Html.BeginForm("Add", "User", FormMethod.Post))
{
    @Html.ValidationSummary() //This is key
    <div>
        <div>
            @Html.LabelFor(x => x.FirstName)
            @Html.TextBoxFor(x => x.FirstName)
        </div>
        <div>
            @Html.LabelFor(x => x.LastName)
            @Html.TextBoxFor(x => x.LastName)
        </div>
        <div>
            @Html.LabelFor(x => x.BirthDate)
            @Html.TextBoxFor(x => x.BirthDate, new { type = "date" })
        </div>
        <div>
            @Html.LabelFor(x => x.Username)
            @Html.TextBoxFor(x => x.Username)
        </div>
        <div>
            <input type="submit" value="Save" />
        </div>
    </div>
}

The Html.ValidationSummary() is key, since that will display the error messages found in the ModelState.

What Does THIS Button Do?

It saves the User. Sheesh, if you just wait a minute, you'll find out.

(My son asked me this question regarding a different app as I was writing this section. My response was the same.)

We will now test the app to ensure that it is receiving validation errors and displaying them on the page in the Html.ValidationSummary() control. As a reminder, here's the rules that validate the User:

  1. The FirstName cannot be blank.
  2. The FirstName cannot be more than 100 characters.
  3. The LastName cannot be blank.
  4. The BirthDate cannot be in the future (relative to the current date).
  5. The Username must be at least 8 characters long.

If we run the MVC app, we'll end up on a page that looks like this:

Now let's test adding a valid user and an invalid user.

Valid User

We can click on "Add a User" to see the Add a User page. Let's attempt to add a valid user, like so:

Clicking on save causes none of the validation rules to fire, so we end up back at the index page. Success!

Invalid User

Now let's try to add an invalid user.

Note that this user violates three of the rules:

  • The first name is blank.
  • The birth date is in the future.
  • The username is less than 8 characters long.

Attempting to save this user results in this:

Success! The error messages were successfully returned to the ModelState and shown to the user.

The Best Part

Here's the best part about this whole situation: adding new validation rules on the API causes zero changes on the consumers! We've removed any dependency the MVC app has on the validation implemented by the API.

Let's say we implement a new validation rule:

  • The Last Name cannot be less than 5 characters.

The resulting change to our UserValidator looks like this:

public class UserValidator : AbstractValidator<User>  
{
    public UserValidator()
    {
        ...
        RuleFor(x => x.LastName).Length(5, 999).WithMessage("The Last Name cannot be less than 5 characters");
        ...
    }
}

Now, let's resubmit that invalid user we just created, and here's the error messages we get:

Summary

We have now built an ASP.NET MVC app that can successfully:

  • Consume the responses sent by the Web API's custom validation layer, even though they're all wrapped in ResponsePackage.
  • Display the error message returned by the API.

Take a look at the GitHub project if you haven't already done so, and feel free to point out what I did wrong (or right, hey I can hope) in the comments.

Happy Coding!