分享XmlHttpRequest調(diào)用Webservice的一點(diǎn)心得
更新時(shí)間:2012年07月20日 20:01:18 作者:
因?yàn)轫?xiàng)目需要,以后前端、手機(jī)客戶端調(diào)用ASP.NET的Webservice來(lái)獲取信息.所以這段時(shí)間開始看Webservice,試著通過(guò)XmlHttpRequest調(diào)用Webservice,過(guò)程中碰到不少問(wèn)題,也有不少的收獲
首先,因?yàn)镴SON對(duì)于JS的便利性,考慮通過(guò)JSON來(lái)請(qǐng)求和返回?cái)?shù)據(jù)。在JS中實(shí)例化一個(gè)xmlHttpRequest對(duì)象,然后根據(jù)網(wǎng)上的說(shuō)明POST的地址為:asmx頁(yè)面地址/Web方法名。在RequestHeader中設(shè)置Content-Type為application/json; charset=utf-8,SOAPAction設(shè)為Web方法名。Web方法的參數(shù)用JSON格式send出去。
代碼如下:
function getXmlHttp() {
var xmlHttp;
if (window.XMLHttpRequest)
{ // code for IE7+, Firefox, Chrome, Opera, Safari
xmlHttp = new XMLHttpRequest();
}
else
{ // code for IE6, IE5
xmlHttp = new ActiveXObject('Microsoft.XMLHTTP');
}
return xmlHttp;
}
function webservice(url, action, data, success, error, complete, failed) {
var xmlHttp = getXmlHttp(); //獲取XMLHttpRequest對(duì)象
xmlHttp.open('POST', url + '/' + action, true); //異步請(qǐng)求數(shù)據(jù)
xmlHttp.onreadystatechange = function () {
if (xmlHttp.readyState == 4) {
try {
if (xmlHttp.status == 200 && typeof (success) == 'function') {
success(xmlHttp.responseText);
}
else if ((xmlHttp.status / 100 == 4 || xmlHttp.status / 100 == 5) && typeof (error) == 'function') {
error(xmlHttp.responseText, xmlHttp.status);
}
else if (xmlHttp.status / 100 == 200 && typeof (complete) == 'function') {
complete(xmlHttp.responseText, xmlHttp.status);
}
else if (typeof (failed) == 'function') {
failed(xmlHttp.responseText, xmlHttp.status);
}
}
catch (e) {
}
}
}
xmlHttp.setRequestHeader('Content-Type', 'application/json; charset=utf-8');
xmlHttp.setRequestHeader('SOAPAction', action);
xmlHttp.send(data);
}
比如請(qǐng)求調(diào)用Webservice1中的HelloWorld方法:
webservice('/Webservice1.asmx','HelloWorld','{}',function (msg) { alert(msg); });
調(diào)用前請(qǐng)記得把Webservice1上面的 [System.Web.Script.Services.ScriptService] 取消注釋,調(diào)用后可以看到彈出警告窗口:{"d": "Hello World"}。把Content-Type設(shè)為text/xml時(shí),警告窗口的內(nèi)容變就變成了<?xml version="1.0" encoding="utf-8"?> <string xmlns="http://tempuri.org/">Hello World</string>。
這時(shí)候雖然參數(shù)“{}”還是JSON的形式請(qǐng)求卻是XML格式,但因?yàn)镠ello World沒(méi)有參數(shù),所以忽略了內(nèi)容的格式,能夠正常返回值。
如果修改服務(wù)端的HelloWorld方法,添加一個(gè)string類型的參數(shù)somebody。
[WebMethod]
public string HelloWorld(string somebody) {
return "Hello World&Hello, " + somebody + "!";
}
將請(qǐng)求端的Content-Type改回application/json,傳送參數(shù)改為{"somebody": "Krime"},調(diào)用后彈出窗口內(nèi)容變?yōu)閧d: "Hello World&Hello, Krime!"}。
但如果這時(shí)再直接把Content-Type改為text/xml,調(diào)用后服務(wù)器將會(huì)報(bào)錯(cuò):System.InvalidOperationException: 請(qǐng)求格式無(wú)效: text/xml; charset=UTF-8。 在 System.Web.Services.Protocols.HttpServerProtocol.ReadParameters() 在 System.Web.Services.Protocols.WebServiceHandler.CoreProcessRequest()
于是我們把參數(shù)格式也修改一下,按照Webservice調(diào)試頁(yè)面的示例,將參數(shù)改為:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<HelloWorld xmlns="http://tempuri.org/">
<somebody>Krime</somebody>
</HelloWorld>
</soap:Body>
</soap:Envelope>
這樣應(yīng)該就能正常返回XML的結(jié)果了吧?結(jié)果卻出乎意料,服務(wù)器仍然報(bào)同樣的錯(cuò)誤。
折騰了很久后,幾乎要抓狂了,難道ASP.NET突然不認(rèn)識(shí)XML了?這個(gè)時(shí)候,再回去仔細(xì)看看Webservice調(diào)試頁(yè)面的示例,終于發(fā)現(xiàn)了一點(diǎn)問(wèn)題:
POST /WebServiceTest/Webservice1.asmx HTTP/1.1
Host: localhost
Content-Type: text/xml; charset=utf-8
Content-Length: length
SOAPAction: "http://tempuri.org/HelloWorld"
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<HelloWorld xmlns="http://tempuri.org/">
<somebody>string</somebody>
</HelloWorld>
</soap:Body>
</soap:Envelope>
上面POST的地址后面并沒(méi)有像請(qǐng)求JSON數(shù)據(jù)時(shí)一樣加上/方法名,而SOAPAction的方法名前面還需要加上命名空間。于是修改XMLHttpRequest的請(qǐng)求頭,url和action做相應(yīng)修改,結(jié)果終于正常返回了XML的結(jié)果:<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Body><HelloWorldResponse xmlns="http://tempuri.org/"><HelloWorldResult>Hello World&Hello, Krime!</HelloWorldResult></HelloWorldResponse></soap:Body></soap:Envelope>
后來(lái)繼續(xù)測(cè)試,發(fā)現(xiàn)請(qǐng)求內(nèi)容類型為application/json時(shí),SOAPAction完全可以忽略不加,但是url后面一定要加上/方法名,否則服務(wù)器不會(huì)返回?cái)?shù)據(jù)。而請(qǐng)求text/xml時(shí),SOAPAction是必須的且前面要加上命名空間,url后面則不能有/方法名。
最后,經(jīng)過(guò)總結(jié),將代碼改成了最終的樣子:
function getXmlHttp() {
var xmlHttp;
if (window.XMLHttpRequest)
{ // code for IE7+, Firefox, Chrome, Opera, Safari
xmlHttp = new XMLHttpRequest();
}
else
{ // code for IE6, IE5
xmlHttp = new ActiveXObject('Microsoft.XMLHTTP');
}
return xmlHttp;
}
function webservice(url, options) {
if (typeof (url) == 'object') { //將url寫在options里的情況
options = url;
url = url.url;
}
if (!url) return;
if (options.dataType.toLowerCase() == 'json') { //請(qǐng)求JSON格式的數(shù)據(jù)時(shí),url后面需要加上“/方法名”
url = url + '/' + options.method;
}
var xmlHttp = getXmlHttp(); //獲取XMLHttpRequest對(duì)象
xmlHttp.open('POST', url, true); //異步請(qǐng)求數(shù)據(jù)
xmlHttp.onreadystatechange = function () {
if (xmlHttp.readyState == 4) {
try {
if (xmlHttp.status == 200 && typeof (options.success) == 'function') {
options.success(xmlHttp.responseText);
}
else if ((xmlHttp.status / 100 == 4 || xmlHttp.status / 100 == 5) && typeof (options.error) == 'function') {
options.error(xmlHttp.responseText, xmlHttp.status);
}
else if (xmlHttp.status / 100 == 200 && typeof (options.complete) == 'function') {
options.complete(xmlHttp.responseText, xmlHttp.status);
}
else if (typeof (options.failed) == 'function') {
options.failed(xmlHttp.responseText, xmlHttp.status);
}
}
catch (e) {
}
}
}
xmlHttp.setRequestHeader('Content-Type', options.contentType); //設(shè)置請(qǐng)求頭的ContentType
xmlHttp.setRequestHeader('SOAPAction', options.namespace + options.method); //設(shè)置SOAPAction
xmlHttp.send(options.data); //發(fā)送參數(shù)數(shù)據(jù)
}
請(qǐng)求JSON數(shù)據(jù):
window.onload = function () {
var data = '{"somebody": "Krime"}';
var options = {
namespace: 'http://tempuri.org/',
method: 'HelloWorld',
contentType: 'application/json; charset=utf-8',
dataType: 'json',
data: data,
success: function (msg) {
alert(msg);
}
};
webservice('http://localhost:8003/WebServiceTest/Webservice1.asmx', options);
};
請(qǐng)求XML數(shù)據(jù):
window.onload = function () {
var data = '<?xml version="1.0" encoding="utf-8"?>'
+ '<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">'
+ '<soap:Body>'
+ '<HelloWorld xmlns="http://tempuri.org/">'
+ '<somebody>Krime</somebody>'
+ '</HelloWorld>'
+ '</soap:Body>'
+ '</soap:Envelope>';
var options = {
namespace: 'http://tempuri.org/',
method: 'HelloWorld',
contentType: 'text/xml; charset=utf-8',
dataType: 'xml',
data: data,
success: function (msg) {
alert(msg);
}
};
webservice('http://localhost:8003/WebServiceTest/Webservice1.asmx', options);
};
測(cè)試情況正常。
需要注意的一點(diǎn)是,請(qǐng)求JSON數(shù)據(jù)時(shí),如果返回類型是DataTable是不行的,需要轉(zhuǎn)換成相應(yīng)數(shù)據(jù)實(shí)體類的List<>再返回。
在解決返回XML問(wèn)題的過(guò)程中,還找到另一種解決方法。具體操作時(shí),是將ContentType設(shè)為application/x-www-form-urlencoded,數(shù)據(jù)體不用JSON也不用XML格式的SOAP包,而是用類似QueryString的“arguement1=XXX&arguement2=XXX”。這個(gè)方法是模擬了窗體數(shù)據(jù)的HTTP POST格式,將每個(gè)控件值編碼為名稱=值對(duì)發(fā)送出去。
這種情況下的頁(yè)面地址后面也需要加上/方法名。
代碼如下:
復(fù)制代碼 代碼如下:
function getXmlHttp() {
var xmlHttp;
if (window.XMLHttpRequest)
{ // code for IE7+, Firefox, Chrome, Opera, Safari
xmlHttp = new XMLHttpRequest();
}
else
{ // code for IE6, IE5
xmlHttp = new ActiveXObject('Microsoft.XMLHTTP');
}
return xmlHttp;
}
function webservice(url, action, data, success, error, complete, failed) {
var xmlHttp = getXmlHttp(); //獲取XMLHttpRequest對(duì)象
xmlHttp.open('POST', url + '/' + action, true); //異步請(qǐng)求數(shù)據(jù)
xmlHttp.onreadystatechange = function () {
if (xmlHttp.readyState == 4) {
try {
if (xmlHttp.status == 200 && typeof (success) == 'function') {
success(xmlHttp.responseText);
}
else if ((xmlHttp.status / 100 == 4 || xmlHttp.status / 100 == 5) && typeof (error) == 'function') {
error(xmlHttp.responseText, xmlHttp.status);
}
else if (xmlHttp.status / 100 == 200 && typeof (complete) == 'function') {
complete(xmlHttp.responseText, xmlHttp.status);
}
else if (typeof (failed) == 'function') {
failed(xmlHttp.responseText, xmlHttp.status);
}
}
catch (e) {
}
}
}
xmlHttp.setRequestHeader('Content-Type', 'application/json; charset=utf-8');
xmlHttp.setRequestHeader('SOAPAction', action);
xmlHttp.send(data);
}
比如請(qǐng)求調(diào)用Webservice1中的HelloWorld方法:
復(fù)制代碼 代碼如下:
webservice('/Webservice1.asmx','HelloWorld','{}',function (msg) { alert(msg); });
調(diào)用前請(qǐng)記得把Webservice1上面的 [System.Web.Script.Services.ScriptService] 取消注釋,調(diào)用后可以看到彈出警告窗口:{"d": "Hello World"}。把Content-Type設(shè)為text/xml時(shí),警告窗口的內(nèi)容變就變成了<?xml version="1.0" encoding="utf-8"?> <string xmlns="http://tempuri.org/">Hello World</string>。
這時(shí)候雖然參數(shù)“{}”還是JSON的形式請(qǐng)求卻是XML格式,但因?yàn)镠ello World沒(méi)有參數(shù),所以忽略了內(nèi)容的格式,能夠正常返回值。
如果修改服務(wù)端的HelloWorld方法,添加一個(gè)string類型的參數(shù)somebody。
復(fù)制代碼 代碼如下:
[WebMethod]
public string HelloWorld(string somebody) {
return "Hello World&Hello, " + somebody + "!";
}
將請(qǐng)求端的Content-Type改回application/json,傳送參數(shù)改為{"somebody": "Krime"},調(diào)用后彈出窗口內(nèi)容變?yōu)閧d: "Hello World&Hello, Krime!"}。
但如果這時(shí)再直接把Content-Type改為text/xml,調(diào)用后服務(wù)器將會(huì)報(bào)錯(cuò):System.InvalidOperationException: 請(qǐng)求格式無(wú)效: text/xml; charset=UTF-8。 在 System.Web.Services.Protocols.HttpServerProtocol.ReadParameters() 在 System.Web.Services.Protocols.WebServiceHandler.CoreProcessRequest()
于是我們把參數(shù)格式也修改一下,按照Webservice調(diào)試頁(yè)面的示例,將參數(shù)改為:
復(fù)制代碼 代碼如下:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<HelloWorld xmlns="http://tempuri.org/">
<somebody>Krime</somebody>
</HelloWorld>
</soap:Body>
</soap:Envelope>
這樣應(yīng)該就能正常返回XML的結(jié)果了吧?結(jié)果卻出乎意料,服務(wù)器仍然報(bào)同樣的錯(cuò)誤。
折騰了很久后,幾乎要抓狂了,難道ASP.NET突然不認(rèn)識(shí)XML了?這個(gè)時(shí)候,再回去仔細(xì)看看Webservice調(diào)試頁(yè)面的示例,終于發(fā)現(xiàn)了一點(diǎn)問(wèn)題:
復(fù)制代碼 代碼如下:
POST /WebServiceTest/Webservice1.asmx HTTP/1.1
Host: localhost
Content-Type: text/xml; charset=utf-8
Content-Length: length
SOAPAction: "http://tempuri.org/HelloWorld"
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<HelloWorld xmlns="http://tempuri.org/">
<somebody>string</somebody>
</HelloWorld>
</soap:Body>
</soap:Envelope>
上面POST的地址后面并沒(méi)有像請(qǐng)求JSON數(shù)據(jù)時(shí)一樣加上/方法名,而SOAPAction的方法名前面還需要加上命名空間。于是修改XMLHttpRequest的請(qǐng)求頭,url和action做相應(yīng)修改,結(jié)果終于正常返回了XML的結(jié)果:<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Body><HelloWorldResponse xmlns="http://tempuri.org/"><HelloWorldResult>Hello World&Hello, Krime!</HelloWorldResult></HelloWorldResponse></soap:Body></soap:Envelope>
后來(lái)繼續(xù)測(cè)試,發(fā)現(xiàn)請(qǐng)求內(nèi)容類型為application/json時(shí),SOAPAction完全可以忽略不加,但是url后面一定要加上/方法名,否則服務(wù)器不會(huì)返回?cái)?shù)據(jù)。而請(qǐng)求text/xml時(shí),SOAPAction是必須的且前面要加上命名空間,url后面則不能有/方法名。
最后,經(jīng)過(guò)總結(jié),將代碼改成了最終的樣子:
復(fù)制代碼 代碼如下:
function getXmlHttp() {
var xmlHttp;
if (window.XMLHttpRequest)
{ // code for IE7+, Firefox, Chrome, Opera, Safari
xmlHttp = new XMLHttpRequest();
}
else
{ // code for IE6, IE5
xmlHttp = new ActiveXObject('Microsoft.XMLHTTP');
}
return xmlHttp;
}
function webservice(url, options) {
if (typeof (url) == 'object') { //將url寫在options里的情況
options = url;
url = url.url;
}
if (!url) return;
if (options.dataType.toLowerCase() == 'json') { //請(qǐng)求JSON格式的數(shù)據(jù)時(shí),url后面需要加上“/方法名”
url = url + '/' + options.method;
}
var xmlHttp = getXmlHttp(); //獲取XMLHttpRequest對(duì)象
xmlHttp.open('POST', url, true); //異步請(qǐng)求數(shù)據(jù)
xmlHttp.onreadystatechange = function () {
if (xmlHttp.readyState == 4) {
try {
if (xmlHttp.status == 200 && typeof (options.success) == 'function') {
options.success(xmlHttp.responseText);
}
else if ((xmlHttp.status / 100 == 4 || xmlHttp.status / 100 == 5) && typeof (options.error) == 'function') {
options.error(xmlHttp.responseText, xmlHttp.status);
}
else if (xmlHttp.status / 100 == 200 && typeof (options.complete) == 'function') {
options.complete(xmlHttp.responseText, xmlHttp.status);
}
else if (typeof (options.failed) == 'function') {
options.failed(xmlHttp.responseText, xmlHttp.status);
}
}
catch (e) {
}
}
}
xmlHttp.setRequestHeader('Content-Type', options.contentType); //設(shè)置請(qǐng)求頭的ContentType
xmlHttp.setRequestHeader('SOAPAction', options.namespace + options.method); //設(shè)置SOAPAction
xmlHttp.send(options.data); //發(fā)送參數(shù)數(shù)據(jù)
}
請(qǐng)求JSON數(shù)據(jù):
復(fù)制代碼 代碼如下:
window.onload = function () {
var data = '{"somebody": "Krime"}';
var options = {
namespace: 'http://tempuri.org/',
method: 'HelloWorld',
contentType: 'application/json; charset=utf-8',
dataType: 'json',
data: data,
success: function (msg) {
alert(msg);
}
};
webservice('http://localhost:8003/WebServiceTest/Webservice1.asmx', options);
};
請(qǐng)求XML數(shù)據(jù):
復(fù)制代碼 代碼如下:
window.onload = function () {
var data = '<?xml version="1.0" encoding="utf-8"?>'
+ '<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">'
+ '<soap:Body>'
+ '<HelloWorld xmlns="http://tempuri.org/">'
+ '<somebody>Krime</somebody>'
+ '</HelloWorld>'
+ '</soap:Body>'
+ '</soap:Envelope>';
var options = {
namespace: 'http://tempuri.org/',
method: 'HelloWorld',
contentType: 'text/xml; charset=utf-8',
dataType: 'xml',
data: data,
success: function (msg) {
alert(msg);
}
};
webservice('http://localhost:8003/WebServiceTest/Webservice1.asmx', options);
};
測(cè)試情況正常。
需要注意的一點(diǎn)是,請(qǐng)求JSON數(shù)據(jù)時(shí),如果返回類型是DataTable是不行的,需要轉(zhuǎn)換成相應(yīng)數(shù)據(jù)實(shí)體類的List<>再返回。
在解決返回XML問(wèn)題的過(guò)程中,還找到另一種解決方法。具體操作時(shí),是將ContentType設(shè)為application/x-www-form-urlencoded,數(shù)據(jù)體不用JSON也不用XML格式的SOAP包,而是用類似QueryString的“arguement1=XXX&arguement2=XXX”。這個(gè)方法是模擬了窗體數(shù)據(jù)的HTTP POST格式,將每個(gè)控件值編碼為名稱=值對(duì)發(fā)送出去。
這種情況下的頁(yè)面地址后面也需要加上/方法名。
相關(guān)文章
原生JS實(shí)現(xiàn)網(wǎng)頁(yè)手機(jī)音樂(lè)播放器 歌詞同步播放的示例
下面小編就為大家分享一篇原生JS實(shí)現(xiàn)網(wǎng)頁(yè)手機(jī)音樂(lè)播放器 歌詞同步播放的示例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-02-02
微信小程序中this.data與this.setData的區(qū)別詳解
這篇文章主要給大家介紹了關(guān)于微信小程序中this.data與this.setData區(qū)別的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起看看吧2018-09-09
JavaScript面試之如何實(shí)現(xiàn)數(shù)組拍平(扁平化)方法
數(shù)組扁平化是指將一個(gè)多維數(shù)組變?yōu)橐痪S數(shù)組,下面這篇文章主要給大家介紹了關(guān)于JavaScript面試之如何實(shí)現(xiàn)數(shù)組拍平(扁平化)方法的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2021-11-11
js使用正則表達(dá)式對(duì)身份證號(hào)的判斷
這篇文章主要給大家介紹了關(guān)于js使用正則表達(dá)式對(duì)身份證號(hào)的判斷的相關(guān)資料,在用戶注冊(cè)頁(yè)面有些需求要求的比較嚴(yán)格,需要對(duì)身份證驗(yàn)證是否合法,通過(guò)此功能嚴(yán)格此系統(tǒng)軟件,從而過(guò)濾到很多,需要的朋友可以參考下2023-08-08
理解javascript中的回調(diào)函數(shù)(callback)
這篇文章主要介紹了理解javascript中的回調(diào)函數(shù)(callback),本文著重于對(duì)回調(diào)函數(shù)概念的理解,需要的朋友可以參考下2014-09-09

