65.9K
CodeProject 正在变化。 阅读更多。
Home

WCF REST 服务中的 JSONP 支持

starIconstarIconstarIconstarIconstarIcon

5.00/5 (4投票s)

2012年7月9日

CPOL

3分钟阅读

viewsIcon

44449

downloadIcon

778

此示例演示如何构建支持跨域脚本的 WCF REST 服务并在 Web 应用程序中使用它。

引言

根据“同源策略”,如果域名、协议和端口号与数据检索源不同,则不允许访问。但是,html <script src=""> 允许从外部来源检索内容。这就是 JSONP(带填充的 JSON)的起源。

JSONP 广泛用于请求驻留在不同域中的服务器的数据。JSONP 执行此操作的过程非常简单。它会动态地向当前文档添加脚本标签,结果将使用回调函数返回。

.Net 4.0 在 WCF 中添加了对 JSONP 的支持。

WCF 对 JSONP 的支持

您可以使用WebHttpBinding 并将crossDomainScriptAccessEnabled 设置为“true”来使 WCF 服务准备好进行跨域脚本 (JSONP)。但还有一种方法。那就是使用标准端点。WebScriptEndpoint 是一个标准端点,它使用固定的WebHttpBinding自动添加WebScriptEnabling行为。

构建服务

为了演示,我将在此处构建一个 WCF 服务,该服务返回一个字符串数组以加载 UI 中 html select 控件的选项。

根据以下代码,作为服务契约的 OptionsService 类公开了一种名为GetOptions的方法。GetOptions方法接收 typeId 作为输入参数,并返回ResponseOption对象(这是一个数据契约)。主要为GetOpions添加了属性 [WebGet(ResponseFormat = WebMessageFormat.Json)] 以使其可以通过 REST 编程模型访问,并指示响应数据格式为 JSON。

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;
        }
    }
}

以下是使上述服务准备好进行跨域脚本 (JSONP) 所需的配置设置。我使用了webScriptEndpoint并将crossDomainScriptAccessEnabled设置为 true。

我使用了serviceActivations标签,因为我没有将 .svc 文件添加到我的 WCF 项目中。如果您有 .svc 文件并且正在使用它,那么在“@SeviceHost”指令的标记中,将“factory”属性设置为“System.ServiceModel.Activation.WebScriptServiceHostFactory”。

WebScriptServiceHostFactory 会向服务添加一个 ASP.NET AJAX 端点,而无需配置。此工厂创建的 ASP.NET AJAX 端点配置了WebHttpBindingWebScriptEnablingBehavior,并具有所有默认设置。

<?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>

构建客户端

此处的客户端是一个简单的 ASP.NET 应用程序。它有一个名为 *Default.aspx* 的页面,该页面使用 Master.master。*Default.aspx* 有两个 HTML Select 控件SelectVehicleTypsSelectVehiclesSelectVehicles的选项来自服务调用。

以下代码在页面呈现后动态添加脚本块。此脚本块调用 WCF REST 服务并获取选项以加载SelectVehicles控件。

当 WCF REST 服务响应时,将使用响应数据调用回调函数,并将数据用于进一步使用。脚本标签注入和回调函数调用由 ASP.NET AJAX ScriptManager自动处理。这里的用法类似于 ASP.NET AJAX 代理,但增加了一行以启用 JSONP。请查看makeMyServiceCall()函数以查看 JSONP 的启用方式。

<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>
<asp:ScriptManager ID="ScriptManager1" runat="server">
        <Services>
            <asp:ServiceReference Path="https://: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 --%>

SelectVehicleTyps的选中选项更改时,将向SelectVehicles加载新选项。

关注点

* 验证响应。

* 设置 X-Content-Type-Options: nosniff

历史

V1.0

© . All rights reserved.