技术开发 频道

用IE的Web服务建立ASP.NET应用程序

  【IT168 技术文档】

  在建立商业网站的时候,开发者遇到的一种限制是只能把浏览器作为用户界面。例如,在很多情形中,用户希望在执行某些操作(例如输入雇员编号)之后从服务器检索到信息。为了达到这个目的,他们将把页面发回到服务器,检索雇员信息,并用从服务器上检索到的信息刷新页面。尽管目前这种刷新整个页面的方法很普遍,但是它的效率很低,因为Web页面刷新了,并且重新呈现了整个页面的内容,即使页面只要少量的部分真正地发生了改变。在搜索某个类别或者搜索引擎的时候你就可以注意到这种低下的效率。它的延迟和资源浪费非常明显。但是,如果相同的功能不用刷新浏览器页面就能够完成,用户体验将会得到很大的提高。为了实现这种目的,我们需要在不离开当前页面的情况下执行一段服务器代码的途径,这就是Web服务行为起的作用。在这种情形下,服务器上执行的代码片段是Web服务方法的代码,浏览器的角色是调用这段服务器代码而不离开或刷新当前页面。
 
  使用Web服务行为的时候,你只需要从某个客户端浏览器的Web页面中发送一个请求执行特定的Web服务方法。在服务器端,ASP.net运行时接收到请求,使用相关的参数调用Web服务方法。在Web服务执行完成后,它把结果传达给调用者,接下来结果被浏览器显示或处理。其结果是,你可以建立典型的客户端/服务器通讯,而不需要理会下层HTTP协议的无状态(stateless)特性。Web服务行为的另一个优点是为了实现功能,客户端上只需要一个文件(WebService.htc)存在。使用Web服务方法的时候,你还可以异步调用Web服务方法。这种能力非常强大,可以用于在客户端建立丰富的用户体验。例如,当用户继续处理相同页面上的事务的时候,你可以使用Web服务行为让服务器验证某些数据。一旦函数调用返回了,你就可以得到执行结果并把结果传达给用户。
 
  Web服务行为
 
  Web服务行为是把HTML组件(HTC)文件作为附属行为实现功能的,它可以用于Internet Explorer 5及以后版本。前面提到过,Web服务行为通过利用工业标准协议(例如HTTP、SOAP和XML)提供了跨平台调用远程Web方法的途径。Web服务行为的重要特性之一是,它允许你在没有深厚的SOAP知识的情形下使用这些功能。Web服务通过处理浏览器和Web服务之间的SOAP数据包通讯,基本上简化了Web服务的远程调用。你不用担心SOAP消息的聚集(assembling)和分解(disassembling)。所有处理SOAP详细信息的代码都被封装在行为之中,简化了主Web页面中的客户端脚本。
 
  Web服务行为是使用特定的IE行为语法嵌入Web页面的JavaScript文件。通过把属性和方法暴露给客户端脚本,Web服务行为聚集消息并分解Web服务发回的响应信息。行为所暴露的对象不仅能够启动清晰的错误处理方法,而且提供了对返回数据的简单地访问。Web服务行为从客户端脚本接收到方法调用,并使用SOAP消息给Web服务发送请求。结果会返回客户端脚本,并且处理过程继续。接下来Web页面可以把信息用于任何需要的情形中,例如更新页面的某些部分,发送错误消息等等。
 
  Web服务行为的一个关键特性是它允许客户端脚本访问Web服务而不用导航到另一个URL。下面的列表详细说明了Web服务行为支持的重要的方法:
 
  · createUseOptions(建立使用的选项)--允许我们跨越远程方法调用保存用户认证信息。当我们使用SSL与远程Web服务通讯的时候会很有用。
 
  · callService(调用服务)--允许我们异步调用远程Web服务。
 
  · useService(使用服务)--允许我们在调用Web服务的时候为该服务建立一个"友好的"名称。
 
  为了在IE 5.0和以上版本的Web页面中使用行为,你必须下载webservice.htc行为文件,并把它保存在与你的Web页面相同的文件夹中。
 
  实现过程
 
  你已经了解了Web服务行为的一些基础知识,现在可以看一个示例应用程序了,它演示了在ASP.NET应用程序中如何使用Web服务行为。在这个例子中,你将建立一个简单的应用程序,它允许你从Northwind数据库检索雇员信息。示例应用程序还允许基于雇员的ID搜索雇员信息。

  Employee Web服务的建立过程

  在这一部分,你需要首先建立一个叫作EmployeeWebService的新Visual C# Web服务项目。项目建立之后,你需要把默认的Web服务类的名字Service1改成EmployeeService。接着你需要导入下面的名字空间以执行数据访问和处理XML数据。

using System.Data.SqlClient; using System.Xml; [WebMethod] public XmlDocument GetEmpDetailsByEmpID (int employeeID) {  string connString =   System.Configuration.ConfigurationSettings.AppSettings["connectionString"];   SqlConnection sqlConnection = new SqlConnection(connString);   try   {    DataSet employeeDataset = new DataSet("EmployeesRoot");    //把需要执行的存储过程的名字和SqlConnection 对象作为参数传递进来    SqlDataAdapter adapter = new SqlDataAdapter();    SqlCommand command = new SqlCommand("Select * from Employees Where EmployeeID ="+
   employeeID.ToString(),sqlConnection);    //设置SqlCommand对象的属性    command.CommandType = CommandType.Text;    adapter.SelectCommand = command;    //使用存储过程返回的值填充数据集    adapter.Fill(employeeDataset,"Employees" );    XmlDocument xmlDoc = new XmlDocument();    xmlDoc.LoadXml(employeeDataset.GetXml());    return xmlDoc;   }   catch (Exception ex)   {    throw ex;   }   finally   {    if (sqlConnection.State == ConnectionState.Open)    {     sqlConnection.Close();    }   }  }
  
  属性WebMethod表明该方法将作为可以被调用的Web方法暴露。在项目部署的时候,ASP.NET运行时提供使用某些协议(例如XML、HTTP和SOAP)在Internet上调用这个方法所需要的所有管道信息。
 
   [WebMethod]
 
  上面的方法名称告诉我们,GetEmpDetailsByEmpID把employeeID作为参数并返回XMLDocument形式的雇员详细信息。
public XMLDocument GetEmpDetailsByEmpID(int employeeID) { string connString = System.Configuration.ConfigurationSettings. AppSettings["connectionString"];
  
  上面的代码行使用ConfigurationSettings类的AppSettings属性从web.config文件的<appsettings>部分检索连接字符串。在web.config文件中连接字符串是这样定义的:
<appSettings> <add key="connectionString" value="server=localhost;uid=sa;pwd=;database=Northwind" /> </appSettings>
  
  下面一行代码建立了SqlConnection对象的一个实例,给它传递了用于建立数据库连接的连接字符串:
SqlConnection sqlConnection = new SqlConnection(connString);
  
  接着你把所有的可执行代码封装在一个try...catch代码块中以处理执行后面的语句时可能发生的任何错误:
try { DataSet employeeDataset = new DataSet("EmployeesRoot"); SqlDataAdapter adapter = new SqlDataAdapter();
  
  下一步,你建立了SqlCommand对象的一个实例,给它的构造函数传递你希望执行的SQL语句和前面步骤中建立的SqlConnection对象:
SqlCommand command = new SqlCommand("Select * from Employees Where EmployeeID =" + employeeID.ToString(),sqlConnection);
  
  接着你把SelectCommand属性设置为适当的值,表明你希望执行一个SQL语句:
//设置SqlCommand对象的属性 command.CommandType = CommandType.Text;

 

 

  

  接着把SqlDataAdapter对象的SelectCommand属性设置为前面建立的SqlCommand对象:
adapter.SelectCommand = command;
  
  现在使用Fill方法,通过在数据源上执行前面指定的SQL语句从数据源检索数据:
//用存储过程返回的值填充数据集 adapter.Fill(employeeDataset,"Employees" );
  
  一旦雇员信息成为数据集形式的,你就可以检索它的内容,并把它作为参数传递到XmlDocument对象的LoadXml方法中。最后,把该XmlDocument对象返回到该Web服务的调用者:
XmlDocument xmlDoc = new XmlDocument(); xmlDoc.LoadXml(employeeDataset.GetXml()); return xmlDoc; } catch (Exception ex) {  throw ex; }
  
  在最后的代码块中,你检查了State属性以验证Connection(连接)是否仍然是打开的。如果连接仍然是打开的,你就通过调用该连接对象的Close方法关闭它:
finally {  if (sqlConnection.State == ConnectionState.Open)  {   sqlConnection.Close();  } } }
  
  现在你已经建立了Web服务了,可以用鼠标右键点击EmployeeService.asmx文件并选择Build(建立)和Browse(浏览)来测试它的功能。
 
   在ASP.NET页面中如何使用Web服务行为调用Web服务
 
   在Web页面中使用Web服务行为的第一步是使用类似下面的语法把它嵌入页面代码:
<div id="service" style="BEHAVIOR:url(WebService.htc)"></div>
  
  上面的代码依赖IE 5(及以上版本)内建的行为功能来验证JavaScript文件的位置,而该文件被用于调用Web服务。前面谈到,webservice.htc文件的位置必须与Web页面的文件夹相同。我们要重点注意,行为文件的载入发生在客户端而不是服务器上。
 
   在嵌入了上面的代码后,我们就可以使用JavaScript代码调用行为并把它链接到兼容WSDL 1.1的Web服务了。这是通过引用被嵌入的行为id(前面代码中的服务)和调用它的useService方法来实现的:
service.useService("http://localhost/MyProjects/
WebServiceBehavior/EmployeeService.asmx?WSDL
","sVCEmployee");
  
      你需要在页面的onLoad事件句柄中调用useService方法,这样才能确保在调用Web服务的任何方法之前,该Web服务已经映射了。
 
  UseService方法有下面两个参数:
 
  · Web服务的WSDL文件的路径。
 
  · 用于以后引用该Web服务的一个"友好的"名字。每次使用行为调用EmployeeService.asmx文件中的方法的时候都会使用这个名字。
现在已经建立了Web服务并可以访问它了。异步调用Web服务方法可以分为两个步骤。异步调用的优点是Web页面不用等待Web服务返回。第一步,你调用Web方法并把回调(callback)函数作为参数。第二步,在执行了需要的方法后,Web服务返回,启动回调函数。
 
  下面是完整的源代码列表:
<%@ Page language="C#" Codebehind="EmployeeServiceClient.ASPx.cs" AutoEventWireup="false" Inherits=" EmployeeWebServiceClient.EmployeeServiceClient" %> <HTML> <HEAD> <title>Employee Details</title> <SCRIPT LANGUAGE="JScript"> //定义一个模块级的变量来捕捉事件id var iCallID ; function GetEmployeeDetails() {  // 调用svcEmployee Web服务的GetEmployeeDetails方法  iCallID =   service.svcEmployee.callService(DisplayResults,
"GetEmpDetailsByEmpID",txtEmployeeID.value); } function DisplayResults(result) {  var strXML,objXMLNode,objXMLDoc,objEmployee,strHTML;    //检查事件id是否相同  if (iCallID != result.id)   return;  if(result.error)  {   // 显示错误信息   var faultCode = result.errorDetail.code;   var faultString = result.errorDetail.string;   alert("ERROR: Code = " + faultCode + ", Fault String=" + faultString);  }  else  {   //把结果值赋予本地变量   objXMLNode = result.value;   objXMLDoc = new ActiveXObject("Microsoft.XMLDOM");   //把返回的XML字符串载入XMLDOM对象   objXMLDoc.loadXML(objXMLNode.xml);   //得到Employees节点的指针   objEmployee = objXMLDoc.selectSingleNode("GetEmpDetailsByEmpIDResult").selectSingleNode
(
"EmployeesRoot").selectSingleNode("Employees");   //检查从服务器返回的雇员指针是否有效   strHTML = "<font color=’#0000FF’>";   if (objEmployee != null)   {    //动态生成HTML,并把它添加到一个字符串变量中     strHTML += "<br><br>Employee ID :<b>" +       objEmployee.selectSingleNode("EmployeeID").text + "</b><br><br>";     strHTML += "Employee First Name :<b>" +       objEmployee.selectSingleNode("FirstName").text +       "</b><br><br>";     strHTML += "Employee Last Name :<b>" +       objEmployee.selectSingleNode("LastName").text + "</b><br><br>";     strHTML += "Employee Title :<b>" +       objEmployee.selectSingleNode("Title").text + "</b><br><br>";     strHTML += "Employee Title :<b>" +       objEmployee.selectSingleNode("Title").text + "</b><br><br>";     strHTML += "Title Of Courtesy:<b>" +       objEmployee.selectSingleNode("TitleOfCourtesy").text + "</b><br><br>";     strHTML += "Postal Code:<b>" +       objEmployee.selectSingleNode("PostalCode").text + "</b><br><br>";   }   else   {    strHTML += "<br><br><b>Employee    not found</b>";   }   strHTML += "</font>"   //把动态生成的HTML赋予div标记   divContents.innerHTML = strHTML;  } } function init() {  // 建立Web服务的实例并把它叫做svcEmployee  service.useService("http://localhost/MyProjects/15Seconds/WebServiceBehavior/_    EmployeeWebService/EmployeeService.asmx?WSDL","svcEmployee"); } </SCRIPT> </HEAD> <body onload="init()"> <div id="service" style="BEHAVIOR: url(webservice.htc)"></div> <H1 align="center"> <font color="#800080">Employee Details</H1> </FONT> <br><br> <P align="left"> <font color="#800080"><b>Enter the Employee ID:</b></font> <INPUT id="txtEmployeeID" name="txtEmployeeID" style="LEFT: 149px; TOP: 72px"> <INPUT id="btnAdd" type="button" value="Get Employee
Details
" name="btnGetEmployee" onclick="return GetEmployeeDetails()"> </P> <P></P> <div id="divContents"> </div> <P></P> </body> </HTML>
  
  在GetEmployeeDetails方法中,你通过把回调方法名称和输入参数作为参数传递给Web服务来调用该Web服务的web方法。这是通过调用Web服务行为的callService方法实现的:
function GetEmployeeDetails() { // 调用svcEmployee web服务的GetEmployeeDetails方法 iCallID = service.svcEmployee.callService(DisplayResults,"GetEmpDetailsByEmpID",txtEmployeeID.value); }

  CallService方法返回一个唯一的标识符,它可以用于识别Web服务调用。如果你进行多个异步Web服务调用,接着在客户端浏览器中把结果拼装在一起,那么这个标识符就是必要的。在这种情况下,你把这个ID与作为result对象的一个属性返回的ID进行匹配。匹配过程是在回调函数中完成的:
function DisplayResults(result) { var strXML,objXMLNode,objXMLDoc,objEmployee,strHTML;
  
  在下面几行代码中,你把result对象的ID与callService方法返回的ID进行匹配:
//检查事件是否相同 if (iCallID != result.id) return;
  
  接着检查error属性以确定在Web服务的执行过程中是否发生过错误。如果发生过错误,就在消息窗口中显示错误信息。如果没有错误,就处理返回的结果并把它们显示在HTML DIV标记中:
if(result.error) {  // 读取错误信息  var faultCode = result.errorDetail.code;  var faultString = result.errorDetail.string;  alert("ERROR: Code = " + faultCode + ", Fault String=" + faultString); } else {  //把结果值赋予本地变量  objXMLNode = result.value;  objXMLDoc = new ActiveXObject("Microsoft.XMLDOM");  //把返回的XML字符串载入XMLDOM 对象  objXMLDoc.loadXML(objXMLNode.xml);  //得到Employees节点的指针  objEmployee = objXMLDoc.selectSingleNode("GetEmpDetailsByEmpIDResult").   selectSingleNode("EmployeesRoot").selectSingleNode("Employees");  //查看从服务器返回的employee 指针是否有效  strHTML = "<font color=’#0000FF’>";  if (objEmployee != null)  {   //动态生成HTML并添加到字符串的内容中   strHTML += "<br><br>Employee ID :<b>" +    objEmployee.selectSingleNode("EmployeeID").text +    "</b><br><br>";   strHTML += "Employee First Name :<b>" +    objEmployee.selectSingleNode("FirstName").text +    "</b><br><br>";   strHTML += "Employee Last Name :<b>" +    objEmployee.selectSingleNode("LastName").text +    "</b><br><br>";   strHTML += "Employee Title :<b>" +    objEmployee.selectSingleNode("Title").text +    "</b><br><br>";   strHTML += "Employee Title :<b>" +    objEmployee.selectSingleNode("Title").text +    "</b><br><br>";   strHTML += "Title Of Courtesy:<b>" +    objEmployee.selectSingleNode("TitleOfCourtesy").text +    "</b><br><br>";   strHTML += "Postal Code:<b>" +    objEmployee.selectSingleNode("PostalCode").text +    "</b><br><br>";  }  else  {   strHTML += "<br><br><b>Employee   not found</b>";  }  strHTML += "</font>"  //把动态生成的HTML赋予div标记  divContents.innerHTML = strHTML;  } }
  
  在上面的例子中,你使用调用Web服务时指定的回调函数来处理Web服务返回的结果。还有一种办法,在定义DIV标记的时候你也可以指定一个回调函数(用这种方法把Web服务行为包含在页面中)。例如,在下面的代码中,你使用Web服务行为支持的onresult事件句柄指定了回调函数:
<div id="service" style="BEHAVIOR: url(webservice.htc)" onresult="DisplayResults()"></div>
  
  有了上面的定义后,你就可以在DisplayResults函数中处理Web服务返回的结果了。下面的代码演示了DisplayResults函数的一种实现示例:
function DisplayResults() {  //检查事件id是否相同  if (iCallID != event.result.id)   return;  if(event.result.error)  {   var faultCode = event.result.errorDetail.code;   var faultString = event.result.errorDetail.string;   alert("ERROR: Code = " + faultCode + ", Fault String=" +   faultString);  }  else  {   //显示结果值   alert(event.result.value);  } }
  
  在代码中你可以看到,我们使用event对象得到包含Web服务调用返回结果的result对象的指针。
 
   把代码放在一起
 
  如果使用浏览器查看上面的ASP.NET页面,在employee文本框中输入一个有效的Employee ID并点击"Get Employee Details"调用远程Web服务。这种操作将导致对该Web服务的异步调用,并且该Web服务返回的结果将显示在Web页面的DIV元素中。
 
  我要再次重点强调,对于IE Web服务行为,需要IE 5或以上版本,因此如果你能够确定用户所使用的浏览器类型,这种技术就很适合用于企业内部网应用程序。
 
     结论
 
  在本文中,你看到了Web服务行为是如何提供一种从Web服务器向客户端浏览器传递信息的改进的解决方法的。使用Web服务行为调用远程Web方法简化了客户端的操作,使Web服务的使用更加有吸引力。我们同时看到Web服务行为是如何通过提供动态的交互操作Web页面,帮助我们提高了用户体验的。由于Web服务行为(webservice.htc文件)封装了使用SOAP调用远程Web服务所需要的代码,随着SOAP标准的演化,你可以独立地更新行为而不需要改变客户端脚本。
0
相关文章