XmlHttpRequest調用Webservice的一點心得


因為項目需要,以后前端、手機客戶端調用ASP.NET的Webservice來獲取信息.所以這段時間開始看Webservice,試着通過XmlHttpRequest調用Webservice,過程中碰到不少問題,也有不少的收獲。

首先,因為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格式,將每個控件值編碼為名稱=值對發送出去。

這種情況下的頁面地址后面也需要加上/方法名


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM