作者: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表裏的某個僱員信息 |
GetEmployees() 和 GetEmployee()方法以Employee對象的形式返回數據,因此,首先創建一個Employee類。右鍵單擊App_Code文件夾,選擇 “添加新項…”,添加一個叫Employee的類,下面顯示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>部分如下:
<add name="connstr" connectionString=
"data source=./sqlexpress;
initial catalog=northwind;
integrated security=true"/>
</connectionStrings>
這部分存放數據庫鏈接字符串,用於指向Northwind數據庫,確保修改SqlServer名稱、IP地址以及驗證方式以和我們的開發環境相符。
現在,打開EmployeeService.cs添加如下代碼:
public EmployeeService()
{
strConn = ConfigurationManager.ConnectionStrings["connstr"].
ConnectionString;
}
代碼使用了ConfigurationManager類來讀取配置文件中的數據庫鏈接字符串,並存放在一個類級別的變量strConn中,這個變量將被下面的所有Web Method所使用。
現在,添加GetEmployees() web method:
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如下:
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 語句
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類的定義:
[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頁面文件中:
<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操作完成後,將會顯示成功或者失敗的信息。下面是所有的頁面代碼:
<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()的函數:
{
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相應的返回值。
{
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()的函數中實現:
{
CallWebMethod("select");
}
pageLoad()函數在客戶端瀏覽器頁面加載時自動調用,最後,錯誤處理(error handler)和超時處理(timeout handler)函數如下:
{
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來代替