且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

实现jquery.ajax及原生的XMLHttpRequest跨域调用WCF服务的方法

更新时间:2022-07-01 10:00:47

关于ajax跨域调用WCF服务的方法很多,经过我反复的代码测试,认为如下方法是最为简便的,当然也不能说别人的方法是错误的,下面就来上代码,WCF服务定义还是延用上次的,如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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服务文件,文件内容:

1
<%@ ServiceHost Language="C#" Debug="true" Service="WcfService1.AddService" %>

上面实现的是支持GET方法请求调用,下面就是配置WEB.CONFIG,使其支持跨域调用,注意我将standardEndpoints注释掉了,当然如果不注释也不会有什么影响,关键是bindings节点中的属性:crossDomainScriptAccessEnabled="true",如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<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文件并添加如下的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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服务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//简单封装
        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这个方案就无解:

1
2
3
4
5
6
7
8
9
10
11
12
13
$("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调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$("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来调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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服务端已支持跨域调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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请求,采用的原理是上面我写的原始跨域调用方法)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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时,走了很多弯路,也尝试过很多方法,但最终还是弄明白了,希望大家能从这篇博文中受益,文中不足之处,敬请指出,谢谢!

本文转自 梦在旅途 博客园博客,原文链接:http://www.cnblogs.com/zuowj/p/4820117.html  ,如需转载请自行联系原作者