One (more) ASP.NET

This is an old post and doesn't necessarily reflect my current thinking on a topic, and some links or images may not work. The text is preserved here for posterity.

Visual Studio 2012 shipped last week, so I'm working on a branch in Octopus Deploy to upgrade the ASP.NET frontend of Octopus to ASP.NET MVC 4.0. My main reason for upgrading was the inbuilt bundling and minification support, which is awesome.

Octopus provides a RESTful API (browse to /api on your Octopus server), which serves up documents in JSON like this:

[
  {
    "Id": "DeploymentEnvironments-1",
    "Name": "Production",
    "Description": "A production environment",
    "SortOrder": 0,
    "Links": {
      "Self": "/api/environments/DeploymentEnvironments-1",
      "Machines": "/api/environments/DeploymentEnvironments-1/machines"
    }
  }
]

Web API wasn't stable when I first built it, so I implemented it on top of ASP.NET MVC 3.0 using a custom ActionResult that used Json.NET. It doesn't support content negotiation, so everything is JSON-only. So when it came time to upgrade, I was also excited to try and convert my REST API to use ASP.NET Web API.

The ASP.NET team has been focussed on "one ASP.NET" for a while. Scott Hanselman sums it up with a nice image, which I'll shamelessly hotlink:

One ASP.NET to bring them all and in the darkness bind them

ASP.NET MVC and Web Forms have definitely done a lot of work to integrate, but ASP.NET Web API, I think, has a long way to go.

For instance, you'll be familiar with routing in ASP.NET:

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

You can also do routing in ASP.NET Web Forms:

routes.MapPageRoute(
  "products-browse", 
  "products/{category}", 
  "~/Products.aspx"
);

Of course, as one would expect, both of these build on top of a shared routing system provided by the green ASP.NET box at the bottom of the diagram.

Naturally, you can also do routing in ASP.NET Web API:

routes.MapHttpRoute(
  "DefaultApi", 
  "api/{controller}/{id}", 
  new { id = RouteParameter.Optional }
);

You would expect this to be built on top of the same routing framework. Right? Right? Wrong!

ASP.NET MVC and Web Forms use classes like RouteCollection and the RouteBase abstract class. ASP.NET Web API on the other hand makes use of HttpRouteCollection and IHttpRoute. Of course, the reason they need a second copy becomes clear when you see the vast differences between the two.

For example, IHttpRoute has methods like:

public interface IHttpRoute
{
    IHttpRouteData GetRouteData(string virtualPathRoot, HttpRequestMessage request);  
    IHttpVirtualPathData GetVirtualPath(HttpRequestMessage request, IDictionary<string, object> values);
}

While ASP.NET's routing RouteBase class has completely different methods like:

public abstract class RouteBase 
{
    public abstract RouteData GetRouteData(HttpContextBase httpContext); 
    public abstract VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values);
}

My guess is that this duplication exists because ASP.NET Web API is designed to also operate outside of IIS. But if we're serious about "One ASP.NET", why not do it properly and make the whole stack run outside of IIS? (It can be done now actually, but it is very painful and breaks a lot).

The madness doesn't stop here. ASP.NET Web API ApiController classes don't inherit from the ASP.NET MVC Controller class. ASP.NET gives you a UrlHelper class to generate URI's from the route table, while ASP.NET Web API has its own completely separate (and less nice) UrlHelper class. ASP.NET provides some well known basic types like HttpRequest and HttpContext. Web API has its own alternatives. Filters are the same but different. Both stacks have their own dependency resolver. Both have their own model binders. Both have their own model state/validation. The list goes on and on.

Here's a more correct image:

One more ASP.NET

While both frameworks work, it's hard to say they "work together". I'm finding myself creating a bunch of wrapper classes and adapters so that I can do basic things like generate URL's when I'm in either context. It all feels very messy.

Right now, the only reason I can think of moving to ASP.NET Web API is content negotiation, but making ASP.NET Web API generate nice looking XML and JavaScript off the same model also seems to be a lot of work, so I'm not sure there's much to gain there.

(Part of me wonders whether content negotiation and a few REST idioms should just have been implemented in ASP.NET MVC, which would really obsolete the whole Web API project.)

If I could have one wish for ASP.NET 5.0, it would be to stop having "one ASP.NET", and instead to have one ASP.NET.