首先,因为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时,警告窗口的内容变就变成了hello world。
这时候虽然参数“{}”还是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调试页面的示例,将参数改为:
复制代码 代码如下:
krime
这样应该就能正常返回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
string
上面post的地址后面并没有像请求json数据时一样加上/方法名,而soapaction的方法名前面还需要加上命名空间。于是修改xmlhttprequest的请求头,url和action做相应修改,结果终于正常返回了xml的结果:hello world&hello, krime!
后来继续测试,发现请求内容类型为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 = ''
+ ''
+ ''
+ ''
+ 'krime'
+ ''
+ ''
+ '';
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格式,将每个控件值编码为名称=值对发送出去。
这种情况下的页面地址后面也需要加上/方法名。