[翻譯]ASP.NET AJAX調用Web Service(超級好)

原文發佈日期:2007.02.08
作者:Bipin Joshi
翻譯:webabcd


介紹
盡 管AJAX是種客戶端技術,但實際上的開發過程,它經常要調用一個服務器端的過程。通常,網站上的數據是存放在一個關係型數據庫中,爲了讓AJAX更有用 處,處理服務器端數據需要一種簡單可靠的方法。幸運的是,ASP.NET AJAX提供了一種有效的基礎架構來做這件事情,瀏覽器和服務器在Internet上可以進行AJAX通信。自然而然,Web Service在數據傳輸和客戶端/服務器之間的一般通信方面可以扮演一個重要角色。本文就演示瞭如果通過ASP.NET AJAX調用ASP.NET web services。


軟件需求
本文所有的範例都是使用ASP.NET AJAX RC版,而且,要在SQL Server 2005 (Express版即可)上有一個Northwind數據庫。範例使用Visual Studio 2005作爲開發環境。


範例場景
範例開發了一個Web頁面,用於輸入Northwind數據庫職員表中的職員數據。頁面通過ASP.NET AJAX功能,調用一個Web Service來完成職員表中的數據增、刪、改、查。


創建一個Web Service
作爲開始,使用Visual Studio 2005創建一個新的Web站點,注意把ASP.NET AJAX項目模板添加到新站點對話框,這個對話框包括一個"ASP.NET AJAX Enabled Web Site" 模板。

圖1:新站點創建模板

使用"ASP.NET AJAX Enabled Web Site" 模板創建的新站點和用普通方法創建的站點區別如下:
    ·它的Web.config自動包括許多ASP.NET AJAX專用的配置信息。
    ·System.Web.Extensions程序集被添加到引用中。

當然,我們可以更改一個普通的Web站點,以使之符合AJAX要求,但模板可以大大簡化我們的工作。

現在我們創建了一個新的Web站點,添加一個新的web service並命名爲EmployeeService.asmx,EmployeeService將包括5個Web方法
Method Name Description
GetEmployees() 返回Employees表裏的僱員列表。 這個列表是一個Employee對象數組
GetEmployee() 接收EmployeeID參數返回Employee對象的詳細信息
Insert() 給Employees表裏增加一個新的僱員信息
Update() 更新Employees表裏的某個僱員信息
Delete() 刪除Employees表裏的某個僱員信息
表1:EmployeeService中的Web方法

GetEmployees() 和 GetEmployee()方法以Employee對象的形式返回數據,因此,首先創建一個Employee類。右鍵單擊App_Code文件夾,選擇 “添加新項…”,添加一個叫Employee的類,下面顯示Employee類的全部代碼:
public class Employee
{
   
private int intEmployeeID;
   
private string strFirstName;
   
private string strLastName;
   
public int EmployeeID
   
{
      
get
      
{
         
return intEmployeeID;
      }

      
set
      
{
         intEmployeeID 
= value;
      }

   }

   
public string FirstName
   
{
      
get
      
{
         
return strFirstName;
      }

      
set
      
{
         strFirstName 
= value;
      }

   }

   
public string LastName
   
{
      
get
      
{
         
return strLastName;
      }

      
set
      
{
         strLastName 
= value;
      }

   }

}

Employee類申明三個Private變量來分別存放employee ID, first name和 last name,三個變量再封裝在三個public屬性中:EmployeeID, FirstName和LastName。

打開 web.config文件,添加<connectionStrings>部分如下:
<connectionStrings>
   
<add name="connstr" connectionString=
        "data source=./sqlexpress;
        initial catalog=northwind;
        integrated security=true"
/>
</connectionStrings>

這部分存放數據庫鏈接字符串,用於指向Northwind數據庫,確保修改SqlServer名稱、IP地址以及驗證方式以和我們的開發環境相符。

現在,打開EmployeeService.cs添加如下代碼:
private string strConn =   "";
public EmployeeService()
{
   strConn 
= ConfigurationManager.ConnectionStrings["connstr"].
             ConnectionString;
}

代碼使用了ConfigurationManager類來讀取配置文件中的數據庫鏈接字符串,並存放在一個類級別的變量strConn中,這個變量將被下面的所有Web Method所使用。

現在,添加GetEmployees() web method:
[WebMethod]
public Employee[] GetEmployees()
{
   SqlConnection cnn 
= new SqlConnection(strConn);
   cnn.Open();
   SqlCommand cmd            
= new SqlCommand();
   cmd.Connection            
= cnn;
   cmd.CommandText           
= "select employeeid,firstname,
                                lastname from employees";
   SqlDataReader reader      = cmd.ExecuteReader();
   List
<Employee> list = new List<Employee>();
   
while (reader.Read())
   
{
      Employee emp   
= new Employee();
      emp.EmployeeID 
= reader.GetInt32(0);
      emp.FirstName  
= reader.GetString(1);
      emp.LastName   
= reader.GetString(2);
      list.Add(emp);
   }

   reader.Close();
   cnn.Close();
   
return list.ToArray();
}

代碼創建了SqlConnection and SqlCommand 對象,然後執行SELECT查詢,以獲取Employees表中EmployeeID, FirstName 和LastName字段。結果通過SqlDataReader返回。然後,創建一個generic-based Employee數組,通過While循環,給每個Employee實例的屬性賦值。當While循環完畢的時候,關閉SqlDataReader 和 SqlConnection。GetEmployees()方法返回的類型是Employee數組。因此,generic List使用List類中的ToArray()方法來轉換成Employee數組。

現在,添加一個GetEmployee() web method如下:
[WebMethod]
public Employee GetEmployee(int pEmployeeId)
{
   SqlConnection cnn 
= new SqlConnection(strConn);
   cnn.Open();
   SqlCommand cmd       
= new SqlCommand();
   cmd.Connection       
= cnn;
   cmd.CommandText      
= "select employeeid,firstname,lastname
                           from employees where employeeid=@id";
   SqlParameter id      = new SqlParameter("@id", pEmployeeId);
   cmd.Parameters.Add(id);
   SqlDataReader reader 
= cmd.ExecuteReader();
   Employee emp         
= new Employee();
   
while (reader.Read())
   
{
      emp.EmployeeID 
= reader.GetInt32(0);
      emp.FirstName  
= reader.GetString(1);
      emp.LastName   
= reader.GetString(2);
   }

   reader.Close();
   cnn.Close();
   
return emp;
}

GetEmployee() web method接受一個employee ID參數作爲輸入,代碼和前面的非常相似,但這次只返回一個employee。注意,使用SqlParameter來定義傳入的EmployeeID。

現 在,再添加Insert()、Update()和 Delete()web methods,其中,Insert() web method 以要添加的Employee的 first name 和 last name 作爲參數,Update() web method 以要更新的employee ID 以及新的first name 和 last name作爲參數,並執行UPDATE語句, Delete() web method 以要刪除的employee ID 作爲參數,然後執行DELETE 語句
[WebMethod]
public int Insert(string pFirstName, string pLastName)
{
   SqlConnection cnn  
= new SqlConnection(strConn);
   cnn.Open();
   SqlCommand cmd     
= new SqlCommand();
   cmd.Connection     
= cnn;
   cmd.CommandText    
= "insert into employees(firstname,lastname)
                         values (@fname,@lname)";
   SqlParameter fname = new SqlParameter("@fname", pFirstName);
   SqlParameter lname 
= new SqlParameter("@lname", pLastName);
   cmd.Parameters.Add(fname);
   cmd.Parameters.Add(lname);
   
int i = cmd.ExecuteNonQuery();
   cnn.Close();
   
return i;
}

[WebMethod]
public int Update(int pEmployeeId,string pFirstName, string pLastName)
{
   SqlConnection cnn  
= new SqlConnection(strConn);
   cnn.Open();
   SqlCommand cmd     
= new SqlCommand();
   cmd.Connection     
= cnn;
   cmd.CommandText    
= "update employees set firstname=@fname,
                         lastname=@lname where employeeid=@id";
   SqlParameter fname = new SqlParameter("@fname", pFirstName);
   SqlParameter lname 
= new SqlParameter("@lname", pLastName);
   SqlParameter id 
= new SqlParameter("@id", pEmployeeId);
   cmd.Parameters.Add(fname);
   cmd.Parameters.Add(lname);
   cmd.Parameters.Add(id);
   
int i = cmd.ExecuteNonQuery();
   cnn.Close();
   
return i;
}

[WebMethod]
public int Delete(int pEmployeeId)
{
   SqlConnection cnn 
= new SqlConnection(strConn);
   cnn.Open();
   SqlCommand cmd  
= new SqlCommand();
   cmd.Connection  
= cnn;
   cmd.CommandText 
= "delete from employees where employeeid=@id";
   SqlParameter id 
= new SqlParameter("@id", pEmployeeId);
   cmd.Parameters.Add(id);
   
int i = cmd.ExecuteNonQuery();
   cnn.Close();
   
return i;
}

這就完成了web service的創建。到目前爲止,還沒有做任何和AJAX特性相關的任何工作,現在,時機已經成熟,我們通過下面的代碼更改web service類的定義:
using System.Web.Script.Services;


[WebService(Namespace 
= "http://tempuri.org/")]
[WebServiceBinding(ConformsTo 
= WsiProfiles.BasicProfile1_1)]
[ScriptService]
public class EmployeeService : System.Web.Services.WebService
{
   
   

注意特地標明的黑體字,我們導入了System.Web.Script.Services命名空間,這個命名空間來自 System.Web.Extensions程序集,這個命名空間提供了[ScriptService]屬性,這將使web service可以被來自客戶端的JavaScript (如ASP.NET AJAX)調用。

好了,我們開始準備從ASP.NET AJAX調用Web Service了!


如何調用Web Service
這 部分,我們將創建一個Web頁面作爲數據輸入,通過調用剛剛創建的Web Service來操作Employees表。作爲開始,我們先添加一個EmployeeServiceClient.aspx頁面,打開工具箱,選擇 View > Toolbox菜單,在工具箱上,選中AJAX Extensions這樣的節點(見圖2)

圖 2: 增加模板後的新站點創建對話框

AJAX Extensions部分顯示一個Web頁面上所有可以使用的ASP.NET AJAX組件。所有使用ASP.NET AJAX的頁面都需要一個ScriptManager組件。打開ScriptManager屬性窗口,定位Services屬性,打開Service引用 編輯器,如圖3:

圖 3: Service 引用編輯器

點擊對話框底部的Add按鈕,設置Path屬性以指向Web Service(EmployeeService.asmx)的虛擬路徑,下面的標記將會產生在Web頁面文件中:
<asp:ScriptManager ID="ScriptManager1" runat="server" >
   
<Services>
      
<asp:ServiceReference Path="EmployeeService.asmx" />
   
</Services>
</asp:ScriptManager>

對每個Web Service調用,都需要在<asp:ScriptManager>部分添加一個<asp:ServiceReference>元素,此標記把要使用的web service註冊到當前web form上。

圖 4: 設計頁面表單

表 單包括一個下拉框(<SELECT>) ,用於顯示所有的employee IDs,一旦選中其中一個employee ID,employee的詳細信息將顯示在2個文本框中,然後可以更新這些信息。如果要添加一個employee,只需要輸入first name 和 last name,然後點擊“插入”按鈕就可以了。同理,如果要刪除一個employee,選擇下拉框中的employee ID,點擊“刪除”按鈕。在INSERT、UPDATE或者 DELETE操作完成後,將會顯示成功或者失敗的信息。下面是所有的頁面代碼:
<table>
   
<tr>
      
<td colspan="2">
         
<asp:Label ID="Label4" runat="server" Font-Size="X-Large"
                    Text
="Employee Management">
         
</asp:Label></td>
   
</tr>
   
<tr>
      
<td style="width: 100px">
         
<asp:Label ID="Label1" runat="server"
                    Text
="Employee ID :"></asp:Label></td>
      
<td style="width: 100px">
         
<select id="Select1" >
         
</select>
      
</td>
   
</tr>
   
<tr>
      
<td style="width: 100px">
         
<asp:Label ID="Label2" runat="server"
                    Text
="First Name :"></asp:Label></td>
      
<td style="width: 100px">
         
<input id="Text1" type="text" /></td>
   
</tr>
   
<tr>
      
<td style="width: 100px">
         
<asp:Label ID="Label3" runat="server"
                    Text
="Last Name :"></asp:Label></td>
      
<td style="width: 100px">
         
<input id="Text2" type="text" /></td>
   
</tr>
   
<tr>
      
<td align="center" colspan="2">
         
<input id="Button3" type="button" value="Insert" />
         
<input id="Button4" type="button" value="Update" />
         
<input id="Button5" type="button" value="Delete" />
      
</td>
   
</tr>
   
<tr>
      
<td align="center" colspan="2">
         
<span id="lblMsg" style="font-weight: bold;
               color: red;"
></span>
      
</td>
   
</tr>
</table>

注意:我們沒有使用ASP.NET服務器端控件,如DropDownList、 TextBox 以及 Button。取而代之的是,我們用的傳統的HTML控件,如:<SELECT> 以及 <INPUT>。這因爲我們要想通過客戶端JavaScript調用web service,而不是通過服務端代碼。同理,注意底下的<SPAN>標記,這是用來顯示成功或者失敗的信息的。

下一步,在<head>元素內增加一個<script>部分,添加一個CallWebMethod()的函數:
function CallWebMethod(methodType)
{
   
switch(methodType)
   
{
      
case "select":
         EmployeeService.GetEmployees(FillEmployeeList,ErrorHandler,
                                      TimeOutHandler);
         
break;
      
case "selectone":
         var select
=document.getElementById("Select1");
         var empid
=select.options[select.selectedIndex].value;
         EmployeeService.GetEmployee(empid,DisplayEmployeeDetails,
                                     ErrorHandler,TimeOutHandler);
         
break;
      
case "insert":
         var text1
=document.getElementById("Text1");
         var text2
=document.getElementById("Text2");
         EmployeeService.Insert(text1.value,text2.value,
                                InsertEmployee,ErrorHandler,
                                TimeOutHandler);
         
break;
      
case "update":
         var select
=document.getElementById("Select1");
         var empid
=select.options[select.selectedIndex].value;
         var text1
=document.getElementById("Text1");
         var text2
=document.getElementById("Text2");
         var emp
=new Employee();
         emp.EmployeeID
=empid;
         emp.FirstName
=text1.value;
         emp.LastName
=text2.value;
         EmployeeService.Update(empid,text1.value,text2.value,
                                UpdateEmployee,ErrorHandler,
                                TimeOutHandler);
         
break;
      
case "delete":
         var select
=document.getElementById("Select1");
         var empid
=select.options[select.selectedIndex].value;
         EmployeeService.Delete(empid,DeleteEmployee,ErrorHandler,
                                TimeOutHandler);
         
break;
   }

}

CallWebMethod() 函數 就是用來調用web service的中央控制函數。 這個函數接收一個字符串參數用來標識調用的方法,它包括一個switch 語句來判斷調用的方法,每個 case 塊 調用一個web method。 注意web method 是如何被調用的:ASP.NET AJAX 框架自動創建一個JavaScript 代理類,這個代理類和要調用的web service有相同的名稱。因此,上面代碼中 EmployeeService 並不是真正的 類,而是一個JavaScript 代理類。 代理類包括原始web service中所有的Web Method。除了原來的web method 所包括的參數外,每個方法還包括3個額外的參數。

第 一個參數是一個JavaScript 函數,用於當web method 成功完成時調用的。記住:所有客戶端和服務器端的AJAX 通信都是異步的,因此,這個函數用來捕獲web method 的返回值。第二個參數是用於發生錯誤的情況下調用的JavaScript 函數。最後,第三個參數是當調用Web Service 發生超時的情況下調用的JavaScript 函數。

第一種情況,case ("select"),就是簡單的調用GetEmployees() 方法;第二種情況,case ("selectone"),調用GetEmployee()方法,通過傳統的JavaScript 代碼獲取下拉框中的employee ID;同理,第三、第四、第五個Case依次調用Insert()、 Update()和 Delete()方法。

上面的代碼通過5個 JavaScript函數實現相應的web method 成功調用:FillEmployeeList(), DisplayEmployeeDetails(), InsertEmployee(), UpdateEmployee()以及 DeleteEmployee()。每個函數接收一個參數作爲web method相應的返回值。
function FillEmployeeList(result)
{
   
var select=document.getElementById("Select1");
   
for(var i=0;i<result.length;i++)
   
{
      
var option=new Option(result[i].EmployeeID,
                            result[i].EmployeeID);
      select.options.add(option);
   }

}

function DisplayEmployeeDetails(result)
{
   
var text1=document.getElementById("Text1");
   
var text2=document.getElementById("Text2");
   text1.innerText
=result.FirstName;
   text2.innerText
=result.LastName;
   
var lblMsg=document.getElementById("lblMsg");
   lblMsg.innerText
="";
}

function InsertEmployee(result)
{
   
if(result>0)
   
{
      
var lblMsg=document.getElementById("lblMsg");
      lblMsg.innerText
="Employee added successfully!";
   }

   
else
   
{
      
var lblMsg=document.getElementById("lblMsg");
      lblMsg.innerText
="Error occurred while adding new employee!";
   }

}

function UpdateEmployee(result)
{
   
if(result>0)
   
{
      
var lblMsg=document.getElementById("lblMsg");
      lblMsg.innerText
="Employee updated successfully!";
   }

   
else
   
{
      
var lblMsg=document.getElementById("lblMsg");
      lblMsg.innerText
="Error occurred while updating the employee!";
   }

}

function DeleteEmployee(result)
{
   
if(result>0)
   
{
      
var lblMsg=document.getElementById("lblMsg");
      lblMsg.innerText
="Employee deleted successfully!";
   }

   
else
   
{
      
var lblMsg=document.getElementById("lblMsg");
      lblMsg.innerText
="Error occurred while deleting employee!";
   }

}

FillEmployeeList() 函數以Employee對象數組作爲輸入參數,還記得GetEmployees() web method 返回的Employee對象數組吧。然後對這個數組迭代處理,在每次迭代中,一個新的OPTION元素被創建並添加到下拉框中。 DisplayEmployeeDetails()函數以一個Employee對象作爲輸入,這個Employee對象包括了一個Employee的詳細 信息,並顯示在2個文本框中。InsertEmployee(), UpdateEmployee()和DeleteEmployee()函數以一個整型的數值標明INSERT, UPDATE和DELETE操作所影響的記錄數,一個大於0的數值標明操作成功,並在<SPAN>標記內顯示一個成功的信息;否則,顯示一個 錯誤信息。當頁面第一次加載時,需要用現有的employee ID給下拉框賦值,這得通過調用一個特定的名爲pageLoad()的函數中實現:
function pageLoad()
{
   CallWebMethod(
"select");
}


pageLoad()函數在客戶端瀏覽器頁面加載時自動調用,最後,錯誤處理(error handler)和超時處理(timeout handler)函數如下:
function ErrorHandler(result)
{
   
var msg=result.get_exceptionType() + "/r/n";
   msg 
+= result.get_message() + "/r/n";
   msg 
+= result.get_stackTrace();
   alert(msg);
}

function TimeOutHandler(result)
{
   alert(
"Timeout :" + result);
}

TimeOutHandler() 函數在任何web method 調用發生超時的情況下調用。它僅僅顯示了一個Alert給用戶。 ErrorHandler() 函數在有錯誤發生的情況下調用,其輸入result 參數提供了3個方法:get_exceptionType()、get_message()以及 get_stackTrace()。這三個方法分別返回異常類型(type of exception)、詳細錯誤信息 和堆棧跟蹤(stack trace)。這裏ErrorHandler()函數也僅僅顯示了一個alert給終端用戶。


測試網頁
現在,我們已經實現了web service和客戶端應用程序。測試一下吧!運行網頁,試着增加、更新、刪除一個employee看看,圖5顯示更新一個employee後的效果:

圖 5: 更新Employee後的頁面效果

要想測試錯誤處理函數,把初始化數據庫鏈接字符串改成一個空值,然後運行網頁看看,這次,就會顯示一個警報(alert),如圖6:

圖 6: 鏈接字符串錯誤報警


調用外部Web Services
這 個例子中,EmployeeService也是Web站點的一部分。有時候,我們的程序也許需要調用根本就沒有部署在我們的域的web services。 ASP.NET AJAX內部需要依賴XML HTTP 對象,而由於安全原因,是不能和部署在其它外部站點進行通信的。這就意味着上面所說的技術對外部的web services調用無效。不幸的是,ASP.NET AJAX關於此問題還沒有直接的解決方案(至少在RC版本)。然而,微軟發佈了一個仍在CTP階段的“Bridge”技術,我們可以使用此技術來調用一個 部署在本地的封裝(Wrapper)的類,然後在這個類中來調用外部的實際的Web Service。在當前的RC版本中,我們可以在我們的Web Site中創建一個Wrapper Web Service,以它來調用最初的Web Service。然後在客戶端程序中通過調用Wrapper Web Service實現通信。下面顯示必要的步驟:
1. 在web站點中添加一個web引用,指向外部的Web service;
2. 創建一個本地Web service;
3. 在新創建的Web service中,提供封裝的web method,這些方法調用外部的Web Method;
4. 用本文中所說的方法在客戶端應用程序中調用本地新添加的web Service。


調用ASP.NET Web Services的基礎架構
ASP.NET AJAX提供了完整的架構以從客戶端JavaScript調用ASP.NET web services。我們可以輕鬆地用AJAX把服務器端數據集成進用戶響應的Web頁面中。而我們所需要做的就是僅僅用[ScriptService]屬 性來標識web Service。ASP.NET AJAX 框架會爲我們的web service自動生成JavaScript代理,然後通過使用代理來調用web methods。


下載源碼
[原文源碼下載]


作者:Bipin Joshi
Email:http://www.dotnetbips.com/contact.aspx
簡介:Bipin Joshi是DotNetBips.com的管理員。他是http://www.binaryintellect.com/的發起人,這個公司提供.NET framwork的培訓和諮詢服務。他在印度孟買爲開發者提供培訓。他也是微軟的MVP(ASP.Net)和ASPInsiders的會員。


譯者注:原文中的document.getElementById可以用$get來代替 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章