Monday, April 27, 2015

Microsoft 70-486: Design and implement MVC controllers and actions

Exam Objectives


Apply authorization attributes, global filters, and authentication filters; specify an override filter; implement action behaviors; implement action results; implement model binding

Quick Overview of Training Materials


New Filter Overrides feature in ASP.NET MVC 5 and ASP.NET Web API 2
MSDN Controllers and Action Methods in ASP.NET MVC Applications
MSDN Filtering in ASP.NET MVC

Apply attributes and filters


Filters and attributes available for controllers:
  • AcceptVerbs - Specifies which HTTP verbs this action method will respond to.
  • ActionName - Specifies the name of the method.  This name is used in the route rather than the method name.
  • AllowAnonymous - Permits access to a controller method on a controller with the [Authorize] attribute.
  • AllowHtml - Allows a request to include HTML markup during model binding by skipping request validation for the property. (see "deferred validation" section of post on Implementing Secure Site for usage)
  • AsyncTimeout - Sets the timeout for an asynchronous method, in milliseconds.
  • Authorize - With no parameters set, this property requires that users be logged in.  Roles and Users parameters can be defined to limit access further.
  • Bind - Used to modify the behavior of model binding.  Using Exclude and Include parameters determines which values get bound to the model. Prefix sets the prefix to use on the parameters (example)
  • ChildActionOnly - Indicates that the method can only be called as a child action from a view. (see "Donut Hole Caching" section of post on Caching Strategy for usage)
  • HandleError - Allows you to modify the behavior of error handling. You can specify which exception types to handle, and the view and master view to use for these errors.
  • Http<verb> - Restricts the action method to only accept requests with the specified verb. In order to specify multiple verbs for a method, use [AcceptVerbs]
  • ModelBinder - Specifies the model binder to use.
  • NoAsyncTimeout - Sets the timeout to infinite. Wrapper for AsyncTimeout.
  • NonAction - Specifies that a public method is not invokible as an action method.
  • Obsolete - Marks a method as deprecated
Clearly you wouldn't actually use ALL of these on the same method, but here is just a quick, at-a-glance example of each of the above:

[AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)]
[ActionName("DummyAction")]
[AllowAnonymous]
[AllowHtml]
[AsyncTimeout(1000)]
[Authorize(Roles = "admin", Users = "JohnDoe")]
[Bind(Exclude = "Id", Include = "FirstName,LastName", Prefix = "prefix")]
[ChildActionOnly]
[HandleError(ExceptionType = typeof(NullReferenceException), View = "Error")]
[HttpPost]
[NoAsyncTimeout]
[NonAction]
[Obsolete]
[OutputCache()]
[RequireHttps]
[SessionState(SessionStateBehavior.Disabled)]
[ValidateAntiForgeryToken]
[ValidateInput(true)]
public ActionResult Dummy([ModelBinder(typeof(MyModelBinder))]Object MyObject)
{
    return View();
}

Customizing filters


Since what actually constitutes an "override filter" seems to be a bit nebulous, I'll make sure I cover my bases by looking at both interpretations I ran across in my googling (it didn't help that the exam ref doesn't cover it at all...) Professional ASP.NET covers both varieties of filters.

The first idea of "override filter" is simply the notion of a custom action filter (which probably came up because you "override" the various "on" methods of the ActionFilter class).  To create a custom filter, there are a couple ways you can go about it: Professional ASP.NET mentions implementing the IActionFilter and/or the IResultFilter interfaces, while the tutorial on MSDN inherits from ActionFilterAttribute (which simply implements the two above interfaces plus a third, FilterAttribute).  Depending on where in the lifecycle you want your filtering to take place, you override the On(Action | Result)(Executing | Executed) method.  I've listed them below in what I will admit is a mostly useless code snippet, but the basic idea is that you would put more code into the method overrides:

public class LogAttribute : ActionFilterAttribute
        {
            public override void OnActionExecuted(ActionExecutedContext filterContext)
            {
                base.OnActionExecuted(filterContext);
            }
 
            public override void OnActionExecuting(ActionExecutingContext filterContext)
            {
                base.OnActionExecuting(filterContext);
            }
 
            public override void OnResultExecuted(ResultExecutedContext filterContext)
            {
                base.OnResultExecuted(filterContext);
            }
 
            public override void OnResultExecuting(ResultExecutingContext filterContext)
            {
                base.OnResultExecuting(filterContext);
            }
 
        }

The second type of "override filter" I ran across is apparently new to MVC 5, where it's actually called "Filter overrides".  Since it is specific to MVC 5 it seems less likely to apply to the test material but what the hell, knowledge is power.

To implement filter overrides, create a class implementing the IFilterOverride and FilterAttribute (or a subclass).  Here is a simple code snippet showing the basic format (pretty much verbatim from the Professional ASP.NET book...):

public class ExceptionOverrideAttribute : IOverrideFilter, FilterAttribute
{
    public Type FiltersToOverride
    {
        get { return typeof(IExceptionFilter); }
    }
}

Basically what this code snippet says is, when executing a method with the [ExceptionOverride] attribute, ignore any filters of type IExceptionFilter.

Imran Baloch's blog post on filter overrides goes with a variation on this, creating an override filter that inherits from AuthorizationAttribute instead of FilterAttribute, which means he can pass in the usual authorization parameters and it will act just like a normal Authorize attribute.  However, because typeof(IAuthorizationFilter) is specified as the return type of FiltersToOverride, any Authorize filters higher in the chain will be ignored.  This mean you could set your most restrictive rules in the filter on the controller level, and apply the override filter to methods that have different requirement.  This makes global filters a much more flexible option as well.

Implement action behavior


The chapter from Professional ASP.NET opens with a discussion of controllers in modern web applications that I think is helpful in understanding the role controllers play in the overall behavior of the application.  Controllers essentially act as a bridge between users and the business objects (models) in the application, taking in user input, interacting with and possibly changing models, and returning some form of response, which can be in the from of a view, but can also be JSON data, or a redirect, or an HTTP error message, too.

User requests are first processed for routing, which determines which controller to instantiate, and which action method on that controller to call to process the request.  This is in contrast to the classic web model in which a url directly corresponded to a document on a server file system.  The action filters applied to a method, the interactions of that controller action with various models, and the response served to users all influence the perceived behavior of the application.

Programming ASP.NET points out that controller actions are really just methods like any other when you get right down to it.  These methods are called, parameters are passed, business logic is executed, and some sort of result is returned.

Implement action results


Many times, the result of calling a controller action is to return a response derived from the ActionResult class (other return types are also possible, such as string, int, or bool, but these are ultimately wrapped in an ContentResult and rendered as raw text).  The following are the built in action result types (from the MSDN article):
  • ViewResult - Renders a view as a web page.  The View() helper method will look in the Views/<controller name> folder for a filename corresponding to the controller action.  So calling return View() from the About method on the Home controller, View() would look first for /Views/Home/About.cshtml.  
  • PartialViewResult - Renders a partial view (a section of a page with a master layout that is rendered within another page.)  The PartialView() helper method behaves the same way as View() when searching for the partial view file.
  • RedirectResult - Redirects to a specified URL.
  • RedirectToRouteResult - Redirects to another route, which can be another action on the same controller (RedirectToAction()), or an action on an entirely different controller (RedirectToRoute())
  • ContentResult - Returns string encoded user-defined data, such as plain text or XML. 
  • JsonResult - Returns JSON formatted content.  Commonly used in conjuction with AJAX functionality.
  • JavaScriptResult - Returns JavaScript code that is executed on the client.  Enables targeting functionality at specific browsers or platforms (desktop vs mobile).
  • FileResult - Base class used to send binary file content in a response (user downloads file and views in browser or saves to filesystem).  The three subclasses below all accomplish the same end, they just differ in how the File helper is used:
    • FileContentResult - Created by calling the File helper with a byte array and a mimeType.
    • FilePathResult - Created by calling the File helper with a filepath and a mimeType.
    • FileStreamResult - Created by calling the File helper with a file stream and mimeType.
  • EmptyResult - Return value used if the controller action must be null (void)
  • HttpStatusCodeResult - Action result with a specific HTTP response code and description.


Implement model binding


Because a controller method is ultimately just a regular C# method, it can take values as parameters. These parameters can come from the request body (POST form fields), query string values, cookies, or even the route.  While it is possible to access these collections directly (at least the Request.Form and Request.QueryString collections, anyway... or you can just use Request.Params),  it can be tedious and error prone.  A useful alternative is model binding, in which the method signature of the controller action includes strongly typed parameters, including user defined classes.  The default model binder in MVC will search the request collections for the appropriate data based on variable names (they must match), and will hydrate these parameters based on the fields it is able to find.

As an example, if we have a Person class with three properties as follows:
public class Person
{
    public string name;
    public int age;
    public double weight;
}

It is easy to see how much simpler model binding makes our lives when we compare the process for retrieving the values from the Request collections directly versus using model binding:
public ActionResult Person()
{
    Person p = new Person();
    p.name = Request.Form["name"];
    p.age = Int32.Parse(Request.QueryString["age"]);
    p.weight = Double.Parse(Request.Cookies["weight"].Value);
 
    return View(p);
}
 
public ActionResult PersonWithModelBinding(Person p)
{
    return View(p);
}

There is no need to know which collection to look in, no need to make explicit casts. It just works, automagically.

2 comments:

  1. In the filter override section you write "To implement filter overrides, create a class implementing the IFilterOverride and FilterAttribute (or a subclass)." Then in a code snippet you implement IOverrideFilter, same thing is used in Imran Baloch's blog post.

    Just found your blog, it's great. Thanks a bunch for doing it!

    ReplyDelete
    Replies
    1. Thanks for reading, I'm glad you find it valuable. One of these days I might actually finish the study guide series on 70-486... only been working on it for... 18 months or so lol.

      You are right about the interface being implemented in both my code snipped and Imran Baloch's. The difference is in what class is being extended.

      Delete