Archive for March, 2012

Cross-Domain Calls for a WCF Service

As I develop more services for mobile devices (and mobile web in particular), cross-domain calls are becoming more frequent. Essentially, a cross-domain call happens when a web page uses JavaScript to access resources at a different URL. This can be as complex as hosting services completely separate from the app in another web site, or in another subdomain or on another port, and also can happen simply because the web page is calling a service over SSL.

The problem occurs because the browser only trusts calls from the exact same domain, unless the server says that it trusts your domain (or all domains). This is by design.

So the resolution is to have the server tell the clients what domains it trusts for cross-site calls. Seems simple, but of course there is more to it than that.

When the browser detects a cross-domain call, it behaves differently if your service is configured correctly. If you were expecting your JavaScript code to perform a POST to a REST URL, that is no longer what happens, at least initially. Instead of performing the POST, the browser actually sends an OPTIONS verb call to your REST URL. This is called a preflight request. Your service needs to respond to the preflight request containing the OPTIONS verb and let the browser know which parameters are acceptable on a cross-site call. If everything matches up and your client is in an allowed domain, then the browser performs the original POST that you expected and everything continues on normally.

If your service is not configured correctly, the browser doesn’t cooperate and you don’t see anything at all. Again, this is by design.

Configuring The Service

From an HTTP point of view, what we need to do in code is add the following name/value headers to our HTTP responses:

  • Access-Control-Allow-Origin
  • Access-Control-Allow-Methods
  • Access-Control-Allow-Headers
  • Access-Control-Max-Age

The first, Access-Control-Allow-Origin, needs to be added to any request so the browser knows to use the OPTIONS verb for cross-site calls, which just error out otherwise with an XmlHttpReqeust Error 101 in JavaScript. The other three need to be added in response to the OPTIONS verb request. 

The Access-Control-Allow-Origin header can be added with one or more values. If you expect any client anywhere to call your services, like Google Maps does, you want to add * for the value, allowing all callers. Otherwise, the most secure way to use Access-Control-Allow-Origin is to set it for the specific domains you want to accept. Say your services are at https://service.yoursitehere.com, and your client site is http://www.yousitehere.com, you want to set the value to http://www.yoursitehere.com.

The code adds the Access-Control-Allow-Methods header to tell the browser which HTTP verbs your calls will accept as cross-domain.

By adding the Access-Control-Allow-Headers header, you control exactly which HTTP headers are allowed in the actual request which follows the preflight request.

The Access-Control-Max-Age header tells the browser how long to cache the preflight call result before doing it again during this browser session. I set this long to avoid multiple preflight requests.

In my case, I’m using configuration-less WCF services as described here. In my experience much of the pain with WCF in the past has been with the complex configurations, and this option circumvents that problem substantially.

The C# Code

So this is how it’s done programmatically, in Global.asax in my case:

 protected void Application_BeginRequest(object sender, EventArgs e)
 {
  HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin" , ”http://www.yoursitehere.com”);

 if (HttpContext.Current.Request.HttpMethod == "OPTIONS" )
 {
 //These headers are handling the "pre-flight" OPTIONS call sent by the browser
 HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods" , "GET, POST" );
 HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers" , "Content-Type, Accept" );
 
 
 HttpContext.Current.Response.AddHeader("Access-Control-Max-Age" "1728000" );
 HttpContext.Current.Response.End();
 }
 }
 

I put it Global.asax in order to avoid having each request handle multiple verbs and calling the handler in each service call. The downside to this is that any non-service request also gets the additional header and processing as well. For me, there are only service requests in this app so this is not a big deal.

The Client

I’ve regularly used jQuery to access services from client HTML code. It’s not complicated, the important parts are formatting the call to send JSON properly in response to our preflight request, which includes the dataType, contentType and data parameters to the $.ajax call.

 $.ajax({
 url: "https://www.yoursitehere.com/person/add",
 type: "POST" ,
 dataType: "json" ,
 contentType: "application/json" ,
 data: JSON.stringify(personObject),
 success: function (data) {
 //Process success here 
 },
 error: function (jqXHR, textStatus, errorThrown) {
 //process errors here 
 }
 });
 

Tools

If you are debugging REST services, you need some good tools. I have used Fiddler, the Chrome Developer Tools (the Network panel in particular) and the Chrome app Advanced REST Client Application successfully.

References

This post is the basis of the fix I’m describing

W3C description of how the HTTP process works

FireFox and CORS, also a lot of good info on HTTP headers

Hybrid QR Code/NFC Tag

My colleague Melissa Duckworth implemented a great idea: the hybrid QR Code/NFC tag. She superimposed a QR code on top of an NFC tag. Since NFC adoption is slow, you can still begin to take advantage of the rich possibilities of NFC while still supporting users with older devices.

hybridQRNFC

Posted on:
Posted in Mobile | Comments Off on Hybrid QR Code/NFC Tag