Monthly Archives: December 2008

Asp.Net MVC: Support both static and dynamic views

Lets try this again… the first time I wrote this post, my computer restarted and I lost it entirely 🙁

I have created a handful of sites that require both "static" pages and dynamic pages.  I put static in quotes because there is a dedicated page with unique content that can’t be created with a wysiwyg editor.  For example, show a list of the last 10 visitor ip addresses.  In the past, I’ve used a combination of webforms and isapi_rewrite rules to accomplish what I needed.  I’ve felt that the isapi_rewrite part was a little flaky, but it worked and isapi_rewrite is extremely fast.  Anyway, I’ve been using the Asp.Net MVC bits since preview 1 and have become fairly happy using it as an alternative to webforms.  I wanted to see how hard it was to allow for a situation like this with mvc.  Turns out, it’s pretty damn easy, and fairly clean (maintenance wise).

I tried a few approaches before finding this solution.  There may be other ways to accomplish this same method, but the following solution works.  I’m not going to publish all of the code, but I’ll go through the core concepts:

First, this method only supports single hierarchy url depths.  Meaning, the dynamic pages can have urls like /MyUrl, /my-url, or /this-is_valid.  They cannot (currently) have urls like: /my/url, blog/2008/my-blog-title.  Again, I doubt it would be hard to support cases like that, but I wanted to keep things simple for this explanation.  Onto the setup:

The controller setup is as follows:

  • HomeController
    • Index action – Consider this a "static" page with some custom content on it.  It has a view associated with it in the Views directory.
    • InvalidPage action – Used to show the 404 error
  • DynamicPageController
    • Index action – Only action and it takes a string parameter named id.  Id stands for the friendlyName of the dynamic page to show.  This action grabs the page information from the db and sends it to its related view, that just acts as a template for all of the dynamic pages.  Side note: I left the parameter named id so that I wouldn’t have to add an extra route to accommodate a more appropriate friendlyName parameter.  Minor, and largely irrelevant.

In order to have MVC attempt to locate dynamic pages, I had to create my own controller factory class.  I wanted the original logic of finding a controller, but if one was not found, I wanted send the logic to the DynamicPageController Index action.  This allows for the "static" pages to take precedence and then fallback to the dynamic page content.  The following code does just that:

public class BiaControllerFactory : DefaultControllerFactory
{
    public override IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
    {
        try
        {
            return base.CreateController(requestContext, controllerName);
        }
        catch (Exception ex)
        {
            requestContext.RouteData.Values["id"] = controllerName;

            controllerName = "DynamicPage";
            requestContext.RouteData.Values["controller"] = controllerName;
            return base.CreateController(requestContext, controllerName);
        }
    }
}

It’s pretty straightforward.  The base.CreateController call will throw an exception when it can’t find the specified controller.  Since this example only allows for dynamic urls like /my-dynamic-page, the default routes interpret that as a my-dynamic-page controller, index action.  Easy enough to set the correct RouteData values and send the logic to the DynamicPageController Index action, with the correctly specified id parameter.

If the DynamicPageController Index action doesn’t find the dynamic page specified by the id parameter, it’ll redirect to the HomeController InvalidPage action.  As a bonus, I setup the InvalidPage action to send the proper 404 response code back to the client.

To have MVC use the new controller factory class, add the following in the same place that you define your routes (Usually Global.asax):

ControllerBuilder.Current.SetControllerFactory(new BiaControllerFactory());

That’s basically it… I plan to use this method for smaller sites that require some custom coded pages as well as simple CMS capabilities.  Let me know if you have any questions, comments, or suggestions related to this.

Vino 100

Over the weekend, I had the pleasure of going to Vino 100.  It’s a small wine shop in Wauwatosa, WI (they also have one in the 3rd Ward).  I should begin with a bit of background on my wine knowledge, though.. While I’m not a huge wine drinker, I do enjoy a glass now and then.  Half priced wino wednesdays have really brought out my inner wino and I’ve been drinking wine more frequently lately.  I just don’t have a lot of knowledge when it comes to relating wine names with flavors. 

Vino 100 takes a lot of the unknowns out of choosing wine.  The store is laid out with red wines along one wall and whites along another, with tasting tables and a small bar occupying the space between walls.  The genius is how they’ve organized the wines, though.  The wines are organized by taste rather than name.  For the white wine wall, they start with fruity, sweet wines on the left and gradually move to dry, full bodied wines on the right.  Each wine has a description and a visual representation for the wine’s flavor and body.  If the wine is on their menu, you can even sample the wine before making a purchase.  The red wine wall is organized in much the same way.

For me, they really hit it out of the park.  Instead of catering to the wine connoisseur, they cater to novices like myself and really make it easy to improve wine knowledge without an unexpected flavor or taste.