Click here to Skip to main content
15,887,821 members
Articles / Programming Languages / PHP

AJAX Cross-Origin HTTP request

Rate me:
Please Sign up or sign in to vote.
4.86/5 (5 votes)
24 Nov 2011CPOL3 min read 90.5K   15   1
Describes a standard way to implement Cross-Origin Ajax request other than JSONP or something else.

Background

Cross-Origin HTTP request (A.K.A. Cross-Domain AJAX request) is an issue that most web developers might encounter, according to Same-Origin-Policy, browsers restrict client JavaScript in a security sandbox, usually JS cannot directly communicate with a remote server from a different domain. In the past developers created many tricky ways to achieve Cross-Domain resource request, most commonly using ways are:

  1. Use Flash/Silverlight or server side as a "proxy" to communicate with remote.
  2. JSON With Padding (JSONP).
  3. Embeds remote server in an iframe and communicate through fragment or window.name, refer here.

And so on..

Those tricky ways have more or less some issues, for example JSONP might result in security hole if developers simply "eval" it, and #3 above, although it works, both domains should build strict contract between each other, it neither flexible nor elegant IMHO:)

W3C had introduced Cross-Origin Resource Sharing (CORS) as a standard solution to provide a safe, flexible and a recommended standard way to solve this issue. 

Mechanism

From a high level we can simply deem CORS is a contract between client AJAX call from domain A and a page hosted on domain B, a tipical Cross-Origin request/response would be:

DomainA AJAX request headers

Host DomainB.com
User-Agent Mozilla/5.0 (Windows NT 6.1; WOW64; rv:2.0) Gecko/20100101 Firefox/4.0
Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8,application/json
Accept-Language en-us;
Accept-Encoding gzip, deflate
Keep-Alive 115
Origin http://DomainA.com 

DomainB response headers

Cache-Control private /> Content-Type application/json; charset=utf-8
Access-Control-Allow-Origin DomainA.com
Content-Length 87
Proxy-Connection Keep-Alive
Connection Keep-Alive

The blue parts I marked above were the kernel facts, "Origin" request header "indicates where the cross-origin requestor preflight request originates from", the "Access-Control-Allow-Origin" response header indicates this page allows remote request from DomainA (if the value is * indicate allows remote requests from any domain).

As I mentioned above, W3 recommended browser to implement a "preflight request" before submitting the actually Cross-Origin HTTP request, in a nutshell it is an HTTP "OPTIONS" request:

OPTIONS DomainB.com/foo.aspx HTTP/1.1

If foo.aspx supports OPTIONS HTTP verb, it might return response like below:

HTTP/1.1 200 OK
Date: Wed, 01 Mar 2011 15:38:19 GMT
Access-Control-Allow-Origin: http://DomainB.com
Access-Control-Allow-Methods: POST, GET, OPTIONS, HEAD
Access-Control-Allow-Headers: X-Requested-With
Access-Control-Max-Age: 1728000
Connection: Keep-Alive
Content-Type: application/json

Only if the response contains "Access-Control-Allow-Origin" AND its value is "*" or contain the domain who submitted the CORS request, by satisfying this mandtory condition browser will submit the actual Cross-Domain request, and cache the result in "Preflight-Result-Cache".

Implementation

Let's take a look at server side code samples below (ASP.NET and PHP):

ASP.NET (C#)

C#
protected void Page_Load(object sender, EventArgs e)
{
    String data = String.Empty;
    String returnJSONStr = String.Empty;

    switch (Request.HttpMethod)
    {
        case "GET":
            data = Request.QueryString["Data"];
            returnJSONStr = "{\"Data\":\"Hi remote friend, you tried to passed me data: *" + data + "* through HTTP GET.\"}";
            break;
        case "POST":
            data = Request.Form["Data"];
            returnJSONStr = "{\"Data\":\"Hi remote friend, you tried to POST some mock data: *" + data + "* to me.\"}";
            break;
        case "OPTIONS":
            break;
        default:
            returnBadRequestResponse();
            break;
    }

    if (String.IsNullOrEmpty(data))
        returnBadRequestResponse();
    else
    {
        Response.AddHeader("Access-Control-Allow-Origin", "*");
        Response.ContentType = "application/json";
        Response.Write(returnJSONStr);
    }
}

private void returnBadRequestResponse()
{
    Response.StatusCode = 400;
    Response.ContentType = "application/json";
    Response.Write("{\"Error\":\"Bad HTTP request type!\"}");
}

PHP

PHP
if(isset($["Data"]))
{
    $method=$_SERVER['REQUEST_METHOD'];
    $data="";
    if($method=="POST")
    {
        $data=$_POST["Data"];

        $fakeData=new FakeData();
        $fakeData->Data="Hi remote friend, you tried to POST some mock data: *"+data+"* to me.";
        $fakeData->Time=new DateTime("now");
    }
    elseif($method=="GET")
    {
        $fakeData=new FakeData();
        $fakeData->Data="Hi remote friend, you tried to passed me data: *"+data+"* through HTTP GET.";
        $fakeData->Time=new DateTime("now");
    }
    else
    {
        RaiseError();
    }

    header('Content-type: application/json');
    $jsonStr= json_encode($fakeData);
    echo($jsonStr);
}
else
{
    RaiseError();
}

function RaiseError()
{
    http_send_status(405);
    header("Status: 405 Method Not Allowed");
}

/*Classes definition*/
class FakeData
{
    public $Data;
    public $Time;
}

Then client Ajax call code:

JavaScript
var cor = null; // cor stands for Cross-Origin request

if (window.XMLHttpRequest) {
    cor = new XMLHttpRequest();
}
//else if (window.XDomainRequest) {
    //cor = new XDomainRequest();
//}
else {
    alert("Your browser does not support Cross-Origin request!");
    return;
}

cor.onreadystatechange = function () {
    if (cor.readyState == 4) {
        document.getElementById('lbl').innerHTML = cor.responseText;
    }
};

var data = 'Some fake data';
if (method == 'POST') {
    cor.open('POST', 'http://WayneYe.com/Demo/CORSDemo/CORSDemoServer.aspx', true);
    cor.withCredential = "true";
    cor.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    cor.send('Data=' + data);
}
else if (method == 'GET') {
    cor.open('GET', 'http://WayneYe.com/Demo/CORSDemo/CORSDemoServer.aspx?Data=' + data, true);
    cor.withCredential = "true";
    cor.send(null);
}

The JS code works for all mainstream browsers (IE8+, FF 3.6+, Chrome 8+), I didn’t use XDomainObject which introduced in IE8 because XMLHttpRequest is already supported by IE8+, FF and Chrome, Safari, in additional XDR seems have a lot of restrictions (refer: http://blogs.msdn.com/b/ieinternals/archive/2010/05/13/xdomainrequest-restrictions-limitations-and-workarounds.aspx).

Conclusion

Cross-Origin Resource Sharing provides a safe, flexible and a standard way for web developers to achieve Cross-Origin communication, maybe it is time to forget those tricky or inelegant ways like JSONP, Flash/Silverlight/server bridge or even window.name and so on:)

References

This article was originally posted at http://wayneye.com/Blog/Ajax-Cross-Origin-HTTP-request

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) SAP Labs Shanghai
China China
Wayne is a software developer, Tech Lead and also a geek. He has more than 6 years' experience in Web development(server: ASP.NET (MVC), Web Service, IIS; Client: HTML/CSS/JavaScript/jQuery/AJAX), Windows development (Winform, Windows Service, WPF/Silverlight, Win32 API and WMI) and SQL Server. Deep understanding of GOF Design Patterns, S.O.L.i.D principle, MVC, MVVM, Domain Driven Design, SOA, REST and AOP.

Wayne's Geek Life http://WayneYe.com

Infinite passion on programming!

Comments and Discussions

 
GeneralMy vote of 5 Pin
237411-May-11 12:41
237411-May-11 12:41 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.