I recently converted the entire blog to use Knockout.js. I find Knockout.js to be very suitable for displaying multiple blog posts along with the ability to comment in a single page.
Using Knockout.js as the scripting engine for blog posts has its own problems. Most blogs rely on web crawlers to be discoverable. With knockout.js, the entire post is converted to a JSON object and is shown to the user neatly by a startup script. Web crawlers do not run any startup scripts. Web crawlers see only the Knockout view (without any Viewmodel placed in the HTML). This makes Knockout.js very SEO unfriendly.
To overcome this problem, I cooked up a neat solution. The solution is to provide a special page for Web crawlers. Whenever a web crawler requests a blog post, I run an ActionFilter on the MVC action, which returns a special page, devoid of any layout. This is good for crawlers because crawlers do not need any layout information or any styling information. All that a crawler wants is contents and referenced links. This is what the ActionFilter sitting on the controller action does. Here is the code for the CrawlerFilter:
public override void OnActionExecuting(ActionExecutingContext filterContext)
string id = (string)filterContext.ActionParameters["id"];
var post = PostComp.GetPost(id);
filterContext.ActionParameters["post"] = post;
var controller = filterContext.Controller as BaseController;
// If you are a crawler, I got you a special page!
var viewName = "Crawler" + filterContext.ActionDescriptor.ActionName;
filterContext.Result = controller.GetViewResult(viewName, post);
// If you are not a crawler, go to the cool page that we got.
In the process, I learnt three coding tips related to Action Filters:
Tip #1: Passing action parameters between controller action method and action filter
The filterContext that is available in ActionFilter methods has an ActionParameters collection which can be used to read and write values. This is the link between sending objects to and fro between the action filter and controller.
Tip #2: Getting the controller from the Action Filter
This one is simple. FilterContext has a controller object. But, this controller object is a ControllerBase object. So, you will not find useful properties in there. You will have to cast this object to the appropriate Controller object.
Tip #3: Creating a ViewResult out of a Model
Creating a ViewResult from a View name is pretty straight-forward. But, if you want to pass a model to the ViewResult, it is not that easy. The Model is a read-only property in the ViewResult class. To create a ViewResult with the appropriate model, we can reuse the View(viewName, model) helper method in the controller class. Unfortunately, this helper method is private to the controller class. So, expose this helper method to the outside world by sub-classing your controller.