I really like the .NET WebAPI, it is a simple and lightweight way to create REST services, and it is proving to be pretty powerful if you, like me, like heavy client side applications that communicate with the server through a public API.
This post is a walkthrough on how to enable CORS (Cross-origin resource sharing) for new or existing WebAPI services that you have. If you are unfamiliar with CORS, then the wikipedia explanation is good enough for what you need to know.
Please note that is article is based on a pre-release version of the WebAPI 2.0 that is gonna ship with Visual Studio 2013. This post describes how you can get WebAPI Cors support to work with Visual Studio 2012 Update 3.
I have uploaded the code for this tutorial to github, if you like to look at the complete code. In the following I will describe the individual steps, taken to create the sample project.
Step 1: Open/Create your web project
Your project can be anytype of ASP.NET web project, which is one of the cool things about the WebAPI it works with both MVC and web forms. The only thing that is required of you project for this tutorial is that you choose .net framework 4.5 (so if you are working with an existing project you will have to change the framework version).
I started with an empty solution.
Step 2: Add the CORS WebAPI
To enable CORS for your WebAPI services you have to install the library for it, as it has yet to be put into the framework. You do so by opening the package manager console (Tools > Library Package Manager > Package Manager Console). Type
Install-Package Microsoft.AspNet.WebApi.Cors -IncludePrerelease
As you can see from the -IncludePrerelease argument, we are actually installing a none finished product, but from my testing the release candidate seems to be pretty stable.
In addition to installing the CORS library you also need to install the release candidate of the WebAPI as the CORS library won’t work with the version from the .net framework.
Install-Package Microsoft.AspNet.WebApi -IncludePrerelease
After doing these two steps you packages.config should at least contain:
If you forget to add/update your project with the RC version of the Microsoft.AspNet.WebApi you will get an error like this when you try to use your WebAPI services:
[FieldAccessException: Attempt by method 'System.Web.Http.GlobalConfiguration..cctor()' to access field 'System.Web.Http.GlobalConfiguration.CS$<>9__CachedAnonymousMethodDelegate2' failed.]
System.Web.Http.GlobalConfiguration..cctor() +48
[TypeInitializationException: The type initializer for 'System.Web.Http.GlobalConfiguration' threw an exception.]
System.Web.Http.GlobalConfiguration.get_Configuration() +0
BalkanButton.Global.Application_Start(Object sender, EventArgs e) in j:\Projects\BambinoButton\BambinoButton\Global.asax.cs:16
[HttpException (0x80004005): The type initializer for 'System.Web.Http.GlobalConfiguration' threw an exception.]
System.Web.HttpApplicationFactory.EnsureAppStartCalledForIntegratedMode(HttpContext context, HttpApplication app) +9913001
System.Web.HttpApplication.RegisterEventSubscriptionsWithIIS(IntPtr appContext, HttpContext context, MethodInfo[] handlers) +118
System.Web.HttpApplication.InitSpecial(HttpApplicationState state, MethodInfo[] handlers, IntPtr appContext, HttpContext context) +172
System.Web.HttpApplicationFactory.GetSpecialApplicationInstance(IntPtr appContext, HttpContext context) +336
System.Web.Hosting.PipelineRuntime.InitializeApplication(IntPtr appContext) +296
[HttpException (0x80004005): The type initializer for 'System.Web.Http.GlobalConfiguration' threw an exception.]
System.Web.HttpRuntime.FirstRequestInit(HttpContext context) +9927220
System.Web.HttpRuntime.EnsureFirstRequestInit(HttpContext context) +101
System.Web.HttpRuntime.ProcessRequestNotificationPrivate(IIS7WorkerRequest wr, HttpContext context) +254
Step 3: Adding the code
Now everything is ready project wise, all that is left is to add some code.
First we need to wire you the WebAPI, that is done in application start, so add a global.asax file. Here all we need to do is to add the following lines to Enable CORS. We also enable Attribute routes another new feature of the WebAPI 5.0 version, but more on that later.
using System.Web.Http;
var config = GlobalConfiguration.Configuration;
config.EnableCors();
config.MapHttpAttributeRoutes();
Of course if you want to use old school convention based routing that can be setup too, e.g. like this
RouteTable.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = System.Web.Http.RouteParameter.Optional }
);
Now we are ready to add our WebAPI controller class. For the purpose of the tutorial I just went with the default one. Before the controller will work we need to do two things.
Firstly enable CORS by adding the following attribute to the class:
using System.Web.Http.Cors;
[EnableCors("*","*","*")]
Note that when you like me are lazy and just type stars everywhere everyone can use your REST endpoint and all the HTTP Verbs can be used, so for real life you should probably be a little more cautious.
Secondly we have to set up the route (the url) for the REST service, that can be done with the RoutePrefix attribute.
[RoutePrefix("api/v1")]
And then we add the following to our get method.
[HttpGet("values")]
So when we start our application we can access our Cors enabled REST endpoint at, http://server/api/v1/values – pretty slick.
With the attribute routing it is easy to have different versions of a public API, if you want to read more about what’s possible with the attribute routing, then check out the Attribute Routing article on asp.net.
Calling the endpoint from a remote site, is now possible because we enabled Cors, it can be done easily with jQuery in the following way.
[js]
jQuery.ajax({url: ‘http://localhost:63327/api/v1/values’, success: function(res)
{
console.log(res);
}});
[/js]
If you want to post to your API REST service from javascript, you have to construct your post in the following way. Note this example is from another WebAPI solution, the thing to note is that you must specify the contentType otherwise WebAPI will not be able to deserialize the POST body correct.
[js]
var d = { ‘id’: 1, ‘free’: true };
$.ajax({
type: "POST",
contentType: "application/json;charset=UTF-8",
url: "/api/EditSong",
data: JSON.stringify(d),
success: function (data) {
},
error: function (error) {
console.log(jQuery.parseJSON(error.responseText));
}
});
[/js]
The method that I’m posting to looks like this
[c]
public void Post(EditSongDto song)
{
//Do something
}
//This is my DTO
public class EditSongDto
{
public long id { get; set; }
public bool free { get; set; }
}
[/c]