分享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 相关文章推荐
ExtJS Window 最小化的一种方法
Nov 18 Javascript
JQuery 文本框使用小结
May 22 Javascript
AngularJS实现标签页的两种方式
Sep 05 Javascript
Bootstrap3 模态框使用实例
Feb 22 Javascript
深入浅析JS中的严格模式
Jun 04 Javascript
详解如何在vue-cli中使用vuex
Aug 07 Javascript
VUE 实现复制内容到剪贴板的两种方法
Apr 24 Javascript
countup.js实现数字动态叠加效果
Oct 17 Javascript
node.js中path路径模块的使用方法实例分析
Feb 13 Javascript
JS实现判断移动端PC端功能
Feb 21 Javascript
使用element-ui +Vue 解决 table 里包含表单验证的问题
Jul 17 Javascript
Vue 解决通过this.$refs来获取DOM或者组件报错问题
Jul 28 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 字符串替换的方法
2012/01/10 PHP
PHP运行SVN命令显示某用户的文件更新记录的代码
2014/01/03 PHP
PHP判断远程图片是否存在的几种方法
2014/05/04 PHP
escape、encodeURI、encodeURIComponent等方法的区别比较
2006/12/27 Javascript
javascript实现动态模态绑定grid过程代码
2014/09/22 Javascript
jQuery Html控件基本操作(日常收集整理)
2016/03/11 Javascript
vue.js入门教程之基础语法小结
2016/09/01 Javascript
AngularJS入门教程之多视图切换用法示例
2016/11/02 Javascript
使用纯JS代码判断字符串中有多少汉字的实现方法(超简单实用)
2016/11/12 Javascript
使用UrlConnection实现后台模拟http请求的简单实例
2017/01/04 Javascript
JavaScript实现左右下拉框动态增删示例
2017/03/09 Javascript
javascript中this用法实例详解
2017/04/06 Javascript
js模拟百度模糊搜索的实例
2017/08/04 Javascript
JS实现问卷星自动填问卷脚本并在两秒自动提交功能
2020/06/17 Javascript
nodejs项目windows下开机自启动的方法
2017/11/22 NodeJs
详解react、redux、react-redux之间的关系
2018/04/11 Javascript
详解使用create-react-app快速构建React开发环境
2018/05/16 Javascript
JavaScript数组基于交换的排序示例【冒泡排序】
2018/07/21 Javascript
Angular 多模块项目构建过程
2020/02/13 Javascript
jquery css实现流程进度条
2020/03/26 jQuery
vue+element实现动态加载表单
2020/12/13 Vue.js
微信小程序组件生命周期的踩坑记录
2021/03/03 Javascript
Python实现简单的代理服务器
2015/07/25 Python
Python中使用装饰器来优化尾递归的示例
2016/06/18 Python
python中关于for循环的碎碎念
2017/06/30 Python
Python实现的网页截图功能【PyQt4与selenium组件】
2018/07/12 Python
解决python gdal投影坐标系转换的问题
2020/01/17 Python
python编程进阶之异常处理用法实例分析
2020/02/21 Python
Python日志logging模块功能与用法详解
2020/04/09 Python
详解Python中openpyxl模块基本用法
2021/02/23 Python
世界领先的26岁以下学生和青少年旅行预订网站:StudentUniverse
2018/07/01 全球购物
美国庭院家具购物网站:AlphaMarts
2019/04/10 全球购物
在校生钳工实习自我鉴定
2013/09/19 职场文书
优秀工作者事迹材料
2014/12/26 职场文书
出国留学英文自荐信
2015/03/25 职场文书
2015年全国爱眼日活动方案
2015/05/05 职场文书