分享XmlHttpRequest调用Webservice的一点心得


Posted in Javascript onJuly 20, 2012

首先,因为JSON对于JS的便利性,考虑通过JSON来请求和返回数据。在JS中实例化一个xmlHttpRequest对象,然后根据网上的说明POST的地址为:asmx页面地址/Web方法名。在RequestHeader中设置Content-Type为application/json; charset=utf-8,SOAPAction设为Web方法名。Web方法的参数用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对象 
xmlHttp.open('POST', url + '/' + action, true); //异步请求数据 
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); 
}

比如请求调用Webservice1中的HelloWorld方法:
webservice('/Webservice1.asmx','HelloWorld','{}',function (msg) { alert(msg); });

调用前请记得把Webservice1上面的 [System.Web.Script.Services.ScriptService] 取消注释,调用后可以看到弹出警告窗口:{"d": "Hello World"}。把Content-Type设为text/xml时,警告窗口的内容变就变成了<?xml version="1.0" encoding="utf-8"?> <string xmlns="http://tempuri.org/">Hello World</string>。
这时候虽然参数“{}”还是JSON的形式请求却是XML格式,但因为Hello World没有参数,所以忽略了内容的格式,能够正常返回值。
如果修改服务端的HelloWorld方法,添加一个string类型的参数somebody。
[WebMethod] 
public string HelloWorld(string somebody) { 
return "Hello World&Hello, " + somebody + "!"; 
}

将请求端的Content-Type改回application/json,传送参数改为{"somebody": "Krime"},调用后弹出窗口内容变为{d: "Hello World&Hello, Krime!"}。
但如果这时再直接把Content-Type改为text/xml,调用后服务器将会报错:System.InvalidOperationException: 请求格式无效: text/xml; charset=UTF-8。 在 System.Web.Services.Protocols.HttpServerProtocol.ReadParameters() 在 System.Web.Services.Protocols.WebServiceHandler.CoreProcessRequest()
于是我们把参数格式也修改一下,按照Webservice调试页面的示例,将参数改为:
<?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>

这样应该就能正常返回XML的结果了吧?结果却出乎意料,服务器仍然报同样的错误。
折腾了很久后,几乎要抓狂了,难道ASP.NET突然不认识XML了?这个时候,再回去仔细看看Webservice调试页面的示例,终于发现了一点问题:
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的地址后面并没有像请求JSON数据时一样加上/方法名,而SOAPAction的方法名前面还需要加上命名空间。于是修改XMLHttpRequest的请求头,url和action做相应修改,结果终于正常返回了XML的结果:<?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>
后来继续测试,发现请求内容类型为application/json时,SOAPAction完全可以忽略不加,但是url后面一定要加上/方法名,否则服务器不会返回数据。而请求text/xml时,SOAPAction是必须的且前面要加上命名空间,url后面则不能有/方法名。
最后,经过总结,将代码改成了最终的样子:
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') { //请求JSON格式的数据时,url后面需要加上“/方法名” 
url = url + '/' + options.method; 
} 
var xmlHttp = getXmlHttp(); //获取XMLHttpRequest对象 
xmlHttp.open('POST', url, true); //异步请求数据 
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); //设置请求头的ContentType 
xmlHttp.setRequestHeader('SOAPAction', options.namespace + options.method); //设置SOAPAction 
xmlHttp.send(options.data); //发送参数数据 
}

请求JSON数据:
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); 
};

请求XML数据:
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); 
};

测试情况正常。
需要注意的一点是,请求JSON数据时,如果返回类型是DataTable是不行的,需要转换成相应数据实体类的List<>再返回。

在解决返回XML问题的过程中,还找到另一种解决方法。具体操作时,是将ContentType设为application/x-www-form-urlencoded,数据体不用JSON也不用XML格式的SOAP包,而是用类似QueryString的“arguement1=XXX&arguement2=XXX”。这个方法是模拟了窗体数据的HTTP POST格式,将每个控件值编码为名称=值对发送出去。

这种情况下的页面地址后面也需要加上/方法名。

Javascript 相关文章推荐
jquery对单选框,多选框,文本框等常见操作小结
Jan 08 Javascript
jquery使用正则表达式验证email地址的方法
Jan 22 Javascript
实现无刷新联动例子汇总
May 20 Javascript
基于jQuery Circlr插件实现产品图片360度旋转
Sep 20 Javascript
js插件dropload上拉下滑加载数据实例解析
Jul 27 Javascript
JS创建对象的写法示例
Nov 04 Javascript
AngularJS路由切换实现方法分析
Mar 17 Javascript
妙用Angularjs实现表格按指定列排序
Jun 23 Javascript
ionic2屏幕适配实现适配手机、平板等设备的示例代码
Aug 11 Javascript
JavaScript实现树的遍历算法示例【广度优先与深度优先】
Oct 26 Javascript
深入理解redux之compose的具体应用
Jan 12 Javascript
实现AJAX异步调用和局部刷新的基本步骤
Mar 17 Javascript
基于jquery的可多选的下拉列表框
Jul 20 #Javascript
基于jquery的DIV随滚动条滚动而滚动的代码
Jul 20 #Javascript
最佳6款用于移动网站开发的jQuery 图片滑块插件小结
Jul 20 #Javascript
基于jquery的图片轮播 tab切换组件
Jul 19 #Javascript
JavaScript面向对象知识串结(读JavaScript高级程序设计(第三版))
Jul 17 #Javascript
JavaScript面向对象(极简主义法minimalist approach)
Jul 17 #Javascript
基于jQuery的获取标签名的代码
Jul 16 #Javascript
You might like
PHP 文章中的远程图片采集到本地的代码
2009/07/30 PHP
php批量上传的实现代码
2013/06/09 PHP
Yii Framework框架获取分类下面的所有子类方法
2014/06/20 PHP
PHP页面实现定时跳转的方法
2014/10/31 PHP
PHP树形结构tree类用法示例
2019/02/01 PHP
javascript URL锚点取值方法
2009/02/25 Javascript
AppBaseJs 类库 网上常用的javascript函数及其他js类库写的
2010/03/04 Javascript
js获取控件位置以及不同浏览器中的差别介绍
2013/08/08 Javascript
基于JQuery的列表拖动排序实现代码
2013/10/01 Javascript
基于jquery实现瀑布流布局
2020/06/28 Javascript
再次谈论React.js实现原生js拖拽效果引起的一系列问题
2016/04/03 Javascript
如何用JS/HTML将时间戳转换为“xx天前”的形式
2017/02/06 Javascript
利用jquery去掉时光轴头尾部线条的方法实例
2017/06/16 jQuery
jQuery实现小火箭返回顶部特效
2020/02/03 jQuery
[02:15]2015国际邀请赛选手档案IG.Ferrari 430
2015/07/30 DOTA
[56:42]完美世界DOTA2联赛循环赛 Matador vs Forest 第二场 11.06
2020/11/06 DOTA
Python网站验证码识别
2016/01/25 Python
Python爬虫实现爬取百度百科词条功能实例
2019/04/05 Python
pandas中遍历dataframe的每一个元素的实现
2019/10/23 Python
python自动结束mysql慢查询会话的实例代码
2019/10/27 Python
Python实现栈和队列的简单操作方法示例
2019/11/29 Python
使用Python paramiko模块利用多线程实现ssh并发执行操作
2019/12/05 Python
python通过安装itchat包实现微信自动回复收到的春节祝福
2020/01/19 Python
python实现滑雪者小游戏
2020/02/22 Python
Python post请求实现代码实例
2020/02/28 Python
python json.dumps中文乱码问题解决
2020/04/01 Python
深入解析HTML5使用SVG图像时的viewBox属性用法
2015/09/02 HTML / CSS
canvas探照灯效果的示例代码
2018/11/30 HTML / CSS
美国著名手表网站:Timepiece
2017/11/15 全球购物
医学生求职信
2014/07/01 职场文书
学校运动会报道稿
2014/09/23 职场文书
毕业横幅标语
2014/10/08 职场文书
2014年电信员工工作总结
2014/12/19 职场文书
Pytorch 使用tensor特定条件判断索引
2021/04/08 Python
解决MultipartFile.transferTo(dest) 报FileNotFoundExcep的问题
2021/07/01 Java/Android
HTML5页面打开微信小程序功能实现
2022/09/23 HTML / CSS