關于ajax跨域調用WCF服務的方法很多,經過我反複的代碼測試,認為如下方法是最為簡便的,當然也不能說别人的方法是錯誤的,下面就來上代碼,WCF服務定義還是延用上次的,如:
namespace WcfService1
{
[ServiceContract]
public interface IAddService
{
[OperationContract]
[WebInvoke(Method="GET",RequestFormat=WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json,BodyStyle=WebMessageBodyStyle.WrappedRequest)]
int Add2(int a,int b);
}
}
namespace WcfService1
{
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
//[JavascriptCallbackBehavior(UrlParameterName="jsoncallback")] //不指定的時采用預設的callback回調參數
public class AddService : IAddService
{
public int Add2(int a, int b)
{
return a + b;
}
}
}
建立一個WCF服務檔案,檔案内容:
<%@ ServiceHost Language="C#" Debug="true" Service="WcfService1.AddService" %>
上面實作的是支援GET方法請求調用,下面就是配置WEB.CONFIG,使其支援跨域調用,注意我将standardEndpoints注釋掉了,當然如果不注釋也不會有什麼影響,關鍵是bindings節點中的屬性:crossDomainScriptAccessEnabled="true",如下:
<system.serviceModel>
<!--<standardEndpoints>
<webHttpEndpoint>
<standardEndpoint crossDomainScriptAccessEnabled="true" />
</webHttpEndpoint>
</standardEndpoints>-->
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
<bindings>
<webHttpBinding>
<binding crossDomainScriptAccessEnabled="true">
</binding>
</webHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior>
<!-- 為避免洩漏中繼資料資訊,請在部署前将以下值設定為 false 并删除上面的中繼資料終結點 -->
<serviceMetadata httpGetEnabled="true"/>
<!-- 要接收故障異常詳細資訊以進行調試,請将以下值設定為 true。在部署前設定為 false 以避免洩漏異常資訊 -->
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="AddServiceBehavior">
<enableWebScript />
</behavior>
</endpointBehaviors>
</behaviors>
<services>
<service name="WcfService1.AddService">
<endpoint address="" binding="webHttpBinding" contract="WcfService1.IAddService" behaviorConfiguration="AddServiceBehavior" ></endpoint>
</service>
</services>
</system.serviceModel>
建立Global.asax檔案并添加如下的代碼:
protected void Application_BeginRequest(object sender, EventArgs e)
{
HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.NoCache);
HttpContext.Current.Response.Cache.SetNoStore();
EnableCrossDmainAjaxCall();
}
private void EnableCrossDmainAjaxCall()
{
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin","*");
if (HttpContext.Current.Request.HttpMethod == "OPTIONS")
{
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods",
"GET, POST");
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers",
"Content-Type, Accept");
HttpContext.Current.Response.AddHeader("Access-Control-Max-Age",
"1728000");
HttpContext.Current.Response.End();
}
}
下面是實作WEB端跨域調用WCF服務代碼
1.采用原生的XMLHttpRequest跨域調用WCF服務:
//簡單封裝
var $ = function (id) {
return document.getElementById(id);
};
function getXMLHTTPRequest() {
var req = false;
try {
req = new XMLHttpRequest();
} catch (err) {
try {
req = new ActiveXObject("Msxml2.XMLHTTP");
} catch (err) {
try {
req = new ActiveXObject("Microsoft.XMLHTTP");
} catch (err) {
req = false;
}
}
}
return req;
}
//以下為按鈕的點選事件,我采用的同步調用,當然也可以采用回調方式,回調方式的話就需要在請求的URL中加入:callback=回調方法,然後再定義一個回調方法即可
$("btnGet").onclick = function () {
var querystr = "a=" + $("num1").value + "&b=" + $("num2").value;
var xmlhttp = getXMLHTTPRequest();
xmlhttp.open("GET", "http://localhost:30348/addservice.svc/Add2?" + querystr, false);
xmlhttp.send();
var r = eval("(" + xmlhttp.responseText + ")");
$("result").value = r.d;
}
2.通過動态以JS方式請求WCF位址資源實作原始的跨域方法,雖然可以實作跨域調用,但隻支援GET方式,如果需要支援POST這個方案就無解:
$("btnGet").onclick = function () {
var querystr = "a=" + $("num1").value + "&b=" + $("num2").value;
var script =document.getElementById("crossDomainScript_wcf") || document.createElement("script");
script.type = "text/javascript";
script.id = "crossDomainScript_wcf";
script.src = "http://localhost:30348/addservice.svc/Add2?callback=success_callback&" + querystr;
document.getElementsByTagName("head")[0].appendChild(script);
}
//回調方法
function success_callback(data) {
$("result").value = data;
}
以下是POST調用:
$("btnGet").onclick = function () {
var xmlhttp = getXMLHTTPRequest();
xmlhttp.open("POST", "http://localhost:30348/addservice.svc/Add2", true);
xmlhttp.setRequestHeader("Content-Type", "application/json");
xmlhttp.onreadystatechange = function () {
alert(xmlhttp.status);
if (xmlhttp.readyState == 4) {
if (xmlhttp.status == 200) {
var r = eval("(" + xmlhttp.responseText + ")");
$("result").value = r.d;
}
}
};
xmlhttp.send('{"a":' + $("num1").value + ',"b":' + $("num2").value + '}');
}
2.采用jQuery.ajax來調用:
var jq = jQuery.noConflict();
jq("#btnGet").click(function () {
jq.ajax("http://localhost:30348/AddService.svc/Add2", {
type: "get",
dataType: "jsonp",
data: 'a=' + jq("#num1").val() + '&b=' + jq("#num2").val(),
success: function (data) {
jq("#result").val(data);
},
error: function (x, textStatus, errorThrown) {
alert("error:" + textStatus);
}
});
});
其實可按正常方式直接調用,無需采用JSONP,因為WCF服務端已支援跨域調用:
var jq = jQuery.noConflict();
jq("#btnGet11").click(function () {
jq.ajax("http://localhost:30348/AddService.svc/Add2", {
type: "GET",
dataType: "json",
data: 'a=' + jq("#num1").val() + '&b=' + jq("#num2").val(),
success: function (data) {
jq("#result").val(data.d);
},
error: function (x, textStatus, errorThrown) {
alert("error:" + textStatus);
}
});
});
當然傳參時也可以用JSON的寫法(注意POST與GET的JSON寫法有所不同,POST時鍵值必需是嚴格的JSON字元串,GET時是一個JS對象),再此就不作說明
POST調用:(注意上述JQUERY.AJAX 采用JSONP+GET模式不适用于POST模式,因為經調試,發現采用JSONP模式,終始發起的是GET請求,采用的原理是上面我寫的原始跨域調用方法)
var jq = jQuery.noConflict();
jq("#btnGet").click(function () {
jq.ajax("http://localhost:30348/AddService.svc/Add2", {
type: "POST",
dataType: "json",
contentType: "application/json",
data: '{"a":' + jq("#num1").val() + ',"b":' + jq("#num2").val() + '}',
success: function (data) {
jq("#result").val(data.d);
},
error: function (x, textStatus, errorThrown) {
alert("error:" + textStatus);
}
});
});
這裡針對跨域再特别說明一下,若采用AJAX跨域調用時,會發送兩次請求,第一次為OPTIONS,用于伺服器進行預檢,第二次才會發出真正的請求,這也就是為什麼WCF服務的Global.asax需要添加EnableCrossDmainAjaxCall的原因。本人在研究跨域調用WCF時,走了很多彎路,也嘗試過很多方法,但最終還是弄明白了,希望大家能從這篇博文中受益,文中不足之處,敬請指出,謝謝!