One (more) ASP.NET
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:
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:
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.