Archive for the ‘AJAX’ Tag

REST, WEB API And CORS   2 comments

Introduction

Cross Domain AJAX calls (CORS) on desktop browsers require special processing on both the server side and in the way we call AJAX from within the browser. A general overview of CORS can be found hereASP.Net WEB API allows a couple of fairly straight forward ways to implement REST HTTP endpoints with CORS support.  Using the current release build of WEB API we can code our CORS handlers directly or if you want to use the nightly builds of the WEB API you can use an attribute approach.  This post will concentrate on how to write CORS handlers directly since this is the approach I have this in test right now and this approach allows you more flexibility in implementation and improved debugging options.  I will be concentrating on implementation details and assume you have read the background blogs listed above before we start.  I will also be looking at the browser side implementation of the CORS call and some issues with IE browsers (IE 9 in particular).  We are testing with Windows Server 2012 and are using Firefox, Chrome and IE as our test browsers.

Voice from the future: Brock Allen’s great work on CORS, CORS based CORS Attribute support has now been incorporated into Web API 2.  See here and here for details.

So What’s the Problem.

The W3C defines special procedures required if a browser is going to make an AJAX call to a server which is not in the domain of the page which served the page which is making the call (hence Cross Domain).  To enable CORS the server must implement CORS and the browser must make the AJAX call following some conventions.  In the WEB API framework CORS can be implemented on the method or site level.  We will focus on site level CORS in this post.  The WEB API pipeline allows us to hook in message handlers at several places.  The canonical CORS handler, given by the links listed above looks like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.Tracing;

public class CorsHandler : DelegatingHandler
{
const string AccessControlRequestMethod = “Access-Control-Request-Method”;
const string AccessControlRequestHeaders = “Access-Control-Request-Headers”;
const string AccessControlAllowOrigin = “Access-Control-Allow-Origin”;
const string AccessControlAllowMethods = “Access-Control-Allow-Methods”;
const string AccessControlAllowHeaders = “Access-Control-Allow-Headers”;

       protected override Task<HttpResponseMessage> SendAsync( HttpRequestMessage request, CancellationToken cancellationToken )
{
const string Origin = “Origin”;
bool isCorsRequest = request.Headers.Contains( Origin );
bool isPreflightRequest = request.Method == HttpMethod.Options;
if ( isCorsRequest )
{
//HTTP CORS OPTIONS
if ( isPreflightRequest )
{
HttpResponseMessage response = new HttpResponseMessage( HttpStatusCode.OK );
response.Headers.Add( AccessControlAllowOrigin, request.Headers.GetValues( Origin ).First( ) );
                   string accessControlRequestMethod = request.Headers.GetValues( AccessControlRequestMethod ).FirstOrDefault( );
if ( accessControlRequestMethod != null )
{
response.Headers.Add( AccessControlAllowMethods, accessControlRequestMethod );
}

                   string requestedHeaders = string.Join( “, “, request.Headers.GetValues( AccessControlRequestHeaders ) );
if ( !string.IsNullOrEmpty( requestedHeaders ) )
{
response.Headers.Add( AccessControlAllowHeaders, requestedHeaders+”, AICJWT” );
}

                   TaskCompletionSource<HttpResponseMessage> tcs = new TaskCompletionSource<HttpResponseMessage>( );
tcs.SetResult( response );
return tcs.Task;
}
else
{
//HTTP CORS GET
return base.SendAsync( request, cancellationToken ).ContinueWith<HttpResponseMessage>( t =>
{
HttpResponseMessage resp = t.Result;
resp.Headers.Add( AccessControlAllowOrigin, request.Headers.GetValues( Origin ).First( ) );
return resp;
} );
}
}
else
{
//NOT A CORS CALL
return base.SendAsync( request, cancellationToken );
}
}
}

Lets break this down from the simplest part first.  We create a class derived from DelegatingHandler (since we are implementing at the site level).  We hook this handler into the system within the framework generated class WebApiConfig as

public static class WebApiConfig
{
public static void Register( HttpConfiguration config )
{
//your route code here

           );
config.MessageHandlers.Add( new WebAPI.Handler.CorsHandler( ) );

//other handlers are included here.
}
}

If you have other classes based on DelegatingHandler the order in which they are added in WebApiConfig matters.

In the simplest case where we are not making a CORS call we can simply return the handler without action as:

return base.SendAsync( request, cancellationToken );

When the CORS call is made by the browser the caller should include the standard HTTP header: Origin with a value of the calling pages domain.  The canonical code assumes this and uses the presence of this header to detect a CORS call. Hence the code:

const string Origin = “Origin”;
bool isCorsRequest = request.Headers.Contains( Origin );

If the CORS call is not an OPTIONS call (which the canonical code call preFlight) we see the code:

return base.SendAsync( request, cancellationToken ).ContinueWith<HttpResponseMessage>( t =>
{
HttpResponseMessage resp = t.Result;
resp.Headers.Add( AccessControlAllowOrigin, request.Headers.GetValues( Origin ).First( ) );
return resp;
} );

Here the code returns a required header for the Browser: Access-Control-Allow-Origin with the value taken from the Origin Header of the caller.

We could, if we choice to, have set the value to  the wild card value ( * ) but this openness may make your system administrator nervous.  Notice here that nothing in the W3C specification restricts what other Headers the sender can include in the CORS call.  However certain browsers (IE) and certain Javascript packages (jQuery) restrict the call to standard HTTP request Headers.  In our implementation this gave us some problems but more on this later. The browser code (User-Agent), not the user code, will refuse to accept the return if the Origin Header is missing or does not contain either the wild card or the calling page’s domain in the value for the Origin header.

So What is the Rest of the Handler Code Doing?

Following this document from Mozilla.org, the browser making the call may make an optional CORS OPTIONS call (see here for HTTP verbs if this one is new to you).  This preflight call (as the canonical code names it) has asks the server for details about what may be in the CORS request when it is actually call.  Following the Mozilla explanation here is what needs to happen:

    • 1a. The User-Agent, rather than doing the call directly, asks the server, the API, the permission to do the request. It does so with the following headers:
      • Access-Control-Request-Headers, contains the headers the User-Agent want to access.
      • Access-Control-Request-Method contains the method the User-Agent want to access.
    • 1b. The API answers what is authorized:
      • Access-Control-Allow-Origin the origin that’s accepted. Can be * or the domain name.
      • Access-Control-Allow-Methods a list of allowed methods. This can be cached. Note than the request asks permission for one method and the
        server should return a list of accepted methods.
      • Access-Allow-Headers a list of allowed headers, for all of the methods, since this can be cached as well.

In the canonical code given above here is what happens in the CORS OPTIONS call:

//( 0 )create a response object

HttpResponseMessage response = new HttpResponseMessage( HttpStatusCode.OK );
//( 1 ) build the value string for the Access-Control-Allow-Origin header from the ORIGIN header value of the request

response.Headers.Add( AccessControlAllowOrigin, request.Headers.GetValues( Origin ).First( ) );

//( 3 )build the value string for the Access-Control-Request-Headers from the values in the request
string accessControlRequestMethod = request.Headers.GetValues( AccessControlRequestMethod ).FirstOrDefault( );
if ( accessControlRequestMethod != null )
{
response.Headers.Add( AccessControlAllowMethods, accessControlRequestMethod );
}

//( 4 ) build the value string for the Access-Control-Allow-Headers header from the ORIGIN headers value of the request

string requestedHeaders = string.Join( “, “, request.Headers.GetValues( AccessControlRequestHeaders ) );
if ( !string.IsNullOrEmpty( requestedHeaders ) )
{
response.Headers.Add( AccessControlAllowHeaders, requestedHeaders);
}

//( 5 ) interrupt the pipeline and return the response object to the caller.

TaskCompletionSource<HttpResponseMessage> tcs = new TaskCompletionSource<HttpResponseMessage>( );
tcs.SetResult( response );
return tcs.Task;

Please note that we can put whatever we need into the Header values.  For example if we wanted to limit CORS calls to GET request only we could replace ( 3) with the simple:

response.Headers.Add( AccessControlAllowMethods, “GET” );

To allow a specific domain only to make the CORS call we could replace ( 1 ) with:

response.Headers.Add( AccessControlAllowOrigin, “www.special.com” );

In our case we wanted to allow a specific non-standard Header into the CORS request.  We called this Header AICJWT. So we expanded the key line in ( 4 ) to be:

response.Headers.Add( AccessControlAllowHeaders, requestedHeaders+”, AICJWT” );

The reason we added it explicitly here is due to problems in both JQuery and in IE.  Please note again that the CORS OPTIONS call is optional.  At this point in our development we were using the awesome async Framework 4.5  object: System.Net.Http.HttpClient.  This is a get object and very useful during development BUT there is no User-Agent (browser side code) involved.

The Trouble Begins: Browser Side Code

All seemed swell, till the JavaScript coders tried to call into the system.  JQuery forces a CORS OPTIONS call when it detects a Cross Domain AJAX call.  For reasons which remain unclear JQUERY does not include custom headers in the OPTIONS request.  Some people think this is in the W3C spec for CORS but I don’t see it there, do you?  Some folks out there indicate that the user-agent is forcing the OPTIONS request but this is not true.  If we use a direct AJAX call, not using JQUERY we can make our own CORS OPTIONS request or skip the OPTIONS call completely.  Here is the code to make the call using JavaScript in IE:

function callHttpReq() {
var invocation = new XMLHttpRequest();
var url = ‘
http://whereever/api/Concert?Year=1980′;
    var body = ”;

var token = “myspecialstuff”;
function callOtherDomain(){
if(invocation)
{
invocation.open(‘GET’, url, true);
invocation.setRequestHeader(‘AICJWT’, token);
invocation.setRequestHeader(‘Accept’, ‘application/json’);
invocation.onreadystatechange = httpHandler;
invocation.send(body);
}
}
callOtherDomain();

    function httpHandler() {
if(invocation.readyState == 4) {
var jsonObj = $.parseJSON(invocation.responseText);
if(jsonObj.length > 0) {
htmlStr = “<ul>”;
$.each(jsonObj, function(i,row) {
htmlStr += “<li>” + row.Date + ‘—-‘ + ” ” + row.Venue +”</li>”;
});
htmlStr += “</ul>”;
$(“#responeBody”).append(htmlStr);
}
}
}

}

Note we are skipping JQUERY because we require a custom header in our use case.  This step is not necessary if you are NOT using custom headers in the CORS call.  Note also that if you are not using JQUERY you need to use different AJAX object other than IE’s XMLHttpRequest.  If you can use JQUERY there is a masive amount of documentation about how to make AJAX calls and JQUERY will handle CORS and differences between the IE and other AJAX objects automatically.

IE Blues

OK all is good but when we test with IE 8 or 9 we get back the data from the CORS get BUT the user also gets the dialog box:

image

Microsoft tells us the USER can suppress this IN IE8 and IE9 by following this procedure:

You can check your Security Zone level as well as change this setting by going to Tools, Internet Options, and click Security tab. Your Security Zone level will be listed on this screen, by default this is set to Medium-high. To change the setting for your message click Custom Level , locate the Access data sources across domains under the Miscellaneous section and change the setting from Prompt to a desired setting.

image

We do not have this problem in Chrome or Firefox. Live Free or Die.

One Last Server Side Issue

During our testing, using Windows Server 2012 we ran into one additional problem.  Our CORS OPTIONS calls were not getting to our site but where being intercepted by the HTTP Module prior to the site Delegate Handler.  Without getting into it too deeply we needed to modify the web.config for our CORS site and disable WebDAV (don’t ask) and allow OPTIONS for the ExtensionlessUrlHandler.  See here for details.  As far as we know this is a pure Windows 2012 server issue.

JQuery 2009 & AJAX Experience 2009   Leave a comment


John Resig
Photo By dlr2008 On FlickR.com

JQuery Conference 2009

This conference took place at the Microsoft Research Center in Cambridge Massachusetts on September 12 & 13, 2009. This was a small but intense gathering of JQuery and JQuery UI core developers and assorted developers working on extensions to JQuery (aka plug-ins). There were about 300 participants most of whom were intensely involved with JQuery. Your humble scribe was one of the least informed participants. JQuery has never looked stronger, the development team is cohesive and motivated, and community support is extensive. John Resig’s numbers indicate that something in excess of 35% of all public sites running JavaScript are using JQuery. The library wars are over. Let the framework wars begin. The conference agenda and speakers list and slides can be found by starting: here.

JQuery core is stable at release 1.3.2 but 1.3.3 is coming “real soon now.” The 1.3 versions have focused on optimization, simplification and (proactive) HTML5 support.John’s talk about recent changes to JQuery Internals can be found here. Next year will see moving JQuery core and open source licenses will be transferred to Software Freedom Law Center. Projects for the next year (think version 1.4) include move JQuery\UI infrastructure into JQuery proper. Significant work has been done within JQuery core to streamline and simplify plug-in development via the Widget Factory ($.widget(…)) (thanks to Scott González for this excellent presentation). For the hard core, Paul Irish gave an excellent presentation on JQuery Anti-Patterns. This was bookended by Yehuda Katz’s excellent Best Practices presentation. Aaron Quint was among the Rubists who are advancing the state of JQuery Art. His Sammy.js project attempts to use JQuery to create a browser side MVC/REST framework. John Nunemaker is also working in this basic area and his presentation can be found here.


The Jupiter Room


Photo By dlr2008 On FlickR.com

The walk away component demonstrated at the conference was the work done by Filament Group employees, Todd Parker and Scott Jehl who developed and maintain the new ThemeRoller CSS generator for use with JQuery/UI components. Outstanding Work!

This year’s conference was sold out at 300 participants and was a mind blowing experience. Two days of the sort of “deep dive” Microsoft presenters can only dream of. All this, plus great food and a T-shirt, for one hundred bucks American. We won’t see this sort of thing until the next big thing comes along. Look for the following event during 2010: one virtual (online) developer conference (similar to JQuery 2009 but without the food) and three ‘bigger’ user conferences (London, Los Angles and Boston). Splendid!

The AJAX Experience 2009

This conference took place at the Boston Airport Hilton on September 14 – 16, 2009. What an ugly hotel. Isolated, bad restaurant, overpriced breakfast, cold design, the hotel truly sucks. The conference itself was much better. If at JQuery 2009 we saw significant evidence of what the web will be in the next two years, The AJAX Experience showed us some of what will not happen:

  • The HTML5 specification will be released too late to mater,
  • ES5 will not change the world, or JavaScript.
  • Browser vendors will implement HTML5 using different API’s and approaches,
  • Conflicts between Security and JavaScript will NOT be resolved anytime soon,
  • JSON is not going away but XML ,
  • JavaScript is not going to be changed in any fundamental way,
  • Page Refreshes are not.

The AJAX Experience is a developer driven conference and uniquely includes presenters from both the standards community (W3C, ES5) and major players (Google, Facebook, Yahoo and Microsoft).


Douglas Crockford


Photo By dlr2008 On FlickR.com

The conference is well worth attending to take the pulse of the AJAX/REST world from a developer perspective. It is a conference with does not have an edge to it.  To be fair, this is a conference with a large number of big faces present and the structure of the conference is oriented towards making these faces easily available for one on one’s.  And for that we thank the folks at AJAX Experience.

On the plus side AJAX and REST continue to hold an enduring fascination for web developers looking for the edge. One of the best attended sessions was Facebook’s overview of how AJAX, careful architectural design, client side caching and REST principles, can be brought together into a workable whole. The presentation slides can be found here. This is an important presentation and should be viewed by anyone who want to ‘go all the way’ with AJAX/REST. If nothing else this presentation shows how little native support there is in the current set of browsers for this effort and how clumsy the basic design of JavaScript is when this advanced work is attempted. Please understand dear reader that the best AJAX/REST web site would have only ONE page load all other data exchange and UI work is done on this canvas. The Facebook folks found that after years of effort they still force unnecessary page loads as the only way to clean up events, timers, namespace debris and memory leaks.

Security

For me the most interesting, and the most depressing presentation was the much anticipated panel discussion: Secure Mashups: Getting to Safe Web Plug-ins

The panelists where Marcel Laverdet (Facebook – FBJS), Mike Samuel (Google – Caja), Scott Issacs (Microsoft – The Open Source Web Sandbox) and Douglas Crockford (adSafe). And the discussion was spirited. The goal here is to develop a method by which a web site (think Facebook for example) can accept browser widgets (think ads or user gadgets) from multiple sources and assure “secure cooperation” between all widgets and with the vendor’s (the page owner) HTML and JavaScript code. Although there are nuances between the different approaches and differences in scope, each of these attempts to mash-up ‘security’ follow the same pattern:

  • Clean incoming HTML for “evil parts”
  • Clean incoming JavaScript for “evil parts”
  • Encapsulate the remaining JavaScript code in wrapper functions which ultimately call methods in:
  • The Vendor’s Security Library

The Security Library purpose is to prevent, at run time, manipulation of HTML belonging to other parts of the system and preventing JavaScript exploits. Each solution provides its own definition of what the “evil parts” are, what constitutes ‘secure’ behavior and what are the limits of the security library. None of the solutions currently support JavaScript libraries. Although, Google and Microsoft made noises like they will attempt to bring third party libraries (read JQuery here) into the tent. There was a lot of discussion around this point. My notebook shows that John Resig’s name was mentioned by panelists eight times during this discussion. The overall goals of the projected solutions vary from forcing no code changes (Web Sandbox) to forcing complete re-writes (adSafe is alone is requiring safe widgets to being written exclusively using ONLY the adSafe library). All four projects are in early beta.
Significantly, there were no presentations which addressed Secure Browser OS projects like Microsoft Gazelle or Google’s Chrome OS.

PS: On the UI side of the house Bill Scott gave a delightful presentation on UI methods. For UI designers his presentations (and books) are not be to be missed.

JQuery And AJAX Experience 2009 – The Movie:



%d bloggers like this: