It’s probably an understatement to say that ASP.NET Web API has sparked a bit of debate about RESTful design lately.

Web API’s new features like content negotiation, flexible media formatters, and improved JSON formatter are great, but they’ve been presented as features that are tied to the REST paradigm. That may seem troubling if you’re more accustomed to .NET’s RPC endpoints like ASMX, WCF, and even the way ASP.NET MVC controller actions are often used as a makeshift API.

A couple months after the new incarnation of Web API was announced, I’m still seeing a lot of confusion and unhappiness about Web API’s apparent push toward REST. However, what almost everyone has overlooked so far is that Web API supports RPC just as well as REST.


Over-simplifying the difference between REST and RPC

When I say REST, I don’t mean a peaceful afternoon napping in a hammock by the beach, but Representational State Transfer. A RESTful API exposes its data as resources (or nouns) at URIs that clients interact with via HTTP methods like GET and POST (or verbs).

On the other hand, remote procedure call (RPC) refers an API style where endpoints perform arbitrary actions, not necessarily tied to a particular resource. Typically, RPC service methods combine the noun and verb in one method name (e.g. GetPosts or SendInvoice). If you’re using ASMX or WCF today, you’re probably using the RPC approach.

In the context of ASP.NET Web API, the key distinction between REST and RPC is that in a RESTful approach your API consumers interact with public methods corresponding to HTTP verbs (e.g. Get() or Delete(int Id)). If a method doesn’t map to an HTTP verb, it doesn’t fit into a RESTful API.

ASP.NET Web API routing basics

Part of ASP.NET Web API’s magic is that it automatically routes requests to your API methods by convention, matching the request’s HTTP verb to a correspondingly named method in your API class.

For example, this default routing setup in the ASP.NET MVC 4 project template:

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

Means that you could write a class like this:

public class MyApiController: ApiController {
  public string Get() {
    return "Hello World";
  }
}

And then access that Get() method via HTTP by making a GET request to /api/myapi. Similarly, HTTP requests using the POST, PUT, and DELETE verbs would execute methods with names matching those verbs too.

No coloring outside the lines

That automatic RESTful routing is handy, but what if you wanted to add a method that doesn’t correspond to an HTTP verb, like good ‘ol FooBar:

public class MyApiController: ApiController {
  public string Get() {
    return "Hello World";
  }
 
  public string FooBar() {
    return "FooBar";
  }
}

Confusingly enough, a GET request to /api/myapi/foobar returns Hello World instead of FooBar. The reason for that is because our default Web API route sends that request to MyApi with an {id} of FooBar and then Web API executes the Get() method since the request was an HTTP GET.

Web API supports RPC too

Easy REST support by convention is nice, but what’s not obvious in any of the samples I’ve seen is that Web API supports RPC-style routing too.

By adding an {action} parameter to the route, you can override Web API’s default HTTP verb-based routing:

routes.MapHttpRoute(
  name: "DefaultApi",
  routeTemplate: "api/{controller}/{action}/{id}",
  defaults: new { id = RouteParameter.Optional }
);

With this routing configuration, the previous GET request to /api/myapi/ returns a 404, but requests to /api/myapi/get via any HTTP verb return Hello World. That’s not incredibly useful by itself, but it means we can extend the API with arbitrary RPC methods now:

public class MyApiController: ApiController {
  public string Get() {
    return "Hello World";
  }
 
  public string FooBar() {
    return "FooBar";
  }
}

Now, requests to /api/myapi/foobar will return FooBar instead of Hello World.

Gradually migrating from RPC to REST

Since “switching” from REST to RPC was just a routing change, it stands to reason that you could combine both approaches easily enough. Indeed, it’s as simple as using a pair of routes like these:

routes.MapHttpRoute(
  name: "RestApi",
  routeTemplate: "api/{controller}/{id}",
  defaults: new { id = RouteParameter.Optional }
);
 
routes.MapHttpRoute(
  name: "RpcApi",
  // I like "services" as the path prefix for this since it's
  //  where I usually stored ASMX services in the past, but 
  //  you can use anything you want. "rpc" would be good too.
  routeTemplate: "services/{controller}/{action}"
);

One important caveat with this approach is that you need to name your controllers carefully. Even if they’re in different namespaces, you’ll get an ambiguity error if both folders contain an API controller with the same class name.

It should be possible to resolve that issue by adding a constraint to the route, but I haven’t been able to get that working with MapHttpRoute. If you know how to make that work with Web API, please let me know.

Combining both REST and RPC

If you want to live dangerously, you can even mix both approaches in the same API controller. If you include an optional {action} parameter in your API route definition, you get Web API’s verb-based routing and the option to augment those basic methods with specified RPC-style methods.

routes.MapHttpRoute(
  name: "RestPCApi",
  routeTemplate: "api/{controller}/{action}",
  defaults: new { action = RouteParameter.Optional }
);

Using the previous Get/FooBar class, making GET requests to /api/myapi/ return Hello World, while making a request via any HTTP verb to /api/myapi/foobar will return FooBar. Nifty.

I will say that I haven’t tried using this same-controller hybrid approach in any real code yet, so tread lightly (and please leave a comment on this post if you do try this with or without success).

Why bother? Isn’t REST better?

The prevailing popular opinion these days is clearly in favor of RESTful APIs. REST does make for a nice, clean API when it fits well. That’s especially true for relatively simple public-facing APIs (e.g. services like Twitter and Flickr), where everyone benefits from following common conventions.

On the other hand, private APIs sometimes don’t need a full CRUD implementation for every resource. When you only need a few combinations of noun and verb, a simple RPC API is a perfectly valid alternative to the cartesian product of nouns and verbs that a truly RESTful API requires you to implement.

More importantly, RPC routing support means you don’t need to be afraid of Web API, even if you’re heavily invested in ASMX or WCF services.

You can upgrade to Web API’s goodness without completely revamping your existing services (I recently updated one of my largest ASMX-based projects to ASP.NET Web API in less than a day using this RPC routing approach). That means you can maintain RPC support for those older services, while implementing new services in a RESTful style going forward, and migrate at your own pace without any of your services being stuck on a legacy technology.