熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> .NET編程 >> 正文

ASP.NET 3.5核心編程之AJAX Web Service的調用

2013-11-13 10:29:10  來源: .NET編程 
    AJAX模型基於兩個層次客戶端應用程序層和服務器應用程序層在這種模型下客戶端層向服務器層發送請求而服務器層向客戶端層返回響應服務器端點通過URL標識並通過源(feed)(通常為JSON[JavaScript Object Notation]數據流)向客戶端暴露數據服務器層只是一個接收調用並將其轉發給應用程序業務邏輯層的外觀
 下圖描繪了整個模型

  

 
    為使ASPNET AJAX頁面能夠調用遠程服務該服務必須滿足幾點要求其中最關鍵的一點與端點和底層平台的位置有關支持AJAX的服務必須位於調用者所處的域中這意味著該服務必須是ASPNET XML Web服務(asmx端點)必須以ASPNET應用程序的形式寄放於同一Web服務器的某個IIS應用程序中
    總的來說對於ASPNET AJAX應用服務種定義服務器層服務的方式
    帶有asmx端點的ASPNET XML Web服務
    帶有svc端點的WCF服務
    帶有aspx端點的頁面方法這些方法定義在與主調頁面相同的頁面中
    服務(Service)這個詞往往被誤用在AJAX中服務指的是隸屬於應用程序的代碼(位於應用程序的域中)用於向客戶端暴露相應的功能從根本上講AJAX應用程序使用的服務一般不通過簡單對象訪問協議(SOAP)進行通信(而是使用JSON)不必是面向服務架構(SOA)中自治的服務它們與自身所處平台和域綁定因此不能稱這裡的服務為WS*Web服務和SOA服務
REST服務
    針對AJAX應用程序的服務圍繞著暴露給Web客戶端數據和資源二者可通過HTTP獲取要求客戶端通過URL(也可以有HTTP標頭)來訪問數據和命令操作客戶端與服務的交互是通過GETPOSTPUT和DELETE這樣的動作來完成換言之URL用於描述所要獲取的資源HTTP動作用於描述對資源執行的操作這類交互過程中交換的數據由簡單的格式表示甚至可以用聯合格式(如RSS和ATOM)表示
    具有這些特性的服務為具象狀態傳輸(Representational State TransferREST)


   數據的序列化
    AJAX調用包含作為參數傳給被調服務方法的數據及作為輸出返回的數據這些數據是如何序列化的?
    通信雙方都能理解的序列化格式為JavaScript對象表示法(JSON)JSON是一種基於文本的格式專門用於在不同層次間傳遞對象的狀態JavaScript支持JSON可通過JavaScript的eval函數將JSON兼容的字符串轉換為JavaScript對象然而如果JavaScript字符串代表自定義對象的狀態那麼開發者應確保具有相應類的定義
    ASPNET AJAX網絡堆棧要負責為每個遠程傳遞的對象創建JSON字符串在服務器端通過專門的格式化程序類接收數據並通過NET反射來填充與之匹配的托管類在返回時NET托管類會被序列化為JSON字符串並發送給客戶端腳本管理器會確保引用這些JSON字符串的類(Web服務代理類)存在於客戶端
    下面給出一個描述對象狀態的JSON格式的示例
    {ID:ALFKI Company:Alfred Futterkiste}
    這個字符串說明該對象有兩個屬性ID和Company存儲的是以字符串的形式序列化的值如果某個屬性被賦予一個非基本類型的值(如自定義對象)那麼該值會以遞歸方式序列化為JSON[
   JSON與XML
    相比XMLJSON更精煉更適合JavaScript語言
    應用程序特定的Web服務
    在默認情況下ASPNET Web服務收發的是SOAP數據包(而不是JSON數據包)通過Web服務描述語言(Web Services Description LanguageWSDL)文檔來暴露其協定AJAX應用程序上下文中的ASPNET XML Web服務是如何工作的呢?
    可以通過ASPNET AJAX應用程序的nfig文件來修改接收asmx請求的HTTP處理程序將這些調用重定向給能夠理解JSON流的HTTP處理程序這意味著ASPNET XML Web服務可以是一種雙重的服務即可接受和處理SOAP請求也可針對JSON請求在配置層我們可以禁用SOAP支持並隱藏用於對外公開該服務功能的WSDL文件
    如果要使用支持JSON的ASPNET Web服務則需要刪除XML因為在調用ASPNET Web服務時我們不處理SOAP和XML針對AJAX應用程序的ASPNET Web服務不采用SOAP消息
    遠程編程接口的定義
    協定(contract)用於定義服務器端端點暴露給調用者的內容如果希望以ASPNET Web服務的形式實現則不嚴格要求存在實際的協定但如果ASPNET 中的WCF服務那麼協定就必須存在總而言之以接口形式設計的公共API會使代碼更整潔在實現該接口的類創建完畢後有關服務器API接口的工作就結束了這樣我們就可以發布這個遠程API並使ASPNET AJAX運行庫來管理來自客戶端的調用
    對於ASPNET Web服務我們通過純粹的接口來定義協定使該接口包含與服務器API有關的方法和屬性下面給出一個簡單的服務
    using System;public interface ITimeService{
    DateTime GetTime()
    string GetTimeFormat(string format)}
    這兩個方法構成了可以在客戶端調用的服務器API
  實現已約定的接口
    ASPNET Web服務通常通過派生自基類WebService的NET類來實現
    using SystemWebServices;public class TimeService : WebService ITimeService{
    …}
    注意沒有必要一定要從基類WebService派生這個基類主要用於直接訪問一些常用的ASPNET對象(如Application和Session)如果不需要直接訪問這些ASPNET內部的對象即使不從WebService類派生也能創建ASPNET Web服務這種情況下我們可通過HttpContext對象來間接地使用ASPNET內部的對象


   協定的發布
    從本質來說發布給定的服務器協定就是生成一個嵌在頁面中的腳本能夠調用的JavaScript代理類如果服務器API通過Web服務實現我們要向ASPNET AJAX頁面的腳本管理器注冊該Web服務此外我們還要在nfig文件中添加一個特殊的asmx請求HTTP處理程序
    Web服務的遠程調用
    Web服務提供了服務器端代碼的宿主環境以便在響應客戶端的操作時進行調用服務中的Web方法指向應用程序特定的代碼
    AJAX Web服務的創建
    為ASPNET AJAX應用程序定制的Web服務比其他ASPNET Web服務要小ASPNET AJAX Web服務與傳統的ASPNET XML Web服務間存在兩方面的差異
    首先若使用ASPNET AJAX Web服務那麼為滿足特定應用程序的需要我們要設計ASPNET AJAX Web服務的協定而不是配置公共服務的行為目標應用程序就是Web服務的宿主其次我們必須使用一個新的特性(attribute)來聲明這種Web服務的類而在常規的ASPNET XML Web服務中這是不允許的
    最終的效果是ASPNET AJAX Web服務可能有兩套公共接口一套是基於JSON對接口由宿主ASPNET AJAX應用程序使用另一套是基於SOAP的接口暴露給客戶端任何平台都能訪問該服務的URL
    ScriptService特性(attribute)
    為創建ASPNET AJAX Web服務第一步是要建立標准的ASPNET Web服務項目隨後導入SystemWebScriptServices命名空間
    using SystemWebScriptServices;namespace CoreWebService{
    [WebService(Namespace=)]
    [WebServiceBinding(ConformsTo=WsiProfilesBasicProfile_)]
    [ScriptService]
    public class TimeService : SystemWebServicesWebService ITimeService
    {
    …
    }}
    ScriptService特性是使ASPNET XML Web服務與ASPNET AJAX Web服務間產生差異的關鍵該特性指出該服務旨在接受來自基於JavaScript客戶端代理的調用


阻塞SOAP客戶端
    一旦創建AJAX Web服務便可以ASMX資源的形式發布它默認情況下它會有公共的URL能夠由AJAX客戶端調用同時也能被SOAP客戶端和工具發現和使用但我們可禁用SOAP客戶端和工具為此只需在nfig文件中添加以下設置
    <webSevices>
    <protocols>
    <clear />
    </protocols></webServices>
    這段簡單的設置能禁用ASPNET Web服務定義的所有協議(包括SOAP)使該服務只能響應JSON請求
    注意如果添加這些設置則不能夠通過浏覽器的地址欄來調用Web服務以便進行簡單地測試類似地我們也不能為URL添加?wsdl後綴來調用WSDL
    Web服務方法的定義
    客戶端頁面能夠調用Web服務類中帶有WebMethod特性的公共方法在默認情況下這些方法要通過HTTP動作POST來調用以JSON對象的形式返回其值我們可通過一個可選特性ScriptMethod來更改單個方法的默認設置
    ScriptMethod特性帶有個屬性見下表
 
    由於涉及安全性和性能問題因謹慎使用ScriptMethod特性下面的代碼使用了該特性但未修改默認設置
    [WebMethod][ScriptMethod]public DateTime GetTime(){
    …}
    WebMethod特性是必選的而ScriptMethod特性是可選的
AJAX Web服務的注冊
    為在客戶端發起對ASPNET Web服務的調用我們只需要XMLHttpRequest目標Web服務的URL和JSON流的管理功能為方便起見所有功能都包裝在映射到遠程編程接口的JavaScript代理類中該代理類會由ASPNET AJAX框架自動生成並注入到客戶端
    為使內建的引擎生成所需的JavaScript代理和輔助類我們應在需要AJAX Web服務的頁面中向腳本管理器控件注冊該Web服務
    <asp:ScriptManager ID=ScriptManager runat=server>
    <Services>
    <asp:ServiceReference Path=~/WebServices/TimeServiceasmx />
    </Services></asp:ScriptManager>
    對於每個要綁定到頁面的Web服務我們添加一個ServiceReference標簽將Path屬性設為對應asmx資源的URL對於每個服務引用都會在客戶端自動生成一個額外的<script>塊該腳本的URL指向一個系統HTTP處理程序在內部調用以下URL:
    ~/WebServices/TimeServiceasmx/js
    追加到Web服務URL的/js後綴指示ASPNET AJAX運行庫為指定的Web服務生成JavaScript代理類如果頁面處於調試模板該後綴會被改為/jsdebug
    默認情況下JavaScript代理通過<script>標簽連接到頁面這樣就需要單獨下載通過將ServiceReference對象InlineScript屬性設置為true我們還可以將任何所需的腳本並入當前頁面如果啟用浏覽器緩存且多個Web頁面使用相同的服務引用那麼默認值false更合適在這種情況下不論多少頁面需要這個代理類都只需執行一次請求將InlineScript屬性設為true會降低網絡請求數但會多占用一定的帶寬
    如果以編程方式注冊AJAX Web服務我們使用類似以下的代碼
    ServiceReference service = new ServiceReference()servicePath = ~/WebServices/TimeServiceasmx;ScriptManagerServicesAdd(service)
    不論采用哪種方式為調用Web服務我們只需通過JavaScript代理類發起調用即可
    使用ASPNET應用程序來承托AJAX Web服務
    為啟用ASPNET AJAX應用程序中的Web服務調用我們需要在nfig文件中添加以下內容以注冊一個特殊的asmx請求HTTP處理程序
    <httpHandlers>
    <remove verb=* path=*asmx />
    <add verb=* path=*asmx
    type=SystemWebScriptServicesScriptHandlerFactory />
    …</httpHandlers>
    該設置已包含在VS為支持AJAX的Web項目而創建的nfig文件中
    處理程序工廠(SystemWebScriptServicesScriptHandlerFactory類)會選擇負責處理給定類型請求的HTTP處理程序且能通過Web服務調用中的腳本來識別JSON調用基於JSON的請求由特殊的HTTP處理程序處理而常規的SOAP調用會穿越ASPNET管道
    AJAX Web服務的調用
    被引用的ASPNET AJAX Web服務暴露給JavaScript代碼的類名與服務器類名相同代理類采用單例模式暴露了外界調用的靜態方法無需實例化


    JavaScript代理類
    以上述的timeserviceasmx生成的JavaScript代理類為例讓我們看看它的代碼
    TyperegisterNamespace(CoreWebServicesCoreWebServicesTimeService = function(){
    CoreWebServicesTimeServiceinitializeBase(this)
    this_timeout = ;
    this_userContext = null;
    this_succceeded = null;
    this_failed = null;}CoreWebServicesTimeServiceprototype ={
    //調用GetTime方法
    GetTime : function(succeededCallback failedCallback userContext)
    {
    //invoke參數分別為
    //Web Service URL路徑
    //Web Service方法名稱
    //
    //傳入方法的參數數組
    //執行成功回調函數
    //執行失敗回調函數
    //調用上下文對象
    return this_invoke(CoreWebServicesTimeServiceget_path()
    GetTime
    false
    {}
    succeededCallback
    failedCallback
    userContext)
    }
    GetTimeFormat : function(timeFormat succeededCallback failedCallback userContext)
    {
    return this_invoke(CoreWebServicesTimeServiceget_path()
    GetTimeAsFormat
    false
    {format:timeFormat}
    succeededCallback
    failedCallback
    userContext)
    }}//注冊CoreWebServicesTimeService類該類繼承於SysNetWebServiceProxyCoreWebServicesTimeServiceregisterClass(CoreWebServicesTimeService
    SysNetWebServiceProxy)//創建一個JavaScript代理類實例CoreWebServiceTimeService_staticInstance = new CoreWebServicesTimeService()
    在JavaScript中調用WebService方法其實是通過最後創建的JavaScript代理類實現的
    CoreWebServiceTimeServiceGetTime = function(onSuccess onFailed userContext){
    CoreWebServiceTimeService_staticInstanceGetTime(onSuccess onFailed userContext)}CoreWebServiceTimeServiceGetTimeFormat = function(onSuccess onFailed userContext){
    CoreWebServiceTimeService_staticInstanceGetTimeFormat(onSuccess onFailed userContext)}
    在這個代理類的定義中帶有幾個公共屬性
 
    path屬性用於定義Web服務的URL我們可以編程方式更改該屬性值以便將代理重定向到其他URL
    遠程調用的執行
    下面是將JavaScript代理與客戶端按鈕點擊關聯的典型方法
    <input type=button value=Get Time onclick=getTime() />
    按鈕最好是客戶端按鈕但也可以是服務器端Button對象生成的提交按鈕只要將OnClientClick屬性設置為false的JavaScript代碼即可這會避免它執行默認的回發操作
    <asp:Button ID=Button runat=server Text=Button OnClientClick=getTime() return false; />
    getTime函數用於采集必要的輸入數據並調用代理類中的靜態方法如果希望為回調或用戶上下文對象賦予默認值那麼最好在pageLoad函數中進行因為pageLoad函數會在客戶端頁面ASPNET AJAX成功初始化後調用該函數比浏覽器的onload事件更可靠示例代碼如下
    <script language=javascript type=text/javascript>
    function pageLoad()
    {
    //設置默認的調用失敗回調函數
    CoreWebServicesTimeServiceset_defaultFailedCallback(methodFailed)
    }
    function getTime()
    {
    CoreWebServicesTimeServiceGetTimeFormat(ddd dd MMMM yyyy [hh:mm:ss] methodComplete)
    }
    function methodComplete(results context methodName)
    {
    $get(LabelinnerHTML = results;
    }
    function methodFailed(errorInfo context methodName)
    {
    $get(LabelinnerHTML = StringFormat(Execution of method {} failed because of the following:\r\n{}
    methodName errorInfoget_message())
    }</script>


    由於Web服務調用是以異步方式處理的因而我們需要回調來處理調用成功和失敗這兩種情況這兩個回調的簽名類似
    function method(results context methodName)
    下表對各參數做了簡要說明
 
    錯誤處理
    failed回調會在服務器上的遠程方法執行期間發生異常時被調用在這種情況下HTTP響應會包含HTTP錯誤碼(內部錯誤)
    在客戶端服務器異常通過JavaScript中的Error對象暴露該對象會基於從服務器端獲得的消息和堆棧跟蹤而動態創建Error對象會通過results參數暴露給failed回調我們可通過Error對象的message和stackTrace屬性來分別讀取收到的消息和堆棧跟蹤
    如果我們未指派默認的錯誤回調函數ASPNET AJAX會調用自己的默認回調函數該回調函數會彈出一個帶有服務器異常消息的消息框
    為用戶提供反饋
    雖然UpdatePanel中提供了異步調用的反饋機制(如UpdateProgress控件)但對於傳統的遠程方法調用我們只能自行編寫代碼實現對用戶的反饋
    我們可以在遠程方法調用執行前顯示等待消息GIF動畫或其他內容
    function takeaWhile(){
    //顯示等待消息
    $get(FeedbackinnerHTML = Please wait …;
    CoreWebServicesMySampleServiceVeryLengthyTask(methodCompletedWithFeedback methodFailedWithFeedback)}
    在completed回調中我們首先重置用戶界面然後再進行其他操作
    function methodCompletedWithFeedback(results context methodName){
    $get(FeedbackinnerHTML = ;
    …}
    注意在發生錯誤時我們也要清除用戶界面
    超時處理
    如果發起對asmx Web服務的客戶端調用則是對asmx的直接調用對於該請求ASPNET運行庫中只有同步處理程序也就是說不論客戶端如何檢測當前調用是否正在進行ASPNET線程都會被完全阻塞直到AJAX方法執行完畢為此我們可以設置超時時間
    CoreWebServicesMySampleServiceset_timeout(
    timeout屬性是全局的作用於代理類的所有方法
    如果請求超時我們便不會從服務器收到響應客戶端只能單方面的撤銷執行


From:http://tw.wingwit.com/Article/program/net/201311/13787.html
  • 上一篇文章:

  • 下一篇文章:
  • 推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.