asp.net-mvc

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!

What are Areas? - ASP.NET MVC Demystified

ASP.NET MVC relies on certain conventions (both naming and placement) to properly route requests and return views and view models. One of the ways it allows us programmers to manage these conventions is with the use of Areas, which are modules within an MVC project that exist with their own controllers, views, and actions. Let's learn how we can add Areas to an existing project, and what their separation from the main project allows us to do.

What are Areas?

Areas are pieces of an MVC application that do not use the main controllers, actions, and views; rather, they mimic this structure within a specific subfolder in the project directory. In general, we use Areas to split out "semi-independent" sections of our applications, so that they can be maintained and upgraded separately from the rest of the app. Having an area has no functional impact (other than routing, which we will discuss in a bit).

In Visual Studio, we can scaffold an area by right-clicking on the project file and selecting Add -> Area.

A screenshot of an MVC project structure, showing the Areas folder and Billing area

As you might have guessed, Areas are especially useful in larger applications, where these semi-independent portions of the app might be greater in both scope and number.

Registering an Area

Notice that, in the earlier screenshot, the Billing area is a subfolder under the Areas folder. This structure is intentional; an Area by default must exist in the Areas folder and be named appropriately. This is in keeping with MVC's general Separation of Concerns ideology.

Under the Billing folder are folders for Controllers, Models, and Views, as well as another file called BillingAreaRegistration.cs. This file is what registers the new Billing area with the ASP.NET MVC framework. Here's what it looks like:

namespace MVCAreasDemo.Areas.Billing  
{
    public class BillingAreaRegistration : AreaRegistration 
    {
        public override string AreaName 
        {
            get 
            {
                return "Billing";
            }
        }

        public override void RegisterArea(AreaRegistrationContext context) 
        {
            context.MapRoute(
                "Billing_default",
                "Billing/{controller}/{action}/{id}",
                new { action = "Index", id = UrlParameter.Optional }
            );
        }
    }
}

The important part of this file is the RegisterArea() method. In that method, we set up the routes for the area, just like we do for the general application in the RouteConfig file. However, just having this registration isn't enough. We also need to make the following call in our Global.asax file:

protected void Application_Start()  
{
    AreaRegistration.RegisterAllAreas(); //Registers all Areas in the Areas folder
    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
    RouteConfig.RegisterRoutes(RouteTable.Routes);
    BundleConfig.RegisterBundles(BundleTable.Bundles);
}

With both of those pieces, the Billing area becomes accessible in our MVC app.

Controllers in Areas

Notice that any route that directs to this new Billing area must begin with the "Billing" area name. This is so the convention-based routing system can identify which Area to route to when receiving a request like this.

Let's add a simple controller to the Billing area.

public class BillingMainController : Controller  
{
    // GET: Billing/BillingMain
    public ActionResult Index()
    {
        return View();
    }
}

Now that looks pretty darn similar to any other controller we might add to the main MVC app. Areas are only distinguished from the rest of an MVC application by their folder placement and routing; otherwise they behave the same way. But how can we create links to actions in Areas?

Linking to Area Actions

If we want to create links on a Razor view to a controller action in an Area, we need to use an additional parameter called Area:

@Html.ActionLink("BillingMain", "Index", new { Area = "Billing" })

If we click on that link, the follow page shows up:

A screenshot from Chrome, showing the route

Summary

Areas in ASP.NET MVC apps are used to give better structure to our large applications. They exist as modules independent of the main app, with their own controllers, folders, routes, and views. The only setup we need to do is to register the area with the framework, both in the Area's Registration file and in our Global.asax.

For more info, including a discussiong of what happens when two routes conflict with one another, check out Chapter 9 of Professional ASP.NET MVC. You can also take a look at the sample project I whipped up over on GitHub.

Happy Coding!

Using Partial Views - ASP.NET MVC Demystified

When I was writing applications in WebForms (which, thankfully, I no longer do) I often ended up used .ascx files, commonly known as User Controls. Once I moved to MVC, I had to get used to a similar (though not the same) concept: Partial Views.

In this tutorial, we'll walk through a use case for Partial Views and explain what they are and why we need to use them. We'll also walk through the two different render methods: @Html.Partial() and @Html.RenderPartial() and discuss how they are different. Let's get started!

What are Partial Views?

Partial views are .cshtml views that are meant to represent portions of a page that can be used on multiple other views. They are roughly analogous to WebForms' User Controls, but not exactly, mostly because (just like all other views) they don't have a code-behind file.

When you create a partial view, the reason for it to exist is normally that it will be used on many other "parent" views. Let's set up a scenario for this.

Entrees and Ingredients

Say I want to display a list of entrees that I can make for dinner, and along with these entrees I'd like to display the ingredients necessary to make them. In order to do this, I might create classes for Entree and Ingredient like so:

public class Entree  
{
    public int ID { get; set; }
    public string Name { get; set; }
    public List<Ingredient> Ingredients { get; set; }

    public Entree()
    {
        Ingredients = new List<Ingredient>();
    }
}

public class Ingredient  
{
    public string Name { get; set; }
    public decimal Amount { get; set; }
    public string Unit { get; set; }
}

I want to create two views for this: an Index view that will display all the entrees and a Details that will just display one. On each of these views will need to be a list of ingredients, with their units and amounts. Because that display is the same on each view, we can turn that display into a partial view. In our particular case, our partial view will need to have a model bound to it, since it will be displaying the ingredients; of course, it's entirely possible to not use a model on a partial view.

Our partial view (let's call it Ingredients.cshtml) will look like this:

@model List<PartialViewsDemo.Models.Ingredient>

@{
    string display = "";
    foreach (var ingredient in Model)
    {
        display += ingredient.Name + " (" + ingredient.Amount.ToString() + " " + ingredient.Unit + "), ";
    }
    display = display.Remove(display.LastIndexOf(","));
}

<span>@display</span>  

Because this is a partial view, MVC expects it to exist in the Views/Shared folder:

A snapshot of the Views/Shared folder, showing that the Ingredients.cshtml file exists there

Now that we've got our partial view defined, we can define the Entree controller and the Index and Details actions and views. First, here's the Entree controller:

public class EntreeController : Controller  
{
    [HttpGet]
    public ActionResult Index()
    {
        var entrees = EntreeManager.GetAll();
        return View(entrees);
    }

    [HttpGet]
    public ActionResult Details(int id)
    {
        try
        {
            var entree = EntreeManager.GetAll().First(x => x.ID == id);
            return View(entree);
        }
        catch(Exception ex)
        {
            TempData["ErrorMessage"] = "No Entree with ID " + id.ToString() + " exists.";
            return RedirectToAction("Index");
        }
    }
}

In order to use the partial view Ingredients on the Index.cshtml view, we make a call to @Html.Partial(), as shown in the view below:

@model List<PartialViewsDemo.Models.Entree>

@{
    ViewBag.Title = "Index";
}

<h2>All Entrees</h2>

@if(TempData["ErrorMessage"] != null)
{
    <span>@TempData["ErrorMessage"]</span>
}

<table>  
    <thead>
        <tr>
            <th>
                Name
            </th>
            <th>
                Ingredients
            </th>
        </tr>
    </thead>
    <tbody>
        @foreach(var entree in Model)
        {
            <tr>
                <td>
                    @Html.ActionLink(entree.Name, "Details", new { id = entree.ID })
                </td>
                <td>
                    @Html.Partial("Ingredients", entree.Ingredients)
                </td>
            </tr>
        }
    </tbody>
</table>  

On the Details.cshtml view, we make a similar call:

@model PartialViewsDemo.Models.Entree

@{
    ViewBag.Title = "Details";
}

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

<h2>@Model.Name</h2>

<p>Ingredients: @Html.Partial("Ingredients", Model.Ingredients)</p>  

With all of this in place, we can see our completed Index and Details pages.

The Entrees/Index page, showing all three Entrees and the partial call to Ingredients.cshtml

The Entrees/Details/2 page, showing the details for Entree 2 and the partial call to Ingredients.cshtml

An Alternate Rendering Method

In the previous examples we used a call to @Html.Partial() to render the partial views. There is also another call we can make, @Html.RenderPartial(), that will render the partial view in a different manner.

We would use it like this (here's a snippet from the Details view):

<p>Ingredients: @{ Html.RenderPartial("Ingredients", Model.Ingredients); }</p>  

Note that we had to use this call in server-side braces @{}. There's a couple of other differences between @Html.Partial() and @Html.RenderPartial():

  • @Html.Partial() returns a string (meaning you can store the output in a variable), whereas @Html.RenderPartial() returns void and writes directly to the Response.
  • Performance-wise @Html.RenderPartial() will perform better (per an answer on StackOverflow)

I almost always end up using [email protected]() just because I like the syntax better, but the decision is left to you.

Summary

Partial Views in ASP.NET MVC are meant to represent "pieces" of a page that can be rendered in multiple places on different views. They can have models bound to them, and can be rendered using @Html.Partial() or @Html.RenderPartial().

If you'd like to see this demo in a working MVC project, you can download the source from GitHub.

Happy Coding!

Using the _ViewImports.cshtml File to Set Up View Namespaces in MVC 6

As of Beta 5 of ASP.NET 5, there is a new file in the Views folder of an MVC project called _ViewImports.cshtml (here's the GitHub item). In previous Beta versions, this file was called _GlobalImport.cshtml; even though it has been renamed, its functionality is the same in Beta 5 as in Beta 3. What exactly does it do? Let's find out!

Purpose

_ViewImports.cshtml serves one major purpose: to provide namespaces which can be used by all other views. In previous MVC projects, this functionality was provided by the web.config file in the Views folder; since the web.config no longer exists, global namespaces are now provided by this file.

At any rate, a simple _ViewImports.cshtml file might look like this:

@using MyProject.Web
@using Microsoft.Framework.OptionsModel
@addTagHelper "*, Microsoft.AspNet.Mvc.TagHelpers"

The first two lines are fairly easy to understand: we want the MyProject.Web and Microsoft.Framework.OptionsModel namespaces to be available to all views. If we wanted another namespace to be available, we'd simply add it here.

The last line is a bit trickier. We've already seen examples of TagHelpers in previous posts but we've glossed over exactly how they get enabled; I want to remedy that in this post.

The reason that we use the @addTagHelper keyword is because we need the Tag Helpers to actually execute (at some point in the page rendering lifecycle; I'm not sure exactly where yet) rather than just be available to the views. So, we specify a tag helper namespace, and the asterisk (*) specifies that we want all tag helpers in that namespace to be used.

That's it! The _ViewImports.cshtml file is responsible for setting up namespaces that can be accessed by the views in your MVC 6 project, which was previously done by the web.config file in the views folder. IMO this solution is much easier to read, but you should go make up your own mind. Now go out there and start using ASP.NET 5 and MVC 6; trust me, you'll be glad you did.

Happy Coding!