web-api

The ASP.NET Web API 2 HTTP Message Lifecycle in 43 Easy Steps

Anyone who works with ASP.NET Web API should check out this poster that Microsoft created to explain the Request/Response Pipeline that Web API utilizes. It's amazing, and if you do any work in Web API you should check it out! Right now. Yes, seriously. Go ahead, I'll wait.

I love this poster, but in my opinion it doesn't do a good job of explaining the decision logic and ideas behind each step in the pipeline. Further, it doesn't explicitly tell you exactly how many things happen during this pipeline (answer: a surprisingly large number of things). In short: it's awesome, but it can be made more awesome by incorporating just a little more detail.

Here's what we're going to do in this post: using that poster, we're going to enumerate every single step involved in processing a request and receiving a response using ASP.NET Web API 2, and explain a little more about each piece of the pipeline and where we programmers can extend, change, or otherwise make more awesome this complex lifecycle. So let's get going and step through the ASP.NET Web API 2 Request Lifecycle in just 43 easy steps!

The 43 Steps

It all starts with IIS:

  1. IIS (or OWIN self-hosting) receives a request.
  2. The request is then passed to an instance of HttpServer.

  3. HttpServer is responsible for dispatching HttpRequestMessage objects.

  4. HttpRequestMessage provides strongly-typed access to the request.

  5. If one or more global instances of DelegatingHandler exist on the pipeline, the request is passed to it. The request arrives at the instances of DelegatingHandler in the order said instances were added to the pipeline.

  6. If the HttpRequestMessage passes the DelegatingHandler instances (or no such handler exists), then the request proceeds to the HttpRoutingDispatcher instance.

    • HttpRoutingDispatcher chooses which routing handler to call based on the matching route. If no such route exists (e.g. Route.Handler is null, as seen in the diagram) then the request proceeds directly to Step 10.

  7. If a Route Handler exists for the given route, the HttpRequestMessage is sent to that handler.

  8. It is possible to have instances of DelegatingHandler attached to individual routes. If such handlers exist, the request goes to them (in the order they were added to the pipeline).
  9. An instance of HttpMessageHandler then handles the request. If you provide a custom HttpMessageHandler, said handler can optionally return the request to the "main" path or to a custom end point.

  10. The request is received by an instance of HttpControllerDispatcher, which will route the request to the appropriate route as determined by the request's URL.

  11. The HttpControllerDispatcher selects the appropriate controller to route the request to.

  12. An instance of IHttpControllerSelector selects the appropriate HttpControllerDescriptor for the given HttpMessage.
  13. The IHttpControllerSelector calls an instance of IHttpControllerTypeResolver, which will finally call...
  14. ...an instance of IAssembliesResolver, which ultimately selects the appropriate controller and returns it to the HttpControllerDispatcher from Step 11.
    • NOTE: If you implement Dependency Injection, the IAssembliesResolver will be replaced by whatever container you register.
  15. Once the HttpControllerDispatcher has a reference to the appropriate controller, it calls the Create() method on an IHttpControllerActivator...
  16. ...which creates the actual controller and returns it to the Dispatcher. The dispatcher then sends the request into the Select Controller Action routine, as shown below.

  17. We now have an instance of ApiController which represents the actual controller class the request is routed to. Said instance calls the SelectAction() method on IHttpActionSelector...

  18. ...which returns an instance of HttpActionDescriptor representing the action that needs to be called.

  19. Once the pipeline has determined which action to route the request to, it executes any Authentication Filters which are inserted into the pipeline (either globally or local to the invoked action).

    • These filters allow you to authenticate requests to either individual actions, entire controllers, or globally throughout the application. Any filters which exist are executed in the order they are added to the pipeline (global filters first, then controller-level filters, then action-level filters).
  20. The request then proceeds to the [Authorization Filters] layer, where any Authorization Filters which exist are applied to the request.

    • Authorization Filters can optionally create their own response and send that back, rather than allowing the request to proceed through the pipeline. These filters are applied in the same manner as Authentication Filters (globally, controller-level, action-level). Note that Authorization Filters can only be used on the Request, not the Response, as it is assumed that if a Response exists, the user had the authorization to generate it.
  21. The request now enters the Model Binding process, which is shown in the next part of the main poster. Each parameter needed by the action can be bound to its value by one of three separate paths. Which path the binding system uses depends on where the value needed exists within the request.

  22. If data needed for an action parameter value exists in the entity body, Web API reads the body of the request; an instance of FormatterParameterBinding will invoke the appropriate formatter classes...

  23. ...which bind the values to a media type (using MediaTypeFormatter)...

  24. ...which results in a new complex type.

  25. If data needed for a parameter value exists in the URL or query string, said URL is passed into an instance of IModelBinder, which uses an IValueProvider to map values to a model (see Phil Haack's post about this topic for more info)....

  26. ...which results in a simple type.

  27. If a custom HttpParameterBinding exists, the system uses that custom binding to build the value...

  28. ...which results in any kind (simple or complex) of object being mappable (see Mike Stall's wonderful series on this topic).

  29. Now that the request is bound to a model, it is passed through any Action Filters which may exist in the pipeline (either globally or just for the action being invoked).

  30. Once the action filters are passed, the action itself is invoked, and the system waits for a response from it.

  31. If the action produces an exception AND an exception filter exists, the exception filter receives and processes the exception.

  32. If no exception occurred, the action produces an instance of HttpResponseMessage by running the Result Conversion subroutine, shown in the next screenshot.

  33. If the return type is already an HttpResponseMessage, we don't need to do any conversion, so pass the return on through.

  34. If the return type is void, .NET will return an HttpResponseMessage with the status 204 No Content.

  35. If the return type is an IHttpActionResult, call the method ExecuteAsync to create an HttpResponseMessage.

    • In any Web API method in which you use return Ok(); or return BadRequest(); or something similar, that return statement follows this process, rather than any of the other processes, since the return type of those actions is IHttpActionResult.
  36. For all other types, .NET will create an HttpResponseMessage and place the serialized value of the return in the body of that message.

  37. Once the HttpResponseMessage has been created, return it to the main pipeline.

  38. Pass the newly-created HttpResponseMessage through any AuthenticationFilters which may exist.

    • Remember that Authorization Filters cannot be used on Responses.

  39. The HttpResponseMessage flows through the HttpControllerDispatcher, which at this point probably won't do anything with it.

  40. The Response also flows through the HttpRoutingDispatcher, which again won't do anything with it.

  41. The Response now proceeds through any DelegatingHandlers that are set up to handle it. At this point, the DelegatingHandler objects can really only change the response being sent (e.g. intercept certain responses and change to the appropriate HTTP status).

  42. The final HttpResponseMessage is given to the HttpServer instance...

  43. ...which returns an Http response to the invoking client.

Tada! We've successfully walked through the entire Web API 2 request/response pipeline, and in only 43 easy steps!

Let me know if this kind of deep dive has been helpful to you, and feel free to share in the comments! Microsoft people and other experts, please chime in to let me know if I got something wrong; I intend for this post to be the definitive guide to the Web API 2 Request/Response Lifecycle, and you can't be definitive without being correct.

Happy Coding!

Getting All Valid Enum Values in ASP.NET Web API

One of the commenters on my blog posed an interesting question on my article about serializing enumerations in Web API:

A comment on my blog, asking

He wanted to know how someone who is querying a Web API might know what possible values a given enumeration has. I didn't answer that in the post he commented on, so I'll do that now. How do we tell consumers what possible values an enumeration in a Web API app has?

Let's say we have the following enums:

public enum AddressType  
{
    Physical,
    Mailing,
    Shipping
}

public enum AccessLevel  
{
    Administrator,
    ReadWrite,
    ReadOnly
}

We want to expose a query which returns these values for each enumeration. To do that, let's create a class for EnumValue:

public class EnumValue  
{
    public string Name { get; set; }
    public int Value { get; set; }
}

It's a pretty generic class, to be sure, but since all enumerations are is a name and a value it serves our purposes well enough.

The trick now is to create an extension which uses Reflection to get all the names for the given enum. In fact, we can even make that method generic, so it can be used for any enumeration at all.

public static class EnumExtensions  
{
    public static List<EnumValue> GetValues<T>()
    {
        List<EnumValue> values = new List<EnumValue>();
        foreach (var itemType in Enum.GetValues(typeof(T)))
        {
            //For each value of this enumeration, add a new EnumValue instance
            values.Add(new EnumValue()
            {
                Name = Enum.GetName(typeof(T), itemType), 
                Value = (int)itemType
            });
        }
        return values;
    }
}

Finally, we can use Web API methods that call this extension:

public class HomeController : ApiController  
{
    [HttpGet]
    [Route("accesslevels/all")]
    public IHttpActionResult GetAccessLevels()
    {
        return Ok(EnumExtensions.GetValues<AccessLevel>());
    }

    [HttpGet]
    [Route("addresstypes/all")]
    public IHttpActionResult GetAddressTypes()
    {
        return Ok(EnumExtensions.GetValues<AddressType>());
    }
}

When we call this method using Postman, we now get an appropriate response code (200 OK) and the data we were looking for. Here's the address types:

A Postman response, showing the three possible values for the enumeration AddressType

And here's the access levels:

A Postman response, showing the three possible values for the enumeration AccessLevel

Of course, this is just one of many possible solutions to this particular problem. I like this method because it's generic and reusable for all enumerations, but if you find a better method, please feel free to share in the comments!

That's all there is to it! Now any consumer can hit these methods and know exactly what values can be used for the AccessLevel and AddressType enumerations!

I also have a repository over on GitHub which stores the sample code for this post. Check it out!

Happy Coding!

Real-World CQRS/ES with ASP.NET and Redis Part 4 - Creating the APIs

NOTE: This is Part 4 of a five-part series in which I detail how a real-world ASP.NET Web API app using the Command-Query Responsibility Segregation and Event Sourcing (CQRS/ES) patterns and the Redis database might look. Here's Part 1 of this series. The corresponding repository is over on GitHub.

We've done quite a lot of work to get to this point! We've discussed why we might want to use Command-Query Responsibility Segregation (CQRS) and Event Sourcing (ES) in our app, we've built a Write Model to handle the processing of our commands, and we've built a Read Model to query our data.

Now we can show why this is a "real-world" app. Here's what we're going to do in Part 4 of Real World CQRS/ES with ASP.NET:

  • Build a Queries API so we can query the system for data.
  • Build a Commands API so that we can issue commands to the system.
  • Implement a validation layer using FluentValidation to ensure that commands being issued are valid to execute.
  • Implement dependency injection using StructureMap in both the commands and queries APIs.

Don't stop now! Let's get starting building our APIs!

The Queries API

We're going to switch it up a bit and build the Queries API first, as that turns out to be easier than building the Commands API right off the bat. After all, the Queries API doesn't have to worry about things like validation. So, let's create a new ASP.NET Web API app.

Dependency Injection with StructureMap

After creating the new ASP.NET Web API project, the first thing we need to do is download the StructureMap.WebApi2 NuGet package and install it. Doing so gives us a folder structure that looks something like this (notice the new DependencyResolution folder):

I've blogged about how to use StructureMap with Web API in a previous post, so if you're not familiar with the StructureMap.WebApi2 package, you might want to read that post first, then come back here. It's OK, I'll wait.

Once we've downloaded and installed the StructureMap.WebApi2 package, we'll need to change just a couple of things. In our Global.asax file, we need to start the StructureMap container:

public class WebApiApplication : System.Web.HttpApplication  
{
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        GlobalConfiguration.Configure(WebApiConfig.Register);
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);

        StructuremapWebApi.Start(); //Start! Your! Containers! VROOOOOOOOM
    }
}

We also need to register the appropriate items with the container so that they can be injected. Among those items are the Repositories we created in the previous part of this series; we must register them so our API controller have them injected.

In Part 3, we also established that we are using Redis as our Read Data Store, and that we are utilizing StackExchange.Redis to interface with said Redis instance. StackExchange.Redis conveniently comes prepared for dependency injection, so we will need to register the IConnectionMultiplexer interface with our container.

In all, our DefaultRegistry class for the Queries API looks like this:

public class DefaultRegistry : Registry {  
    public DefaultRegistry() 
    {
        //Repositories
        For<IEmployeeRepository>().Use<EmployeeRepository>();
        For<ILocationRepository>().Use<LocationRepository>();

        //StackExchange.Redis
        ConnectionMultiplexer multiplexer = ConnectionMultiplexer.Connect("localhost");
        For<IConnectionMultiplexer>().Use(multiplexer);
    }
}

See, that wasn't too bad! Just wait until you see the Commands API's registry.

Building the Queries

Anyway, with StructureMap now in place, we can start building the queries we need to support. Here's the queries list we talked about in Part 3:

  • Get Employee by ID
  • Get Location by ID
  • Get All Locations
  • Get All Employees (with their assigned Location ID)
  • Get All Employees at a Location

Let's start with the easy one: getting an Employee by their ID.

Get Employee by ID

We need an EmployeeController, with a private IEmployeeRepository, to execute this query. The complete controller is as follows:

[RoutePrefix("employees")]
public class EmployeeController : ApiController  
{
    private readonly IEmployeeRepository _employeeRepo;

    public EmployeeController(IEmployeeRepository employeeRepo)
    {
        _employeeRepo = employeeRepo;
    }

    [HttpGet]
    [Route("{id}")]
    public IHttpActionResult GetByID(int id)
    {
        var employee = _employeeRepo.GetByID(id);

        //It is possible for GetByID() to return null.
        //If it does, we return HTTP 400 Bad Request
        if(employee == null)
        {
            return BadRequest("No Employee with ID " + id.ToString() + " was found.");
        }

        //Otherwise, we return the employee
        return Ok(employee);
    }
}

Well, that looks pretty simple. How about the GetAll() query?

Get All Employees

[RoutePrefix("employees")]
public class EmployeeController : ApiController  
{
    ...

    [HttpGet]
    [Route("all")]
    public IHttpActionResult GetAll()
    {
        var employees = _employeeRepo.GetAll();
        return Ok(employees);
    }
}

I think I'm sensing a theme here.

The Location Queries Controller

Let's see what the Location queries are:

[RoutePrefix("location")]
public class LocationController : ApiController  
{
    private ILocationRepository _locationRepo;

    public LocationController(ILocationRepository locationRepo)
    {
        _locationRepo = locationRepo;
    }

    [HttpGet]
    [Route("{id}")]
    public IHttpActionResult GetByID(int id)
    {
        var location = _locationRepo.GetByID(id);
        if(location == null)
        {
            return BadRequest("No location with ID " + id.ToString() + " was found.");
        }
        return Ok(location);
    }

    [HttpGet]
    [Route("all")]
    public IHttpActionResult GetAll()
    {
        var locations = _locationRepo.GetAll();
        return Ok(locations);
    }

    [HttpGet]
    [Route("{id}/employees")]
    public IHttpActionResult GetEmployees(int id)
    {
        var employees = _locationRepo.GetEmployees(id);
        return Ok(employees);
    }
}

Yep, definitely a theme going on. All this setup has made implementing our controllers very simple, and simplicity is definitely better when dealing with complex patterns like CQRS and ES.

We'll run queries against this API in Part 5, but for now let's turn our attention to the Commands API, which may prove to be a bit more difficult to write.

The Commands API

As I mentioned early on in this post, the Commands API is considerably more complex than the Queries API; this is largely due to the number of things we need to inject into our container, as well as the Commands API being responsible for validating the requests that come in to the system. We're going to tackle each of these problems.

Dependency Injection

First, let's deal with Dependency Injection. We'll use the same package as before, with the same Global.asax change. However, our DefaultRegistry looks much different.

In the Commands API, we need the following services available for injection:

  • CQRSLite's Commands and Events bus
  • Our Commands and Events (from Part 2)
  • Our Event Store (from Part 2)
  • AutoMapper
  • Our own Repositories (from Part 3)
  • StackExchange.Redis

That results in this monstrosity of a registry:

public class DefaultRegistry : Registry {  
    #region Constructors and Destructors

    public DefaultRegistry() {
        //Commands, Events, Handlers
        Scan(
            scan => {
                scan.TheCallingAssembly();
                scan.AssemblyContainingType<BaseEvent>();
                scan.Convention<FirstInterfaceConvention>();
            });

        //CQRSLite
        For<InProcessBus>().Singleton().Use<InProcessBus>();
        For<ICommandSender>().Use(y => y.GetInstance<InProcessBus>());
        For<IEventPublisher>().Use(y => y.GetInstance<InProcessBus>());
        For<IHandlerRegistrar>().Use(y => y.GetInstance<InProcessBus>());
        For<ISession>().HybridHttpOrThreadLocalScoped().Use<Session>();
        For<IEventStore>().Singleton().Use<InMemoryEventStore>();
        For<IRepository>().HybridHttpOrThreadLocalScoped().Use(y =>
                new CacheRepository(new Repository(y.GetInstance<IEventStore>()), y.GetInstance<IEventStore>()));

        //AutoMapper
        var profiles = from t in typeof(DefaultRegistry).Assembly.GetTypes()
                        where typeof(Profile).IsAssignableFrom(t)
                        select (Profile)Activator.CreateInstance(t);

        var config = new MapperConfiguration(cfg =>
        {
            foreach (var profile in profiles)
            {
                cfg.AddProfile(profile);
            }
        });

        var mapper = config.CreateMapper();

        For<IMapper>().Use(mapper);

        //StackExchange.Redis
        ConnectionMultiplexer multiplexer = ConnectionMultiplexer.Connect("localhost");
        For<IConnectionMultiplexer>().Use(multiplexer);
    }

    #endregion
}

Holy crap that's a lot of things that need to be injected. But, as we will see, each of these things is actually necessary and provides a lot of value to our application.

(Hold on a second while I smack myself. I sounded way too much like a marketer just now.)

SMACK

Okay, I'm better now.

Requests

I've been using the term "request" liberally throughout this series, and now it's time to truly define what a request is.

In this system, a request is a potential command. That's all. Consuming applications which would like commands issued must submit a request first; that request will be validated and, if found to be valid, mapped to the corresponding command.

A request is, therefore, a C# class which contains the data needed to issue a particular command.

Request 1 - Create Employee

Let's begin to define our requests by first creating a request for creating a new employee.

public class CreateEmployeeRequest  
{
    public int EmployeeID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime DateOfBirth { get; set; }
    public string JobTitle { get; set; }
    public int LocationID { get; set; }
}

WTF Matthew, you say, that looks almost EXACTLY like the CreateEmployeeCommand! Why can't we just use that?! And after I'm done looking around for my parents (nobody calls me Matthew), I can tell you that there are two reasons why we don't reuse the command objects as requests.

First, requests must be validated against the Read Model, whereas commands are assumed to be valid. Second a single request may kick off more than one command, as is the case with this request.

But how do we accomplish that validation, you say? By using one of my favorite NuGet packages of all time: FluentValidation.

The Validation Layer

FluentValidation is a NuGet package which allows us to validate objects and places any validation errors found into the Controller's ModelState. But (unlike StackExchange.Redis) it doesn't come ready for use in a Dependency Injection environment, so we must do some setup.

First, we need a factory which will create the validator objects:

public class StructureMapValidatorFactory : ValidatorFactoryBase  
{
    private readonly HttpConfiguration _configuration;

    public StructureMapValidatorFactory(HttpConfiguration configuration)
    {
        _configuration = configuration;
    }

    public override IValidator CreateInstance(Type validatorType)
    {
        return _configuration.DependencyResolver.GetService(validatorType) as IValidator;
    }
}

Next, in our WebApiConfig.cs file, we need to enable FluentValidation's validator provider using our factory:

public static void Register(HttpConfiguration config)  
{
    ...

    FluentValidationModelValidatorProvider.Configure(config, x => x.ValidatorFactory = new StructureMapValidatorFactory(config));

    ...
}

Finally, we need to register the validator provider in our StructureMap container, which is done in the DefaultRegistry class.

public DefaultRegistry()  
{
    ...
    //FluentValidation 
    FluentValidation.AssemblyScanner.FindValidatorsInAssemblyContaining<CreateEmployeeRequestValidator>()
            .ForEach(result =>
            {
                For(result.InterfaceType)
                    .Use(result.ValidatorType);
            });

With all of that in place, we're ready to begin building our validators!

Create Employee - Validation

Here's the validation rules we need to implement when creating an employee:

  • The Employee ID must not already exist.
  • The First Name cannot be blank.
  • The Last Name cannot be blank.
  • The Job Title cannot be blank.
  • Employees must be 16 years of age or older.

Here's how we would implement such a validator using FluentValidation:

public class CreateEmployeeRequest  
{
    public int EmployeeID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime DateOfBirth { get; set; }
    public string JobTitle { get; set; }
}

public class CreateEmployeeRequestValidator : AbstractValidator<CreateEmployeeRequest>  
{
    public CreateEmployeeRequestValidator(IEmployeeRepository employeeRepo, ILocationRepository locationRepo)
    {
        RuleFor(x => x.EmployeeID).Must(x => !employeeRepo.Exists(x)).WithMessage("An Employee with this ID already exists.");
        RuleFor(x => x.LocationID).Must(x => locationRepo.Exists(x)).WithMessage("No Location with this ID exists.");
        RuleFor(x => x.FirstName).NotNull().NotEmpty().WithMessage("The First Name cannot be blank.");
        RuleFor(x => x.LastName).NotNull().NotEmpty().WithMessage("The Last Name cannot be blank.");
        RuleFor(x => x.JobTitle).NotNull().NotEmpty().WithMessage("The Job Title cannot be blank.");
        RuleFor(x => x.DateOfBirth).LessThan(DateTime.Today.AddYears(-16)).WithMessage("Employees must be 16 years old or older.");
    }
}

Notice that IEmployeeRepository and ILocationRepository are constructor parameters to the validator class. We don't need to do anything else to get those objects injected, as that was taken care of by registering the Repositories and the FluentValidation factory.

There's just one last thing we need to do to have our validation layer fully integrated: whenever validation fails, we need to automatically return HTTP 400 Bad Request. We accomplish this by using an ActionFilter...

public class BadRequestActionFilter : ActionFilterAttribute  
{
    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        if (!actionContext.ModelState.IsValid)
        {
            actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.BadRequest, new ValidationErrorWrapper(actionContext.ModelState));
        }
        base.OnActionExecuting(actionContext);
    }
}

...and registering that action filter in WebApiConfig.

public static class WebApiConfig  
{
    public static void Register(HttpConfiguration config)
    {
        // Web API configuration and services
        config.Filters.Add(new BadRequestActionFilter());
        ...
    }
}

Controller Action

The controller action for creating an employee does two things: it issues the CreateEmployeeCommand, and then issues an AssignEmployeeToLocationCommand. Since this is the only action in the EmployeeController class, the entire class looks like this:

[RoutePrefix("employee")]
public class EmployeeController : ApiController  
{
    private IMapper _mapper;
    private ICommandSender _commandSender;

    public EmployeeController(ICommandSender commandSender, IMapper mapper)
    {
        _commandSender = commandSender;
        _mapper = mapper;
    }

    [HttpPost]
    [Route("create")]
    public IHttpActionResult Create(CreateEmployeeRequest request)
    {
        var command = _mapper.Map<CreateEmployeeCommand>(request);
        _commandSender.Send(command);

        var assignCommand = new AssignEmployeeToLocationCommand(request.LocationID, request.EmployeeID);
        _commandSender.Send(assignCommand);
        return Ok();
    }
}

Since we've now got the EmployeeController written, we can move on to the next request: creating a new location.

Request 2 - Create Location

Now let's build a request and a validator to create a location. Our validation rules for creating a new location look like this:

  1. The location ID must not already exist.
  2. The street address cannot be blank.
  3. The city cannot be blank.
  4. The state cannot be blank.
  5. The postal code cannot be blank.

Implementing those rules results in the following classes:

public class CreateLocationRequest  
{
    public int LocationID { get; set; }
    public string StreetAddress { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string PostalCode { get; set; }
}

public class CreateLocationRequestValidator : AbstractValidator<CreateLocationRequest>  
{
    public CreateLocationRequestValidator(ILocationRepository locationRepo)
    {
        RuleFor(x => x.LocationID).Must(x => !locationRepo.Exists(x)).WithMessage("A Location with this ID already exists.");
        RuleFor(x => x.StreetAddress).NotNull().NotEmpty().WithMessage("The Street Address cannot be null");
        RuleFor(x => x.City).NotNull().NotEmpty().WithMessage("The City cannot be null");
        RuleFor(x => x.State).NotNull().NotEmpty().WithMessage("The State cannot be null");
        RuleFor(x => x.PostalCode).NotNull().NotEmpty().WithMessage("The Postal Code cannot be null");
    }
}

The corresponding controller (LocationController) looks pretty similar to EmployeeController.

[RoutePrefix("locations")]
public class LocationController : ApiController  
{
    private IMapper _mapper;
    private ICommandSender _commandSender;
    private ILocationRepository _locationRepo;
    private IEmployeeRepository _employeeRepo;

    public LocationController(ICommandSender commandSender, IMapper mapper, ILocationRepository locationRepo, IEmployeeRepository employeeRepo)
    {
        _commandSender = commandSender;
        _mapper = mapper;
        _locationRepo = locationRepo;
        _employeeRepo = employeeRepo;
    }

    [HttpPost]
    [Route("create")]
    public IHttpActionResult Create(CreateLocationRequest request)
    {
        var command = _mapper.Map<CreateLocationCommand>(request);
        _commandSender.Send(command);
        return Ok();
    }
}

Looking at this controller, you might be wondering why IEmployeeRepository and ILocationRepository are passed into the constructor when they aren't used by the Create() action. That's because we still have one request left to build: assigning an employee to a location.

Request 3 - Assign Employee to Location

Remember that one of our business rules (from Part 2) says the following:

  1. Employees may switch locations, but they may not be assigned to more than one location at a time.

The request we are going to build now will assign an employee to a new location, as well as remove that employee from the location s/he is currently assigned to.

But, you declare, we defined a command to remove an employee from a location! Is that not also a request? Nope, it's not, and for the same reason that creating an employee results in two commands: one request can result in multiple commands. In this case, assigning an employee to a location could result in one or two commands, depending on if the employee was just created or not.

First, let's build the request and its validator. In this case, we have three validation rules:

  1. The Location must exist.
  2. The Employee must exist.
  3. The Employee must not already be assigned to the given Location.

Implementing those rules result in the following classes:

public class AssignEmployeeToLocationRequest  
{
    public int LocationID { get; set; }
    public int EmployeeID { get; set; }
}

public class AssignEmployeeToLocationRequestValidator : AbstractValidator<AssignEmployeeToLocationRequest>  
{
    public AssignEmployeeToLocationRequestValidator(IEmployeeRepository employeeRepo, ILocationRepository locationRepo)
    {
        RuleFor(x => x.LocationID).Must(x => locationRepo.Exists(x)).WithMessage("No Location with this ID exists.");
        RuleFor(x => x.EmployeeID).Must(x => employeeRepo.Exists(x)).WithMessage("No Employee with this ID exists.");
        RuleFor(x => new { x.LocationID, x.EmployeeID }).Must(x => !locationRepo.HasEmployee(x.LocationID, x.EmployeeID)).WithMessage("This Employee is already assigned to that Location.");
    }
}

Now, all we have to do is write the controller action:

[RoutePrefix("locations")]
public class LocationController : ApiController  
{
    ...
    [HttpPost]
    [Route("assignemployee")]
    public IHttpActionResult AssignEmployee(AssignEmployeeToLocationRequest request)
    {
        var employee = _employeeRepo.GetByID(request.EmployeeID);
        if (employee.LocationID != 0)
        {
            var oldLocationAggregateID = _locationRepo.GetByID(employee.LocationID).AggregateID;

            RemoveEmployeeFromLocationCommand removeCommand = new RemoveEmployeeFromLocationCommand(oldLocationAggregateID, request.LocationID, employee.EmployeeID);
            _commandSender.Send(removeCommand);
        }

        var locationAggregateID = _locationRepo.GetByID(request.LocationID).AggregateID;
        var assignCommand = new AssignEmployeeToLocationCommand(locationAggregateID, request.LocationID, request.EmployeeID);
        _commandSender.Send(assignCommand);

        return Ok();
    }
}

Whew! With that final controller action in place, we have completed building our APIs! Give yourselves a pat on the back for coming this far!

Summary

In this part of our Real-World CQRS/ES with ASP.NET and Redis series, we:

  • Built a Queries API with a DI container and implemented our business queries.
  • Build a Commands API with a DI container and implemented our requests.
  • Used FluentValidation to implement the Commands API's validation layer.

Congratulations! We've completed the build of our real-world CQRS/ES system! All that's left to do is run a few commands and queries to show how the system works, and we will do that in the final part of this series. Keep your eyes (and feed readers) open for Part 5 of Real-World CQRS/ES with ASP.NET and Redis!

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!