Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

Support for JSONP in WCF REST services

5.00/5 (4 votes)
9 Jul 2012CPOL3 min read 43.9K   752  
Sample to demonstrate building a cross domain script enabled WCF REST service and consuming it in a web app.

Introduction

According to the "Same origin policy", if the domain name, protocol and port number is different from the source of data retrieval then it is not permitted. However html <script src=""> is allowed to do content retrieval from the foreign sources. This is the idea behind the JSONP (JSON with padding) origin.

JSONP is widely used to request the data from a server which resides in a different domain. The process by which JSONP does this is very simple. It dynamically adds script tags to the current document and results will be returned using a callback function.

.Net 4.0 adds support for JSONP in WCF.

WCF support for JSONP

You can make a WCF service ready for cross domain scripting (JSONP) by using WebHttpBinding and setting crossDomainScriptAccessEnabled to "true". But there is one more way too. That is by using standard end points. WebScriptEndpoint is a standard end point which adds the WebScriptEnabling behavior automatically by using fixed WebHttpBinding.

Building the service

To demo, I will be building here a WCF service which returns an array of string to load the options for html select control in the UI.

As per the below code, class OptionsService which is a service contract exposes a method called GetOptions. Method GetOptions takes in typeId as input parameter and returns ResponseOption object which is a data contract. Attribute [WebGet(ResponseFormat = WebMessageFormat.Json)] is mainly added for the GetOpions to make it accessible by REST programming models and also to indicate response data format is JSON.

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
using System.ServiceModel.Activation;
 
namespace OptionsBuilderService
{
    [DataContract]
    public class ResponseOption
    {
        [DataMember]
        public string[] Options;
    }
 
    [ServiceContract(Namespace = "OptionsBuilderService")]
    //Applied to a service to indicate whether that service can be run in ASP.NET compatibility code.
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class OptionsService
    {
        //Indicates that a service operation is logically a retrieval
        //operation and that it can be called by the REST programming model.
        //Also indiactes response data format is Json.
        [OperationContract]
        [WebGet(ResponseFormat = WebMessageFormat.Json)]
        public ResponseOption GetOptions(int typeId)
        {
            List<string> options = new List<string>();
            ResponseOption rOptions = new ResponseOption();
            switch (typeId)
            {
                case 1:
                    options.Add("Hero Honda");
                    options.Add("Bajaj");
                    options.Add("Harley Dvson");
                    options.Add("TVS");
                    break;
                case 2:
                    options.Add("Toyota");
                    options.Add("TATA");
                    options.Add("General Motors");
                    options.Add("Audi");
                    options.Add("BMW");
                    break;
            }
            rOptions.Options = options.ToArray();
            return rOptions;
        }
    }
}

Below is configuration setting needed to make the above service ready for cross domain scripting (JSONP). I have used webScriptEndpoint and set crossDomainScriptAccessEnabled to true.

I have used serviceActivations tags as I have not added .svc files to my WCF project. If you have .svc file and if you are using it then in mark up for "@SeviceHost" directive set "factory" attribute to "System.ServiceModel.Activation.WebScriptServiceHostFactory".

WebScriptServiceHostFactory adds an ASP.NET AJAX endpoint to the service without requiring configuration. The ASP.NET AJAX endpoint created by this factory is configured with the WebHttpBinding and the WebScriptEnablingBehavior, with all the default settings.

XML
<?xml version="1.0"?>
<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.0" />
    <authentication mode="None" />
  </system.web>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
  </system.webServer>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceMetadata httpGetEnabled="true" />
        </behavior>  
      </serviceBehaviors>
    </behaviors>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true"
      multipleSiteBindingsEnabled="true">
      <!--I have added below tag as i have not used svc file in the project-->
      <serviceActivations>
        <add relativeAddress="OptionsService.svc" service="OptionsBuilderService.OptionsService"
          factory="System.ServiceModel.Activation.WebScriptServiceHostFactory" />
      </serviceActivations>
    </serviceHostingEnvironment>
    <standardEndpoints>
      <!-- Enabling cross domain script access-->
      <webScriptEndpoint>
        <standardEndpoint name="" crossDomainScriptAccessEnabled="true"/>
      </webScriptEndpoint>
    </standardEndpoints>
  </system.serviceModel>
</configuration>

Building the client

The client here is a simple ASP.NET application. It has a page called Default.aspx which uses Master.master. Default.aspx has two HTML Select controls SelectVehicleTyps and SelectVehicles. Options for SelectVehicles are coming from the service call.

Below code dynamically adds script block after the page is rendered. And this script block makes call to the WCF REST service and gets the options to load the SelectVehicles control.

When the WCF REST service responds callback function is invoked with the response data and data is used for the further usage. The script tag injection and the callback function invoke are automatically handled by the ASP.NET AJAX ScriptManager. Usage here is similar to the ASP.NET AJAX proxies but with addition one line to enable the JSONP. Go through the makeMyServiceCall() function to see the JSONP enabling.

JavaScript
<script type="text/javascript">
 
     //This funcition makes the call to service 
     function makeMyServiceCall() {
         var sel = document.getElementById("SelectVehicleTyps");
         var val = sel.options[sel.selectedIndex].value;
 
         var proxy = new OptionsBuilderService.OptionsService();
         proxy.set_enableJsonp(true); //Enabling the JSONP
         proxy.GetOptions(parseInt(val), onSuccess, onFail, null);
     }
 
     // This is called when the service call successfully returns the result
     function onSuccess(result) {
         var select = document.getElementById("SelectVehicles");
         select.options.length = 0; // clear out existing items
         for (var i = 0; i < result.Options.length; i++) {
             var d = result.Options[i];
             select.options.add(new Option(d, i))//add new option
         }
     }
 
     // This is called if the service call fails due to some error
     function onFail() {
         alert("Error happend!")
     }
    
</script>
XML
<asp:ScriptManager ID="ScriptManager1" runat="server">
        <Services>
            <asp:ServiceReference Path="http://localhost:7838/OptionsService.svc" />
        </Services>
    </asp:ScriptManager>
    <br />
    Select the vehicle type:
    <select id="SelectVehicleTyps" onchange="makeMyServiceCall();"> <%-- Make the service call --%>
        <option value="1">Motor Bike</option>
         <option value="2">Car</option>
    </select>
    <select id="SelectVehicles">
        <option></option>
    </select>
     <br />
    <script type="text/javascript" defer="defer">makeMyServiceCall();</script>
     <%-- Make the service call --%>

When the selected option of the SelectVehicleTyps is changed new options are loaded to the SelectVehicles.

Points of Interest

* Validating the response.

* Setting X-Content-Type-Options: nosniff .

History

V1.0

License

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