利用 jQuery 和 ASP.NET Web 服务实现零回发业务网站
本文详细介绍如何使用 jQuery 和 ASP.NET Web 服务来实现零回发网站。
引言
本文介绍如何利用 jQuery 和 ASP.NET Web 服务编写零回发(无部分/完整回发,即无表单提交)的业务网站。本文还展示了如何从客户端 jQuery 代码调用以 .NET 对象作为参数的 Web 服务,以及如何处理 Web 服务调用返回的接收到的对象。
背景
让我们回到基本概念。为什么要使用控件?原因是我们如果不使用控件,就只能纯粹地打印文本信息,用户将很难与网站进行交互。表单提交(回发)的整个故事就由此开始。服务器如何知道发生了用户交互,并且需要执行某些操作?答案就是回发。很快,回发就变得非常令人讨厌,并催生了 AJAX 技术,实现了异步部分回发,其中只有特定更新面板中的控件才会回发到服务器,当然,还伴随着 ViewState 令人讨厌的负担。对于此类回发,更新面板同样会经历几乎所有的事件,如 Page_Init
、Page_Load
、控件事件、Page_PreRender
等,除了 Page_Render
。很快,即使这样有时也会因为带宽负载过重而变得慢得令人讨厌。那么,如果根本没有表单提交或部分回发,会怎样呢?应该有一种机制,允许用户在客户端与控件进行交互(这是众所周知且容易的),然后让服务器响应在浏览器级别发生的此类交互(我现在将介绍这一点)。我们知道客户端脚本可以调用带有 [ScriptService]
属性的 ASP.NET Web 服务。因此,如果我们能将相关控件的状态放入一个对象中,并通过 jQuery(客户端)调用这样的服务,然后获得作为 aforesaid 服务返回值的另一个对象,并随后在客户端更改控件的状态,那么我们的工作就完成了。用户现在知道,根据他们的交互,服务器已经响应,并且网站也相应地反映了这一点。那么,如果这就是所需,为什么还需要回发呢?
Using the Code
代码是一个简单的 ASP.NET Web 站点解决方案,其中包含一个母版页和一个额外的 Web 内容表单,仅用于说明概念。仅使用了三个服务器控件:一个 asp:Button
,一个 asp:Label
和一个 asp:DropDownList
。
我们必须首先添加 .js 文件 jquery-1.4.1.js 和 JSON2.js。JSON2.js 是从 http://www.JSON.org/json2.js 下载的。它包含非常有用的 JSON.stringify()
函数,该函数可以 JSON 序列化 JavaScript 对象,使其可以作为参数发送到接受类似 .NET 对象的 Web 服务调用中。我们应该首先在母版页的 <head> .... </head>
部分包含对 .js 文件的引用。
<script type="text/javascript" src="jquery-1.4.1.js"></script>
<script type="text/javascript" src="JSON2.js"></script>
<script type="text/javascript" language="javascript">
window.history.forward(1);
</script>
在上面的代码中,我们包含了 .js 文件的引用,并禁用了网站范围内的浏览器后退按钮。我们还在 <form> .... </form>
标签内包含了一个 ScriptManager
。
<asp:ScriptManager ID="KovairScriptManager" runat="server"
EnableScriptGlobalization="true"
EnablePageMethods="true"> </asp:ScriptManager>
现在,我们进入 Web 内容表单 Default.aspx。
现在声明控件。
<asp:Button ID="btnRetrieveData" runat="server"
Text="GetData" Width="175px"
OnClientClick = "javascript:return GetStudents();" UseSubmitBehavior="false"/>
<asp:DropDownList ID="ddlStudents" runat="server"
Height="16px" style="width: 77px"
Width="250px"></asp:DropDownList>
<asp:Label ID="lblResult" runat="server"
Text="Label" Height="50px" Width="100px">
</asp:Label>
现在,我们在数据库 Student
中创建两个表。
USE
[Student]
GO
CREATE TABLE [dbo].[StuRec](
[RollNo] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](50) NULL,
CONSTRAINT [PK_StuRec] PRIMARY KEY CLUSTERED ([RollNo] ASC )
GO
CREATE TABLE [dbo].[StuMark](
[RollNo] [int] NOT NULL,
[Marks] [int] NULL,
CONSTRAINT [PK_StuMark] PRIMARY KEY CLUSTERED ([RollNo] ASC)
GO
ALTER TABLE [dbo].[StuMark] WITH CHECK
ADD CONSTRAINT [FK_StuMark_StuRec]
FOREIGN KEY([RollNo]) REFERENCES [dbo].[StuRec] ([RollNo])
GO
我们添加了两个表:StuRec
,包含学生的 RollNo
和 Name
;以及 StuMark
,包含学生的 RollNo
和 Marks
。这只是为了说明这样一个概念:从客户端,如果下拉列表中选择了一个学生,那么通过从 StuMark
表中检索相应的记录,应该在标签中显示该学生的相应分数。用适当的数据填充这些表。
因此,我们首先编写 Web 服务:一个用于从 StuRec
表中获取所有学生,另一个用于从 StuMark
表中获取特定学生的成绩。请注意,Web 服务类已用 [ScriptService]
属性进行修饰,以便可以从客户端脚本调用它。
[WebService(Namespace = "https:///Kovair.Site")]
[ScriptService]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class KovairWebService : System.Web.Services.WebService {
public KovairWebService ()
{
}
[WebMethod]
public List<ListItem> GetStudents() {
string connStr =
ConfigurationManager.ConnectionStrings["KovairSiteConnectionString"].ToString();
SqlConnection conn = new SqlConnection(connStr);
SqlCommand comm = new SqlCommand();
comm.CommandText = "SELECT RollNo, Name From dbo.StuRec";
comm.Connection = conn;
SqlDataAdapter da = new SqlDataAdapter(comm);
DataSet ds = new DataSet();
conn.Open();
da.Fill(ds, "StudentRecord");
conn.Close();
List<ListItem> stuList = new List<ListItem>();
if (ds.Tables != null)
{
int recordCount = ds.Tables["StudentRecord"].Rows.Count;
if (recordCount > 0)
{
DataTable dtab = ds.Tables["StudentRecord"];
for (int i = 0; i < recordCount; i++)
{
stuList.Add(new ListItem(
dtab.Rows[i]["Name"].ToString(),
dtab.Rows[i]["RollNo"].ToString()
));
}
}
}
return stuList;
}
[WebMethod]
public StuMark GetStudentDetail(StuRec stu)
{
string connStr =
ConfigurationManager.ConnectionStrings["KovairSiteConnectionString"].ToString();
SqlConnection conn = new SqlConnection(connStr);
int roll = stu.RollNo;
SqlCommand comm = new SqlCommand();
comm.CommandText =
@"SELECT R.RollNo, M.Marks From dbo.StuMark M INNER JOIN
dbo.StuRec R ON M.RollNo = R.RollNo WHERE R.RollNo = @rollNo";
comm.Parameters.Add(new SqlParameter("@rollNo", SqlDbType.Int)).Value = roll;
comm.Connection = conn;
SqlDataAdapter da = new SqlDataAdapter(comm);
DataSet ds = new DataSet();
conn.Open();
da.Fill(ds, "StudentDetailRecord");
conn.Close();
string marks = String.Empty;
if (ds.Tables != null)
{
int recordCount = ds.Tables["StudentDetailRecord"].Rows.Count;
if (recordCount > 0)
{
DataTable dtab = ds.Tables["StudentDetailRecord"];
marks = dtab.Rows[0]["Marks"].ToString();
}
}
StuMark sm = new StuMark();
sm.RollNo = stu.RollNo;
sm.Marks = Convert.ToInt32(marks);
return sm;
}
}
在第二个 Web 服务中,我们有一个类型为 StuRec
的参数 stu
,它返回一个类型为 StuMark
的对象。对象定义如下所示。它们是 .NET 对象。
public class StuMark
{
public int RollNo { get; set; }
public int Marks { get; set; }
}
public class StuRec
{
public int RollNo { get; set; }
public string Name { get; set; }
}
现在,我们将看到如何调用第一个 Web 方法 GetStudents()
,它返回一个 ListItem
类型的 List
对象。为什么我们要返回 List<ListItem>
?因为我们想将这些 ListItems
添加到客户端的 DropdownList
中。如果点击按钮,应该调用 JavaScript 方法 GetStudents()
,因为它通过按钮的 OnClientClick
属性绑定。我们利用 jQuery 的 $.ajax()
实用工具通过 jQuery 从客户端调用 Web 服务。代码简洁、干净且易于阅读。因此,人们发现 jQuery 比 JavaScript 更好。
function GetStudents() {
$('#<%=ddlStudents.ClientID %>').empty().append
('<option selected="selected" value="0">Loading...</option>');
$.ajax({
type: 'POST',
url: 'https:///Kovair.Site/KovairWebService.asmx/GetStudents',
data: {},
contentType: 'application/json; charset=utf-8',
dataType: 'json',
success: function(response, status) {
var list = (typeof response.d) == 'string' ? eval('(' + response.d + ')') : response.d;
var control = $('#<%=ddlStudents.ClientID %>');
control.removeAttr("disabled");
control.empty().append('<option selected="selected" value="0">Please select</option>');
$.each(list, function() {
control.append('<option value ="' + this['Value'] +
'">"' + this['Text'] + '"</option>');
});
},
failure: function(response) {
alert(response.d);
}
}
);
}
jQuery 使用 JSON。因此,我们看到内容类型是 JSON,数据类型是 JSON。URL 的形式是 Web 服务 URI / Web 方法。在成功时,我们收集返回的 List<ListItem>
,并运行一个 $.each()
jQuery 函数来迭代它,并将其添加到客户端的 dropdownlist
中。所以,没有表单提交,没有回发。
但真正有趣的是第二个 Web 方法,它有一个类型为 StuRec
的参数 stu
,这是一个 .NET 对象。现在,jQuery 如何传递一个 .NET 对象作为参数?答案是 JSON 和 JavaScript 对象。我们创建一个类似的 JavaScript 对象,并使用 JSON2.js 中编写的 JSON.stringify()
函数将其转换为适当的 JSON string
。代码如下所示:
var NewStudent = {};
NewStudent.RollNo = 22;
NewStudent.Name = "Jacob";
// Create a data transfer object (DTO) with the proper structure.
var DTO = { 'stu': NewStudent };
$(function() {
$('#<%=ddlStudents.ClientID %>').change(function(e) {
var rollNo = this.options[this.selectedIndex].value;
var name = this.options[this.selectedIndex].text;
NewStudent.RollNo = rollNo;
NewStudent.Name = name;
$.ajax({
type: 'POST',
url: 'https:///Kovair.Site/KovairWebService.asmx/GetStudentDetail',
data: JSON.stringify(DTO),
contentType: 'application/json; charset=utf-8',
dataType: 'json',
success: function(response, status) {
var list = (typeof response.d) == 'string' ? eval('(' + response.d + ')') : response.d;
var oup = "RollNo: " + list.RollNo.toString() + " Marks: " + list.Marks.toString();
$('#<%=lblResult.ClientID %>').html(oup);
},
failure: function(response) {
alert(response.d);
}
}
);
}
)
}
);
在上面的方法中,我们使用了 $document.ready()
的简写形式,也可以是 $.ready()
或 简单地 $()
。我们在初始表单加载时注册 dropdownlist
的 change 事件,其中指定了在页面初始加载后,如果 dropdownlist
的选择发生任何变化时要执行的操作。在此之前,我们在 JavaScript 中创建了一个对象 NewStudent
,它收集所选值和所选文本的值,然后使用 JSON.stringify
将其转换为 JSON 文本,以便作为参数传递给 Web 服务。成功时,返回对象的字段值再次收集到一个 JavaScript 对象变量中,然后显示在一个标签中。
关注点
我们已经学会了如何通过 $.ajax()
调用一个带有 [ScriptService]
属性的 Web 服务,作为对客户端(浏览器)捕获的控件事件的响应。因此,我们已经学会了如何获取数据并在客户端显示它,而无需提交表单,正如 default.aspx 的代码隐藏文件所示,它只有一个代码隐藏类声明,如下所示:
public partial class _Default : System.Web.UI.Page
{
}
历史
- 2011 年 9 月 16 日:初次发布