Making Cross-Domain Ajax Requests for a Data Service

April 20, 2009

Making Cross-Domain Ajax Requests for a Data Service


One problem that exists when Making Cross-Domain Ajax Requests for a Data Service
using ADO.NET data services is
cross-domain requests. For security
reasons, XMLHTTP requests don’t
allow cross-domain HTTP requests.
So what can we do if we want to
make an Ajax call for a data service
that isn’t located in our domain? this post will try to give a solution to this
problem.


Making a Cross-Domain Ajax Request for a Data Service


As stated early in the post’s start, for security reasons, XMLHTTP
requests don’t allow cross-domain HTTP requests. But sometimes we do
want to consume data services that aren’t located on our domain through
Ajax. The solution to making requests to a service outside our domain 
is to implement a cross-domain proxy service. It means to create
a private service in your domain that will act as an intermediary between
your application and the data service. Your application will call the private
service and it will make the HTTP request to the data service.


How to Implement the Solution?


We can use WCF to create an Ajax-enabled endpoint using WebHttpBinding
and JSON serialization. One of the cons for such an implementation is
that we are using a traditional SOAP-based WCF service and not the fully
functionality of the data service.


Example


The following example will help you to understand how to implement the
solution proposed early. You can download the example code from
here.

Building the Data Service
We are going to use the same database that I used in my previous posts
in the data services series. The data service is a school service based upon
the following EDM mapping of entity framework:
School Entity Designer Diagram
The code for the data service is very simple and is only for the demonstration
of the post solution:



namespace SchoolDataService
{
    public class SchoolDataService : DataService<SchoolEntities>
    {
        // This method is called only once to initialize service-wide policies.
        public static void InitializeService(IDataServiceConfiguration config)
        {
            config.SetEntitySetAccessRule(“*”, EntitySetRights.All);
            config.SetServiceOperationAccessRule(“*”, ServiceOperationRights.All);
        }
    }
}

Building the Cross-Domain Proxy Service
In order to build the proxy service we need to build service
contracts and data contracts for every operation and entities
that we want to expose from the consumed data service. In the
following example I’m going to implement the GetCourseByID
operation through the proxy service. The following code demonstrate
how to do so:



namespace ConsumerApplication
{
    [DataContract]
    public class CourseEntity
    {
        [DataMember]
        public int CourseID { get; set; }
 
        [DataMember]
        public string Title { get; set; }
 
        [DataMember]
        public string Days { get; set; }
 
        [DataMember]
        public DateTime Time { get; set; }
 
        [DataMember]
        public string Location { get; set; }
 
        [DataMember]
        public int Credits { get; set; }
    }
 
    [ServiceContract(Namespace = “mysite”)]
    [AspNetCompatibilityRequirements(
        RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class ProxyService
    {
        #region Members
 
        private SchoolEntities _proxy = new SchoolEntities(
             new Uri(“http://localhost:2811/SchoolDataService.svc”));
 
        #endregion
 
        #region Service Methods
 
        [OperationContract]
        [WebGet(ResponseFormat = WebMessageFormat.Json)]
        public CourseEntity GetCourseByID(int courseID)
        {
            var returningCourse = (from course in _proxy.Course
                                   where course.CourseID == courseID
                                   select course).ToList().First();
            return new CourseEntity
            {
                CourseID = returningCourse.CourseID,
                Credits = returningCourse.Credits,
                Days = returningCourse.Days,
                Location = returningCourse.Location,
                Time = returningCourse.Time,
                Title = returningCourse.Title
            };
        }
 
        #endregion
    }
}

Pay attention that in order to consume the service from client
side you need to configure the host to use the
WebScriptServiceHostFactory. To do so go to the service markup and
write the following code:



<%@ ServiceHost Language=“C#” Debug=“true” 
Service=“ConsumerApplication.ProxyService” CodeBehind=“ProxyService.svc.cs” 
Factory=“System.ServiceModel.Activation.WebScriptServiceHostFactory” %>

Building the Consuming Web Form
The following code demonstrate the consuming page of the
proxy service:



<%@ Page Language=”C#” AutoEventWireup=”true” CodeBehind=”Default.aspx.cs” Inherits=”ConsumerApplication._Default” %>
 
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
 
<html xmlns=”http://www.w3.org/1999/xhtml” >
<head runat=”server”>
    <title></title>   
</head>
<body>
    <form id=”form1″ runat=”server”>
    <asp:ScriptManager ID=”ScriptManager1″ runat=”server”>
        <Services>
            <asp:ServiceReference Path=”~/ProxyService.svc” />
        </Services>
    </asp:ScriptManager>
    <div id=”content”>        
    </div>
    <script type=”text/javascript”>
   1:  
   2:         function pageLoad() {
   3:             var proxy = new mysite.ProxyService();
   4:             proxy.GetCourseByID(1045, OnSuccess, onerror)
   5:         }
   6:  
   7:         function OnSuccess(result) {
   8:             var div = $get(“content”);             
   9:             div.innerText = result.Title;
  10:         }
  11:  
  12:         function OnError(result) {
  13:             // do something about the error
  14:         }
  15:  
  16:         pageLoad();
  17:     
</script>
    </form>
</body>
</html>

Summary


Lets sum up, in today’s post I gave a solution for making cross-domain
Ajax
requests to a data service. The solution has some cons like
using a traditional SOAP-based WCF service but it is ideal if you want
to consume public data service from other domains.


DotNetKicks Image

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

3 comments

  1. Will SmithApril 20, 2009 ב 16:40

    The work around is to not use the xmlhttp object, but, instead to use json and script tags.  No need for the proxy service.

    http://www.25hoursaday.com/…/JSONVsXMLBrowserSecurityModel.aspx

    http://www.json.com/…/json-vs-rocky

    Reply
  2. Kyle SimpsonApril 22, 2009 ב 21:34

    You could use flXHR (http://flxhr.flensed.com) which is a cross-domain capable Ajax solution, with an identical API to the native XHR object. This means it can just be dropped into a page and it just works like normal Ajax, but with cross-domain ability.

    Reply
  3. Boris ReitmanMay 3, 2009 ב 16:47

    Hello,

    I have implemented a similar solution:

    http://code.google.com/p/crossxhr/

    Reply