WCF REST 服务中的 JSONP 支持





5.00/5 (4投票s)
此示例演示如何构建支持跨域脚本的 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 端点配置了WebHttpBinding
和WebScriptEnablingBehavior
,并具有所有默认设置。
<?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 控件SelectVehicleTyps
和SelectVehicles
。SelectVehicles
的选项来自服务调用。
以下代码在页面呈现后动态添加脚本块。此脚本块调用 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