You are here

How to send cross site request in Ajax?

Blog Terms: 

Cross site or not?

If you are Ajax programmer, and you want get some data from other site(with different domain), you will get such similar exception message in browser:
Error: uncaught exception: Permission denied to call method XMLHttpRequest.open

I am developing the map client for SUAS MapServer, which are using Ajax to get and parse GetCapabilities request XML from Web Map Server. My purpose is not limited to SUAS MapServer but all WMSs that conform to WMS 1.1.1 standard, that why I must face this terrible problem: send cross site request with Ajax. There are many programmers have discussed about this topic, and some of them have developed special API to solve that problem. Now I will write some ideas from them and mine, some problems I have met during the development.

1, what means cross domain(site)?

Firstly, there are 2 ways to open one Html page with Ajax code inside. One is directly open this html file in your computer, the URL in your browser will be like this, I suppose that the html page is in C:/index.html

  1. IE: C:\index.html
  2. Firefox: file:///C:/index.html
  3. Opera: file://localhost/C:/index.html
  4. Safari: file:///C:/index.html

The second is opening this page with its right domain, supposed that the page is in http://www.yourdomain.com/index.html, the url will be same in every browsers.

If you want to get the data from one online page, for example, www.google.com, www.google.com will recognized as other different domain with your www.yourdomain.com domain. But if you open the page in local computer, domain "http://localhost/... "and "http://127.0.0.1/..." (supposed that you have build your localhost with Apache or IIS or others) will not be seen as other different domain, this is same as you open this page with url prefix like http://localhost/index.html or http://127.0.0.1/index.html.

  1. ------------------------------------------------------http://localhost-----http://anyothers.com-
  2. IE: C:\index.html---------------------------------same domain-------other domain
  3. Firefox: file:///C:/index.html--------------------same domain-------other domain
  4. Opera: file://localhost/C:/index.html---------same domain-------other domain
  5. Safari: file:///C:/index.html---------------------same domain-------other domain
  6. -----------------------------------------------------------------------------------------------------------
  7. http://localhost/index.html---------------------same domain-------other domain
  1. -------------------------------------------------------http://mydomain.com-----http://www.anyothers.com-
  2. http://www.mydomain.com(in any browsers)---------other domain-----other domain


Notice:
www.yourdomain.com and yourdomain.com will be recognized as two different domains by the browsers.

2, How to send cross site request

You had better already have basic Ajax programming experience, because I can not waste time to write some detail information about what is Ajax and how to send one asynchronous request, there are tons of tons such articles on the internet. If you have done some thing with Ajax, you will understand this code even you have not used Prototype, one excellent light Javascript Lib. I can not paste all of my code here, it is too much. For getting more detail information, please download SUAS MapServer and find suasclient in Plugin folder.


var proxyURL = "proxy/proxy.php?url=";

function sendGetcapacities(selectedServerUrl){
var pars = 'VERSION=1.1.1&SERVICE=WMS&REQUEST='+reqGetcapabilities;
var url = selectedServerUrl+pars;

enablePrivilege();
url = getCrosssiteUrl(url);

//send ajax http request
var myAjax = new Ajax.Request(
url,
{
method: 'get',
encoding: 'UTF-8',
parameters: pars,
asynchronous:true,
onLoading: showLoading(), //show loading status
onComplete: processSendGetcapacities
}
);
}
//get the response data from server
function processSendGetcapacities(originalRequest){
hideLoading();// hide loading status
xmldoc = originalRequest.responseXML;
xmltext = originalRequest.responseText;
alert("Getcapacities XML DOM: "+xmldoc);
alert("Getcapacities XML TEXT: "+xmltext);
}

/**
* if is for mozilla/ firefox, when open local file in brower,should enablePrivilege
* example: file:///C:/index.html
*/
function enablePrivilege(){
//if the browser is Mozilla of firefox,
if (getClientType() == typeFF) {
//if open local file in browser
if(window.location.href.substring(0,5)=='file:') {
if (typeof netscape != 'undefined' && typeof netscape.security !='undefined') {
try {
netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
//netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
}catch (e) {
showErrorMessage("Error in enablePrivilege: " + e.message);
}
}
}
}
}

/**
* @params: url will be sent
* compare the send url with browser url, if is not local file such as "file:///C:/index.html",
* but the real domain url, if the url is not in the same domain, call the proxy php
*/
function getCrosssiteUrl(url){
var newurl;
var browserurl = window.location.href;
try{
//if is local file
if(browserurl.substring(0,5)=='file:') {
newurl = url;
return newurl;
}
//if is real domain
else if(browserurl.substring(0,5)=='http:'){
//(http://)www.easywms.com/suasclient
var urlafter = browserurl.substring(7,browserurl.length);
var urlafter_ = url.substring(7,browserurl.length);
//if url is :(http://)localhost or (http://)127.0.0.1
if(urlafter_.indexOf('localhost')==0 || urlafter_.indexOf('127.0.0.1')==0){
newurl = url;
return newurl;
}//if not localhost domain
else{
//www.easywms.com
var urldomain = urlafter.substring(0,urlafter.indexOf('/'));
var urldomain_ = urlafter_.substring(0,urlafter_.indexOf('/'));
// not in the same domain, use proxy
if(urldomain!=urldomain_){
debug("Ready to send cross domain request.");
///suasclient/proxy/proxy.php?url=url
newurl = browserurl.substring(0,browserurl.lastIndexOf('/')+1)+ proxyURL+url;
return newurl;
}else{
newurl = url;
return newurl;
}
}
}
else{
newurl = url;
return newurl;
}
}catch(e){
showErrorMessage("Error in getCrosssiteUrl: " + e.message);
return url;
}
return newurl;
}

This is the workflow:

1, if open html file in local computer, call enablePrivilege for Mozilla or Firefox

2, check the url in your browser and the url you want to send request(getCrosssiteUrl):
a, if open file in local computer, reserve the url
b, if it is same domain, if they belong to same domain, reserve the original url;
c, if it is other domain, call proxy.php in the same domain (one proxy php script that can get all the content of one url ). You can write you own proxy script in any language, ASP, JSP,Java, the most important thing is put this script in the same domain with your page where you want to send Ajax request.

3, After sending request, call processSendGetcapacities() to get the DOM object(xmldoc) or Text Content(xmltext).

3. Testing with browsers.

Now I must test the script, the most important VIPs are these 2: xmldoc and xmltext, xmldoc is dom object, with which you can parse the xml structure and content, xmltext is the whole content of response document in text format.

3.1 Test with file opened in local computer

Notice:
1, http://localhost equals to http://127.0.0.1
2, xmldoc full means whole xml dom object can be acquired, xmldoc null means the xml dom object is null. (xmltext is same). If xmldoc is null, when you use xmldoc.getElementsByTagName('nodename'), Js will throw "object undefined" exception.

  1. ------------------------------------------------------http://localhost----------------http://anyothers.com-
  2. IE: C:\index.html---------------------------------xmldoc full, xmltext full-------xmldoc null, xmltext full
  3. Firefox: file:///C:/index.html--------------------xmldoc null, xmltext full-------xmldoc null, xmltext full
  4. Opera: file://localhost/C:/index.html---------xmldoc null, xmltext full-------xmldoc null, xmltext full
  5. Safari: file:///C:/index.html---------------------xmldoc full, xmltext full-------xmldoc null, xmltext full

3.2 Test with file in domain

The result is as expected.

  1. ---------------------------------------------http://www.mysite.com------http://anyothers.com(using Proxy)
  2. IE: http://www.mysite.com-----------xmldoc full, xmltext full-------xmldoc full, xmltext full
  3. Firefox: http://www.mysite.com-----xmldoc full, xmltext full-------xmldoc full, xmltext full
  4. Opera: http://www.mysite.com------xmldoc full, xmltext full-------xmldoc full, xmltext full
  5. Safari: http://www.mysite.com-------xmldoc full, xmltext full-------xmldoc full, xmltext full

4, How to send crosssite request when open file in local computer?

As I have tested, I am a little disappoint with the result in 3.1, sending request when open file in local computer, all the browsers can get the right text content of the response data, but with XMLHttpRequest.responseXML, it works only with localhost with IE or Safari. Now, we meet one awful issue, we must ask the user to build one local host server, or he can only visit your site to use the client; if he does not have the host server, he can not use the client as he will.

I have two ideas to solve this problem,

4.1 write your own XML parser in Javascript

Because all browsers can get the responseText without problem, if the xml document is not complex, you can write one one simple parser in javascript with regular expression or String prototype. But I will forget it, because the GetCapabilities XML document from Web Map Server too complex to parse it with pure javascript.

http://xmljs.sourceforge.net/   could be one good choice:
XML for <SCRIPT> is a powerful, standards-compliant JavaScript XML parser that
is designed to help web application designers implement
cross platform applications that take advantage of client-side manipulation of XML data. You can use http_request.responseText to rebuild the xml document.

4.2 Use Json to replace the responseXML

Remeber, We got responseText everytime in any browsers and whatever you open the file in local computer or in one domain.
The following Javascript code shows how the client can use an XMLHttpRequest to request an object in JSON format from the server.

var the_object;

//only for demo here, only for FF or opera and others

var http_request = new XMLHttpRequest();

http_request.open( "GET", url, true );

http_request.onreadystatechange = function () {

if ( http_request.readyState == 4 ) {

if ( http_request.status == 200 ) {

the_object =
eval( "(" + http_request.responseText + ")" );

} else {

alert( "There was a problem with the URL." );

}

http_request = null;

}

};

An easy way for a JavaScript program to parse JSON-formatted data is to use the built-in JavaScript eval()cross-site request forgery attacks (CSRF or XSRF). function, and naive deployments of JSON are subject to

I think it is much more easier to parse one XML document by the server side with PHP,ASP, Java than the client site, do you agree with it? I can develop one server script, which can parse the XML document from one URL you want to send request, and return back the Jason object. And our client will be very happy to parse this Json object.

Or using Apache mod_rewrite, I have not tested it.

5, other cross site Ajax Libs

1, A cross site ajax plugin for Prototype
http://www.mellowmorning.com/2007/10/25/introducing-a-cross-site-ajax-pl...
As users reported, it does not compatible for IE6

2, Modello.ajax
http://modello.sourceforge.net
http://www.ajaxwing.com/index.php?id=4
Use Proxy php script to realize cross site fucntion

3, cows ajax (Changeable Origin Web Services), funny name Tongue out
http://cows-ajax.sourceforge.net/index.php
COWS Ajax makes this cross-site, asynchronous, tri-directional
communication (the user, your site, the application host) a no-brainer. It use special method to cross site without proxy, I will test it.

6, Proxy script in any language.

How to write one proxy script? I will give you some simple example here, some of them come from other programmers, thx!!

6.1 PHP

<?
//must set the contenttype of response as text/xml
header("Content-type: text/xml;charset=UTF-8;");
header("Cache-Control: no-cache, must-revalidate");

echo file_get_contents("http://www.yahoo.com");
?>

6.2 ASP

<%
Server.ScriptTimeout=9999999;
function send_request(url) {
var codedtext;
http_request = Server.CreateObject("Microsoft.XMLHTTP");
http_request.Open("GET",url,false);
http_request.Send(null);
if (http_request.ReadyState == 4){
var charresult = http_request.ResponseText.match(/CharSet=(\S+)\">/i);
if (charresult != null){
var Cset = charresult[1];
}else{Cset = "utf-8"}

codedtext = bytesToBSTR(http_request.Responsebody,Cset);
}else{
codedtext = "Error";
}

}

function bytesToBSTR(body,Cset){
var objstream;
objstream = Server.CreateObject("Adodb.Stream");
objstream.Type = 1;
objstream.Mode = 3;
objstream.Open();
objstream.Write(body);
objstream.Position = 0;
objstream.Type = 2;
objstream.Charset = Cset;
bytesToBSTR = objstream.Readtext;
objstream.Close;
return(bytesToBSTR);
}

%>
<% Response.Write(send_request("http://www.yoururl.net")) %>

OR:

XmlDataDocument myXml = new XmlDataDocument();
myXml.LoadXml("<Responses><img>fffff</img></Responses>");
Response.ContentType = "text/xml";
Response.Write("<?xml version='1.0' encoding='utf-8'?>");
Response.Write(myXml.DocumentElement.OuterXml);
Response.End();
return(codedtext);

6.3 Java Servlet
http://yousite.com/servlet/xxx/ProxyServletUtil?url=yoururl

package action;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ProxyServletUtil extends HttpServlet {
/**
*
*/

private static final long serialVersionUID = 1L;

private int READ_BUFFER_SIZE = 1024;

public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String urlString = request.getParameter("url");
writeResponse(response, urlString);
}

private void writeResponse(HttpServletResponse response, String urlString) throws ServletException{
try {
URL url = new URL(urlString);
URLConnection urlConnection = url.openConnection();
response.setContentType(urlConnection.getContentType());
InputStream ins = urlConnection.getInputStream();
OutputStream outs = response.getOutputStream();
byte[] buffer = new byte[READ_BUFFER_SIZE];
int bytesRead = 0;
while ((bytesRead = ins.read(buffer, 0, READ_BUFFER_SIZE)) != -1) {
outs.write(buffer, 0, bytesRead);
}
System.out.println(outs);
outs.flush();
outs.close();
ins.close();
} catch (Exception e) {
try {
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
} catch (IOException ioe) {
throw new ServletException(ioe);
}
}
}
}

Ref:
http://www.webdeveloper.com/forum/showthread.php?threadid=90280
http://en.wikipedia.org/wiki/JSON
http://www.zachleat.com/web/2007/08/30/cross-domain-xhr-with-firefox/
http://www.mozilla.org/projects/security/components/signed-scripts.html
http://www.dotnetsky.net/netsave/ShowTopic-40446.html

Comments

wow... it seems to be a little complex U_x but ill try to get it =)

Thanks for the post... Really needed it badly.

Thank you very much for your article!

A question: could you please make the project available for download here, with the article?

It would be very much appreciated, and, I'm sure, not only by me!

Thanks again!

eric

i m creating a bot of a site .but issue is Site block my IP address and don't open in my computer.
please suggest me how can i handle the site with different free proxy ?

I got a nice link -

http://tek-insight.blogspot.com/2010/05/cross-domain-ajax-request-proxy-json.html