Making Cross-Domain Ajax Requests for a Data Service
Making Cross-Domain Ajax Requests for a Data Service
One problem that exists when
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:
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.