|
|
|
Related Articles
|
|
|
|
| |
The ASP.NET MVC team is in the final stages of finishing up a new "Preview 4" release that they hope to ship later this week. The Preview 3 release focused on finishing up a lot of the underlying core APIs and extensibility points in ASP.NET MVC. Starting with Preview 4 this week you'll start to see more and more higher level features begin to appear that build on top of the core foundation and add nice productivity. There are a bunch of new features and capabilities in this new build - so much in fact that I decided I needed two posts to cover them all. This first post will cover the new Caching, Error Handling and Security features in Preview 4, as well as some testing improvements it brings. My next post will cover the new AJAX features being added with this release as well. Understanding Filter Interceptors Action Filter Attributes are a useful extensibility capability in ASP.NET MVC that was first added with the "Preview 2" release. These enable you to inject code interceptors into the request of a MVC controller that can execute before and after a Controller or its Action methods execute. This enables some nice encapsulation scenarios where you can easily package-up and re-use functionality in a clean declarative way. Below is an example of a super simple "ScottGuLog" filter that I could use to log details about exceptions raised during the execution of a request. Implementing a custom filter class is easy - just subclass the "ActionFilterAttribute" type and override the appropriate methods to run code before or after an Action method on the Controller is invoked, and/or before or after an ActionResult is processed into a response. Using a filter within a ASP.NET MVC Controller is easy - just declare it as an attribute on an Action method, or alternatively on the Controller class itself (in which case it will apply to all Action methods within the Controller): Above you can see an example of two filters being applied. I've indicated that I want my "ScottGuLog" to be applied to the "About" action method, and that I want the "HandleError" filter to be applied to all Action methods on the HomeController. Previous preview releases of ASP.NET MVC enabled this filter extensibility, but didn't ship with pre-built filters. ASP.NET Preview 4 now includes several useful filters for handling output caching, error handling and security scenarios. OutputCache Filter The [OutputCache] filter provides an easy way to integrate ASP.NET MVC with the output caching features of ASP.NET (with ASP.NET MVC Preview 3 you had to write code to achieve this). To try this out, modify the "Message" value set within the "Index" action method of the HomeController (created by the VS ASP.NET MVC project template) to display the current time: When you run your application you'll see that a timestamp updates each time you refresh the page: We can enable output caching for this URL by adding the [OutputCache] attribute to the our Action method. We'll configure it to cache the response for a 10 second duration using the declaration below: Now when you hit refresh on the page you'll see that the timestamp only updates every 10 seconds. This is because the action method is only being called once every 10 seconds - all requests between those time intervals are served out of the ASP.NET output cache (meaning no code needs to run - which makes it super fast). In addition to supporting time duration, the OutputCache attribute also supports the standard ASP.NET output cache vary options (vary by params, headers, content encoding, and custom logic). For example, the sample below would save different cached versions of the page depending on the value of an optional "PageIndex" QueryString parameter, and automatically render the correct version depending on the incoming URL's querystring value: You can also integrate with the ASP.NET Database Cache Invalidation feature - which allows you to automatically invalidate the cache when a database the URL depends on is modified (tip: the best way to-do this is to setup a CacheProfile section in your web.config and then point to it in the OutputCache attribute). HandleError Filter The [HandleError] filter provides a way to declaratively indicate on a Controller or Action method that a friendly error response should be displayed if an error occurs during the processing of a ASP.NET MVC request. To try this out, add a new "TestController" to a project and implement an action method that raise an exception like below: By default when you point your browser at this URL, it will display a default ASP.NET error page to remote users (unless you've gone in and configured a <customErrors> section in your web.config file): We can change the HTML error displayed to be a more friendly end-user message by adding a [HandleError] attribute to either our Controller or to an Action method on our Controller: The HandleError filter will catch all exceptions (including errors raised when processing View templates), and display a custom Error view response when they occur. By default it attempts to resolve a View template in your project called "Error" to generate the response. You can place the "Error" view either in the same directory as your other Controller specific views (for example: \Views\Test for the TestController above), or within the \Views\Shared folder (it will look first for a controller specific error view, and then if it doesn't find one it will look in the shared folder - which contains views that are shared across all controllers). Visual Studio now automatically adds a default "Error" view template for you inside the \Views\Shared folder when you create new ASP.NET MVC Projects starting with Preview 4: When we add a [HandleError] attribute to our TestController, this will by default show remote users an html error page like below (note that it picks up the master page template from the project so that the error message is integrated into the site). You can obviously go in and customize the Error view template to display whatever HTML and/or friendlier customer error message you want - below is simply what you get out of the box: To help developers, the default Error view template provided by the new project template in Visual Studio is written to display additional error stack trace information when you are browsing the application locally: You can turn this off either by deleting the code from the Error view template, or by setting <customErrors> to "off" inside your web.config file. By default the [HandleError] filter will catch and handle all exceptions that get raised during the request. You can alternatively specify specific exception types you are interested in catching, and specify custom error views for them by specifying the "ExceptionType" and "View" properties on [HandleError] attributes: In the code above I'm choosing to display custom error views for SqlExceptions and NullReferenceExceptions. All other exceptions will then use the default "Error" view template. Authorize Filter The [Authorize] filter provides a way to declaratively control security access on a Controller or Action method. It allows you to indicate that a user must be logged in, and optionally require that they are a specific user or in a specific security role in order to gain access. The filter works with all types of authentication (including Windows as well as Forms based authentication), and provides support for automatically redirecting anonymous users to a login form as needed. To try this out, add an [Authorize] filter to the "About" action in the HomeController created by default with Visual Studio: Declaring an [Authorize] attribute like above indicates that a user must be logged into the site in order for them to request the "About" action. When non-logged-in users attempt to hit the /Home/About URL, they will be blocked from gaining access. If the web application is configured to use Windows based authentication, ASP.NET will automatically authenticate the user using their Windows login identity, and if successful allow them to proceed. If the web application is configured to use Forms based authentication, the [Authorize] attribute will automatically redirect the user to a login page in order to authenticate (after which they'll have access): The [Authorize] attribute optionally allows you to grant access only to specific users and/or roles. For example, if I wanted to limit access to the "About" action to just myself and Bill Gates I could write: Typically for all but trivial applications you don't want to hard-code user names within your code. Instead you usually want to use a higher-level concept like "roles" to define permissions, and then map users into roles separately (for example: using active directory or a database to store the mappings). The [Authorize] attribute makes it easy to control access to Controllers and Actions using a "Roles" property: The [Authorize] attribute does not have a dependency on any specific user identity or role management mechanism. Instead it works against the ASP.NET "User" object - which is extensible and allows any identity system to be used. AccountController Class I mentioned above that the [Authorize] attribute can be used with any authentication or user identity management system. You can write or use any custom login UI and/or username/password management system you want with it. To help you get started, though, the ASP.NET MVC Project Template in Visual Studio now includes a pre-built "AccountController" and associated login views that implement a forms-authentication membership system with support for logging in, logging out, registering new users, and changing passwords. All of the views templates and UI can be easily customized independent of the AccountController class or implementation: The Site.master template also now includes UI at the top-right that provides login/logout functionality. When using forms-based authentication it will prompt you to login if you are not currently authenticated: And it displays a welcome message along with a logout link if you are authenticated on the site: Clicking the Login link above takes users to a Login screen like below that they can use to authenticate: New users can click the register link to create new accounts: Error handing and error display is also built-in: The AccountController class that is added to new projects uses the built-in ASP.NET Membership API to store and manage user credentials (the Membership system uses a provider API allowing any back-end storage to be plugged-in, and ASP.NET includes built-in providers for Active Directory and SQL Server). If you don't want to use the built-in Membership system you can keep the same AccountController action method signatures, View templates, and Forms Authentication ticket logic, and just replace the user account logic within the AccountController class. For the next ASP.NET MVC preview release we are planning to encapsulate the interaction logic between the AccountController and the user identity system behind an interface - which will make it easier to plug-in your own user storage system (without having to implement a full membership provider) as well as to easily unit test both it and the AccountController. Our hope is that this provides a nice way for people to quickly get started, and enable them to have a working end to end security system as soon as they create a new project. Testing TempData One last improvement to touch on in this first preview 4 post is some improvements being made on the Controller class that allow you to more easily unit test the TempData collection. The TempData property allows you to store data that you want to persist for a future request from a user. It has the semantic of only lasting one future request (after which it is removed). It is typically used for MVC scenarios where you want to perform a client-side redirect to change the URL in the browser, and want a simple way to store scratch data. With previous ASP.NET MVC Previews you had to mock objects in order to test the TempData collection. With Preview 4 you no longer need to mock or setup anything. You can now add and verify objects within the Controller's TempData collection directly within your unit tests (for example: populate a controller's TempData property before calling its action method, or verify that the action updated the TempData after the action returned). The actual storage semantics of the TempData collection is now encapsulated within a separate TempDataProvider property. Conclusion Hopefully the above post provides a quick look at a number of the new features and changes coming with ASP.NET MVC Preview 4. My next post on ASP.NET MVC Preview 4 will cover the new AJAX functionality that has been added, and demonstrate how to take advantage of it. Hope this helps, Scott |
| |
|
| |
| |
This morning we released the Preview 3 build of the ASP.NET MVC framework. I blogged details last month about an interim source release we did that included many of the changes with this Preview 3 release. Today's build includes some additional features not in last month's drop, some nice enhancements/refinements, as well as Visual Studio tool integration and documentation. You can download an integrated ASP.NET MVC Preview 3 setup package here. You can also optionally download the ASP.NET MVC Preview 3 framework source code and framework unit tests here. Controller Action Method Changes ASP.NET MVC Preview 3 includes the MVC Controller changes we first discussed and previewed with the April MVC source release, along with some additional tweaks and adjustments. You can continue to write controller action methods that return void and encapsulate all of their logic within the action method. For example: which would render the below HTML when run: Preview 3 also now supports using an approach where you return an "ActionResult" object that indicates the result of the action method, and enables deferred execution of it. This allows much easier unit testing of actions (without requiring the need to mock anything). It also enables much cleaner composition and overall execution control flow. For example, we could use LINQ to SQL within our Browse action method to retrieve a sequence of Product objects from our database and indicate that we want to render a View of them. The code below will cause three pieces of "ViewData" to be passed to the view - "Title" and "CategoryName" string values, and a strongly typed sequence of products (passed as the ViewData.Model object): One advantage of using the above ActionResult approach is that it makes unit testing Controller actions really easy (no mocking required). Below is a unit test that verifies the behavior of our Browse action method above: We can then author a "Browse" ViewPage within the \Views\Products sub-directory to render a response using the ViewData populated by our Browse action: When we hit the /Products/Browse/Beverages URL we'll then get an HTML response like below (with the three usages of ViewData circled in red): Note that in addition to support a "ViewResult" response (for indicating that a View should be rendered), ASP.NET MVC Preview 3 also adds support for returning "JsonResult" (for AJAX JSON serialization scenarios), "ContentResult" (for streaming content without a View), as well as HttpRedirect and RedirectToAction/Route results. The overall ActionResult approach is extensible (allowing you to create your own result types), and overtime you'll see us add several more built-in result types. Improved HTML Helper Methods The HTML helper methods have been updated with ASP.NET MVC Preview 3. In addition to a bunch of bug fixes, they also include a number of nice usability improvements. Automatic Value Lookup With previous preview releases you needed to always explicitly pass in the value to render when calling the Html helpers. For example: to include a value within a <input type="text" value="some value"/> element you would write: The above code continues to work - although now you can also just write: The HTML helpers will now by default check both the ViewData dictionary and any Model object passed to the view for a ProductName key or property value to use. SelectList and MultiSelectList ViewModels New SelectList and MultiSelectList View-Model classes are now included that provide a cleaner way to populate HTML dropdowns and multi-select listboxes (and manage things like current selection, etc). One approach that can make form scenarios cleaner is to instantiate and setup these View-Model objects in a controller action, and then pass them in the ViewData dictionary to the View to format/render. For example, below I'm creating a SelectList view-model class over the set of unique category objects in our database. I'm indicating that I want to use the "CategoryID" property as the value of each item in the list, and the "CategoryName" as the display text. I'm also setting the list selection to the current CategoryId of the Product we are editing: Within our view we then just have to write the below code to indicate that we want to create a drop-downlist against the SelectList we put into ViewData: This will then render the appropriate drop down with items and selection for us at runtime: Built-in error validation support isn't included with our HTML helpers yet (you currently need to write code for this) - but will show up in the future, which will make form editing scenarios even easier. You'll also start to see ASP.NET AJAX helper methods show up in future preview releases as well, which will make it easier to integrate AJAX into MVC applications with a minimum of code. URL Routing Improvements ASP.NET MVC Preview 3 includes a number of improvements to the URL routing system. URL routing is one of the most "fundamental" components of a web MVC framework to get right, hence the reason we've spent a lot of focus the first few previews getting this area nailed. Our new URL routing engine will ship in .NET 3.5 SP1 this summer, and will support both Web Forms and MVC requests. ASP.NET MVC will be able to use the built-in .NET 3.5 SP1 routing engine when running on .NET 3.5 SP1. ASP.NET MVC will also include its own copy of the assembly so that it can also work on non-SP1 systems. Some of the URL Routing Improvements in the Preview 3 release include: MapRoute() and IgnoreRoute() helper methods ASP.NET MVC Preview 3 includes new "MapRoute" and "IgnoreRoute" helper methods that you can use to more easily register routing rules. MapRoute() provides an easy way to add a new MVC Route rule to the Routes collection. IgnoreRoute() provides an easy way to tell the URL routing system to stop processing certain URL patterns (for example: handler .axd resources in ASP.NET that are used to serve up JavaScript, images, etc). Below is an example of the default RegisterRoutes() method within Global.asax when you create a new ASP.NET MVC project where you can see both of these new helper methods in action. The MapRoute() helper method is overloaded and takes two, three or four parameters (route name, URL syntax, URL parameter default, and optional URL parameter regular expression constraints). You can call MapRoute() as many times as you want to register multiple named routes in the system. For example, in addition to the default convention rule, we could add a "Products-Browse" named routing rule like below: We can then refer to this "Products-Browse" rule explicitly within our Controllers and Views when we want to generate a URL to it. For example, we could use the Html.RouteLink view helper to indicate that we want to link to our "Products-Browse" route and pass it a "Food" category parameter using code in our view template like below: This view helper would then access the routing system and output an appropriate HTML hyperlink URL like below (note: how it did automatic parameter substitution of the category parameter into the URL using the route rule): We could alternatively use the new Url.RouteUrl(routeName, values) within views if we wanted to just retrieve the URL for a named route (and not output the <a> html element). We could also use the new RedirectToRoute(routeName, values) helper method on the Controller base class to issues browser redirects based on named routing rules. Richer URL Route Mapping Features ASP.NET MVC Preview 3 also supports a bunch of new URL route mapping features. You can now include "-", ".", ";" or any other characters you want as part of your route rules. For example, using a "-" separator you can now parse the language and locale values from your URLs separately using a rule like below: This would pass appropriate "language", "locale", and "category" parameters to a ProductsController.Browse action method when invoked: URL Route Rule Example URL Parameters Passed to Action method {language}-{locale}/products/browse/{category} /en-us/products/browse/food language=en, locale=us, category=food /en-uk/products/browse/food language=en, locale=uk, category=food Or you can use the "." file extension type at the end of a URL to determine whether to render back the result in either a XML or HTML format: This would pass both "category" and a "format" parameters to the ProductsController.Browse action method when invoked: URL Route Rule Example URL Parameters Passed to Action method products/browse/{category}.{format} /products/browse/food.xml category=food, format=xml /products/browse/food.html category=food, format=html ASP.NET MVC Preview 3 also supports wildcard route rules (these were also in Preview 2). For example, you can indicate in a rule to pass all remaining URI content on as a named parameter to an action method: This would pass a "contentUrl" parameter to the WikiController.DisplayPage action method when invoked: URL Route Rule Example URL Parameters Passed to Action method Wiki/Pages/{*contentUrl} /Wiki/Pages/People/Scott contentUrl="People/Scott" /Wiki/Pages/Countries/UK contentUrl="Countries/UK" These wildcard routes are very useful to look at if you are building a blogging, wiki, cms or other content based system. Summary Today's Preview 3 release of ASP.NET MVC includes a bunch of improvements and refinements. We are starting to feel good about the URL routing and Controller/Action programming model of MVC, and feel that those areas are starting to bake really well. In future preview releases you'll start to see more improvements higher-up the programming model stack in areas like Views (html helpers, validation helpers, etc), AJAX, sub-controllers and site composition, deeper Login, Authentication, Authorization and Caching integration, as well as data | | | |