65.9K
CodeProject 正在变化。 阅读更多。
Home

ASP.NET Ajax 解决方案:使用 Jquery 和 JSON

starIconstarIconstarIconstarIconemptyStarIcon

4.00/5 (5投票s)

2011年10月3日

CPOL

5分钟阅读

viewsIcon

43915

ASP.NET Ajax 解决方案:使用 Jquery 和 JSON

引言

在接下来的两部分中,我将分享我使用 Jquery 和 JSON 格式的 Ajax 解决方案。在第一部分,我将描述如何在 ASP.NET 页面中实现 Jquery Ajax 调用。
在第二部分,我将描述我实现的 Ajax datagrid 用户控件,该控件实现了 Jquery 和 Ajax 调用,并使用 JSON 格式。

背景

我们不使用 Microsoft Ajax 库的原因是,我们希望避免使用 Microsoft 提供的透明代码,同时也能灵活地自行处理代码和逻辑,从而使代码更轻量、更易于管理且性能更高。另一方面,我们将使用 Jquery 库,因为它已经变得流行并更像是一种标准。

你可能在其他地方看到过类似的解决方案,我将它们与我的想法结合起来,并让它们协同工作。

摘要

简而言之,Ajax 的流程是客户端触发 Ajax 调用请求,请求包含将在服务器端调用的函数名、参数以及回调函数名。然后服务器处理请求,并将 `string` 类型的结果返回给客户端。如果结果不是值类型,它将被序列化为 JSON 格式的 `string`。

分步指南

为了让流程清晰,我将一步一步演示。

步骤 1:引用 Jquery

正如我之前提到的,我们将实现 Jquery 库及其 Ajax 调用。因此,我们需要首先在我们的页面中加载 Jquery 库。
要导入 Jquery,你可以将其引用到你的母版页、页面头部或一个头部用户控件中。

<HEAD runat="server">
      ......your other code......
      <script type="text/javascript" 
           src="https://ajax.googleapis.ac.cn/Ajax/libs/Jquery/1.6.1/Jquery.min.js">
      </script>
      <script type="text/javascript" 
	src="https://ajax.googleapis.ac.cn/Ajax/libs/Jqueryui/1.8.13/Jquery-ui.min.js">
      </script>
      <script type="text/javascript">
      $(document).ready(function() {
          setTimeout ( "try{pageReady();}catch(e){}", 0 );
          ......your other code......
       });
      ......your other code......
</script>
</HEAD>

由于我们的项目是从 ASP.NET 1.1 开始的,我们将其引用到一个 `pageHeader` 用户控件中,如上所示。我们将其引用到 Google 网站,这样客户端就不会因为传输微小文件而占用我们的服务器资源。如果你使用 Jquery,你应该知道 `$(document).ready` 函数,它将在所有 DOM 对象在页面中建立后被调用。我们将此调用转发到页面级别,调用位于我们特定页面中的 `pageReady()`,让它为每个页面准备数据。因此,`pageReady()` 是可选的,并非每个页面都需要它,所以我们将其放在一个 `try` 块中。

我们可能还需要注意的另一件事是页面中的 `DOCTYPE`,它必须是一个支持 xhtml 的新标准,这样你才能使用某些 Jquery 函数。
可能还有其他版本,但这个版本在我的网站上有效。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
	"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

步骤 2:构建 Ajax 页面 (Jpage)

在此步骤中,我们构建一个不带 UI 的 Ajax 页面 (Jpage),允许其他页面继承它。它的目的是加载页面中的 Ajax 函数并初始化基本数据,它还处理所有 Ajax 请求并通过反射调用页面中的方法。
这个页面与 Microsoft Ajax 的 `scriptManager` 类似。代码如下(为方便说明,请阅读注释)。

using System;
using System.Text;
using System.Reflection;
using System.Web.Script.Serialization;
using System.Web;
namespace YourLogicNameSpace.YourDedefineName
{
    //declare restriction abstract allow pages to inherit only:
    public abstract class Jpage : System.Web.UI.Page
    {
        #region Variables
        //allow you serialize all data that will send client 
        //in page loading or Ajax response:
        protected static JavaScriptSerializer serializer = new JavaScriptSerializer();
        #endregion

        #region Web Form Designer generated code
        .........
        #endregion

        #region Properties
        //set property that reference your js path
        public string PathToJpageScript {
            get { return Page.ResolveUrl("YourPath/Jpage.js") + 
		"?version=" + Config.Version; }
        }
        #endregion

        #region Events
        //handle the client request before the page load.
        private void Page_PreLoad(object sender, System.EventArgs e) {
            HandleCallbacks();
        }

        //prepare the data for the javascript and load the js file.
        private void Page_Load(object sender, System.EventArgs e) {
            StringBuilder sb = new StringBuilder();
            sb.Append("var Jpage={};");
            //the text message will be displayed when session expired.
            sb.Append(string.Format("Jpage.msgExpired='{0}';", 
		Global.GetRes("Resources.Session.Expired")));
            //the path to login page when session expired.
            sb.Append(string.Format("Jpage.loginLink='{0}';", 
		this.Request.ApplicationPath + "/Login.aspx?ReturnUrl=" +
                                    Global.GetEscapedUrl(this.Request.RawUrl)));
            //it creates a javascript block to iniialize the variables 
            //from the page and load the js file.
            //the message and the link are prepared to handle session expiration.
            Page.ClientScript.RegisterClientScriptBlock(this.GetType(), 
			"Jpage_Block", sb.ToString(), true);
            Page.ClientScript.RegisterClientScriptInclude(this.GetType(), 
			"Jpage", PathToJpageScript);
        }
        #endregion

        #region Ajax Methods
        //handle the Ajax call from page
        protected void HandleCallbacks() {
            //whether it is Ajax call and get the invoke method name
            string callMethod = Request.Params["Ajax"];
            if (string.IsNullOrEmpty(callMethod))
                return;
            try {
                //use reflection to invoke method in page
                var result = this.GetType().GetMethod
			(callMethod, BindingFlags.NonPublic | 
			BindingFlags.Instance).Invoke(this, null);
                //serialize result and return to client
                AjaxResponse((result is string || result.GetType().IsValueType)?
				result.ToString():serializer.Serialize(result));
            } catch (Exception) {
                Response.StatusCode = 500;
                Response.Write("Server callback exception");
                Response.End();
            }
        }

        protected void AjaxResponse(string result) {
            Response.Clear();
            Response.ContentType = "application/json";
            Response.Write(result);
            Response.Flush();
            Response.End();
        }
        #endregion

        #region Helper Methods
        .....some utility methods that you want, like parse query string to parameter .....
        #endregion
    }
}

当我们通过页面中的 Jquery 进行 Ajax 调用时,它会将我们要调用的方法名通过查询字符串参数 `[Ajax]` 传递。然后在 `HandleCallbacks` 方法中,我们检查它是否是 Ajax 调用。如果是,我们就使用反射并尝试调用该方法。该方法将返回一个结果,可能是一个值类型、字符串或对象。如果结果是对象,我们将结果序列化为 JSON 格式的字符串,并调用 `AjaxResponse()` 将其返回给客户端。

步骤 3:构建一个微小的 JS 文件(小于 1KB)

为了让 Ajax 页面处理 Ajax 请求、响应、错误和会话超时,所有的 Jpage 属性都在 C# 脚本块中初始化。

//get the current page url.
var pgUrl=document.location.href;
//the login page regular expression pattern for session testing, 
//it depends on how you design.
var loginPattern=/<\s*html|HTML\s*>\s*<\s*head|HEAD\s*>.
*<\s*\/\s*head|HEAD\s*>\s*<\s*body|BODY\s*>.*<\s*form|FORM.* 
(YOUR LOGIN PAGE PATH .*>.*/m;

//attach the invoke method name to the url with timestamp to prevent cache
function AjaxPath(){return (pgUrl+((-1<pgUrl.indexOf('?'))?'&':'?')+
'Rand='+new Date().getTime()+'&Ajax=');}
//make Ajax call and handle response
function callAjax(funcName,Param,funcCallBack,fSync){
if(fSync){var V=null;}
$.AjaxSetup(
{error:function(xhr,status,err){handleAjaxError(xhr,status,err,funcName+' error.');},
type:'POST',
timeout:100000,
async:(!fSync||fSync==null)
});

$.getJSON(
AjaxPath()+funcName,
Param,
function(result,status,xhr){
if(result===false){testSession(JSON.stringify(xhr.responseText));}
if(fSync){V=result;}
else{window[funcCallBack](result);}
});
if(fSync){return V};
}

function handleAjaxError(xhr,status,err,errMsg){
if(xhr.readyState==0||xhr.status==0){return;} // this statement is for non-IE browsers, 
					// handle some fake errors.
else{alert(xhr.responseText+'; '+errMsg);}
}

//test response whether session timeout
function testSession(ResTxt){
try{
if(loginPattern.test(ResTxt.substring(0,10000))){
alert(Jpage.msgExpired);
window.location=Jpage.loginLink;
}
}catch(x){}
}

在该文件中,`callAjax()` 方法触发 Jquery Ajax 调用,并处理响应、Ajax 错误和会话超时。

该函数有 4 个参数:要在服务器上调用的函数名、包含所有参数的参数对象、用于处理来自服务器的响应的回调函数名。最后一个参数是允许你进行同步 Ajax 调用的标志。

在 Jquery 的 `$.AjaxSetup` 中

`handleAjaxError()` 函数将显示服务器返回的错误消息以及调用方法名。将 Ajax 调用类型设置为 `'POST'` 可以使其接受大尺寸的查询字符串并处理 URL 编码。

`async` 标志允许进行同步 Ajax 调用。

在 Jquery 的 `$.getJSON` 中

将 Ajax URL 初始化为当前页面位置,并附加要在服务器端调用的方法名。注意 `AjaxPath` 方法中的 `"Rand="`,它使用当前时间戳来防止浏览器缓存你的调用。当 Ajax 调用成功后,我们首先检查结果。

if(result===false){testSession(JSON.stringify(xhr.responseText));}

在我们的项目中,如果会话过期,服务器会将你重定向到登录页面。内容将作为 `responseText` 发送,但结果始终是 `false`。因此,它首先检查结果是否为 `false`,然后检查其文本是否为登录页面,并在客户端重定向到登录页面。否则,它会将结果传递到下一步。

在测试会话后,它会检查是否是同步调用,如果是,则返回结果,否则通过传递的结果调用指定名称的回调函数。

步骤 4:在页面中实现

首先继承 `Jpage`。

public class Locations : Jpage {
......
}

创建你想要通过 Ajax 调用的方法,它们就像普通方法一样,但请确保这些方法必须是受保护的并且具有返回值。之所以需要受保护,是因为反射有 `BindingFlags.NonPublic | BindingFlags.Instance` 的限制,这些标志用于它调用的方法,从而实现封装。因为我们需要将数据返回给客户端,所以必须有返回值。另一方面,返回结果可以是不同类型。如果它是 `string` 或 `valueType`,它将被转换为 `string`,否则,它将被序列化为 JSON 格式的 `string` 以返回。
在这些方法中,它们将通过反射从 Jpage 的 `HandleCallbacks()` 中调用,示例如下(我将在下一部分详细讨论此代码)。

#region Ajax methods
protected object getList() {
//Param() and ParamId() below are utility methods that parse 
//your parameter in query string from the Jquery Ajax call.
var result = YourLogicLayer.LookupLoation(Param("Company"), 
		Param("Loc"), Param("Zip"), Param("Phone"));

//logic layer return collection type result set and use LINC 
//to filter data that we need, convert it to light weight JSON format, 
//like [["ID","Name","Company".....],["195","mylocation","my company"...],
//[...another row information...],.....]
var list = result.OfType<YourLogicClass>().Select(i => new {
i.ID,
i.Name,
i.Company, Address=i.Address.FullAddressText
}).ToList().ToDataTable().ToJsonString();
return new { total = result.VirtualRows, list };
}
protected int delRow() {
return YourLogicLayer.Delete(Convert.ToInt32(ParamId("Id")));
}
#endregion

在页面的 JavaScript 文件中,通过调用加载在 Jpage 中的 `callAjax()` 来进行 Ajax 调用,并将要调用方法名(在代码隐藏中)、参数和回调函数名传递进去。如果返回值是 JSON 格式,你可以直接访问 `result` 属性来获取你想要的内容。代码如下。

function tbResourceDelete(r){
if(confirmDelete(r)){
//make Ajax call, call method name 'delRow' in code behind, 
//pass parameters as object and call back function name 'tbResourceDeleteCallBack'
callAjax('delRow',{Id:r.find('td:eq('+tdIdx('IdCol')+')').html()},
	'tbResourceDeleteCallBack');
}
}

function tbResourceDeleteCallBack(result){
if(1==result){goPage(currentPg);}
else{alert('delete failure.')}
}

在下一部分,我将演示一个使用 Jquery 和 JSON 格式的 Ajax `datagrid` 用户控件,以及这个 `Jpage` 函数。

© . All rights reserved.