AJAX .Net组件用户指南(译文&原文)

Posted on 2007-08-03 09:18  Ekin.S.Sun  阅读(1379)  评论(3编辑  收藏  举报

AJAX .Net组件用户指南
            翻译by:Ekin.S.Sun
例子下载

背景

异步JavaScriptXML (AJAX)最近变得非常流行,它不仅仅被小范围运用,像Google公司在Google推荐中诸如Google地图也使用了该技术。 ASP.Net环境中, AJAX允许服务器端无回调处理客户端(浏览器)事件,因而降低了服务器性能要求。 换句话说它从服务器为异步分派和进程请求提供一个框架。 AJAX是对现有知识的整合,它不是全新的技术,然而却是对这些技术进行的全新整合(整体,这便是AJAX)扩充。

使用Michael SchwarzAJAX .Net 组件可以使ASP.Net开发人员快捷,简便地利用AJAX部署配置页面文件。用户应该注意该组件处于开发的初期,因此它并不是如此地成熟。

应该指出的是有些诸如AJAX可能导致程序违反多层结构的规则(程序多层结构) 依我的观点来看AJAX增加逻辑层数的可能(或坏,业务层)将沦为表示层。严密的架构师,比如我,也许会因为这种想法而畏缩。我认为,即使AJAX的使用轻微地违犯分层的界限,结局也是很值得的。 当然,有些方面您也需要依据您特定项目和环境综合考虑。

     

了解更多的 AJAX知识, 请访问:
http://en.wikipedia.org/wiki/AJAX
http://www.adaptivepath.com/publications/essays/archives/000385.php

工作原理总览

AJAX依靠中介来分派和处理发往或是来自于服务器的请求。为了完成这项任务,.Net 组件使用客户端的XmlHttpRequest对象。 多数浏览器对XmlHttpRequest对象支持都很好,这是使它成为一个可以选择的解决方案。因为组件的目的是隐藏XmlHttpRequest的执行,我们将不对它进行详细的讨论。

组件的运作通过将.Net函数标记为AJAX方法来实现。  一旦标记, AJAX使用XmlHttpRequest对象创建相应的JavaScript函数,在客户端(和其他的JavaScript函数一样)和在服务器端被称为代理。这些代理标明回到服务器端函数的途径。

复杂?其实不染。让我们来看个简单的例子。以下是.Net函数:

'VB.Net

public function Add(firstNumber as integer, secondNumber as integer) as integer

  return firstNumber + secondNumber

end sub

 

//C#

public int Add(int firstNumber, int secondNumber)

{

   return firstNumber + secondNumber;

}

Ajax .Net 组件将自动地创建一个由两个参数组成的被称做“Add”的JavaScript函数。当这个函数被JavaScript(在客户端)调用的时候,请求将传递到服务器端并返回结果值到客户端。

初学阶段

首先我们进入为你的项目“安装”.dll文件的步骤。如果你知道如何添加一个动态库文件引用,可以跳过这个步骤。

     首先,如果你还没有这个组件,下一个最新版本的,将压缩包解压的ajax.dll放在你的项目中的文件夹中。在Visual Studio.Net编译器中,在解决方案浏览器的“引用”接点上点击右键,选择添加引用。在新的打开对话框,点击浏览并找到引用文件/ ajax.dll,然后点击打开和接着的Ok.现在你已经可以使用AJAX .Net组件开始你的编程了。

如果你在添加引用过程遇到困难,请到这里需求帮助:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vbcon/html/vbtskaddingremovingreferences.asp

 

设置 HttpHandler

要使所有的东西都能顺利运行,第一步要在web.config里面设置组件的HttpHandler方式。没有必要详细地探索和什么是HttpHandlers以及它是如何工作的,只要知道它们是被用来处理Asp.Net请求的就足够了。例如,所有*.aspx页面请求都是由System.Web.UI.PageHandlerFactory这个类处理的。同样的我们将做所有对ajax/*.ashx 的请求都由Ajax.PageHandlerFactory 处理:

<configuration>

  <system.web>

    <httpHandlers>

      <add verb="POST,GET" path="ajax/*.ashx"
          
type="Ajax.PageHandlerFactory, Ajax" />

    </httpHandlers> 

    ...

  <system.web>

</configuration>

上述代码主要是告诉ASP.Net对于任何能比配指定路径(ajax/*.ash)的请求都将被Ajax.PageHandlerFactory处理,代替默认的处理工厂。你没有必要创建一个ajax子目录,这是一些被简单使用的虚拟目录,这样以便其它HttpHandlers用他们自己构建的的子目录使用ashx 扩展名的文件。

设置页面

现在我即将开始编码。创建一个新页面或者打开一个已经存在的页面的后台代码文件,在Page_Load事件中添加以下的代码:

 

'vb.net

Public Class Index

  Inherits System.Web.UI.Page

 

  Private Sub Page_Load(sender As Object, e As EventArgs) Handles MyBase.Load

    Ajax.Utility.RegisterTypeForAjax(GetType(Index))

  '...

  end sub

  '...

End Class

 

//C#

public class Index : System.Web.UI.Page{

   private void Page_Load(object sender, EventArgs e){

      Ajax.Utility.RegisterTypeForAjax(typeof(Index));     

      //...                                       

   }

   //... 

}

 

访问RegisterTypeForAjax要在页面里添加下述JavaScript代码(作为二者必须选择一个,所以你应当把以下两行代码都放到页面文件中去):

 

<script language="javascript" src="ajax/common.ashx"></script>

<script language="javascript"
          
src="ajax/NAMESPACE.PAGECLASS,ASSEMBLYNAME.ashx"></script>

粗体部分表示的意思如下:

NAMESPACE.PAGECLASS

当前页面的名字空间和类名(这样将很形象地表示继承了在@Page中指定属性的值

ASSEMBLYNAME

当前页面程序集的一部分(它将标识你的项目名称)

 

下面是一个在AjaxPlay项目中sample.aspx输出的实例:

<%@ Page Inherits="AjaxPlay.Sample" Codebehind="sample.aspx.cs" ... %>

<html>

 <head>

  <script language="javascript" src="ajax/common.ashx"></script>

  <script language="javascript"
         
src="ajax/AjaxPlay.Sample,AjaxPlay.ashx"></script>

 </head>

  <body>

    <form id="Form1" method="post" runat="server">
      
...

    </form> 

  </body>

</html>

你可以测试每个细节是否准确无误地工作用手动导航在你浏览器中src路径(查看源代码,拷贝和粘贴路径)。如果两个路径输出一些(表面上地)没有意义的文本,你已经成功实现测试。如果他们没有输出任何东西或者输出ASP.Net错误提示信息,那么证明有些配置是不正确的。

甚至你可以完全不知道HttpHandlers如何工作的,上述的内容也应当是可以理解的。通过Web.config,我们可以确认所有面向ajax/*.ashx的请求都通过用户方式来处理。显然,这两个脚本标签都将被用户处理方式处理。

服务器端方法编写

现在我们将创建一个服务器端函数,它将根据异步可用性被客户端访问。到现在为止不是所有的返回类型都能被正确地支持(不要担心,将来的版本修复那部分内容),我们将致力于改善简单ServerSideAdd的功能。在代码后置文件中,添加以下方法到你的页的类中:

 

'VB.Net

<Ajax.AjaxMethod()> _

Public Function ServerSideAdd (byval firstNumber As Integer, byval secondNumber
                                                 
As Integer) As Integer

 Return firstNumber + secondNumber

End Function

 

//C#

[Ajax.AjaxMethod()]

public int ServerSideAdd(int firstNumber, int secondNumber)

{

  return firstNumber + secondNumber;

}

标注函数具有Ajax.AjaxMethod属性设置。属性服务是用来告诉组件为这些方法创建JavaScript代理,使它们能被客户端访问。

编写客户端访问

最后一个步骤是使用JavaScript访问函数。Ajax组件创建一个被被叫做Sample.ServerSideAdd的方法,它由两个参数组成。对于最基本的功能,我们要做的一切就是访问方法和传递两个数字:

 

<%@ Page Inherits="AjaxPlay.Sample" Codebehind="sample.aspx.cs" ... %>

<html>

 <head>

  <script language="javascript" src="ajax/common.ashx"></script>

  <script language="javascript"
         
src="ajax/AjaxPlay.Sample,AjaxPlay.ashx"></script>

 </head>

  <body>

    <form id="Form1" method="post" runat="server">

      <script language="javascript">

        var response = Sample.ServerSideAdd(100,99);

        alert(response.value);

      </script>

    </form> 

  </body>

</html>

 

当然,我们想使用强大的功能而不是仅仅向用户发送简单的信号。这也是为什么所有的客户端代理(例如像Sample.ServerSideAdd 函数)同时也接受附加的属性。这些属性回叫被访问的函数,目的是处理响应。

 

Sample.ServerSideAdd(100,99, ServerSideAdd_CallBack);

function ServerSideAdd_CallBack(response){

 if (response.error != null){

   alert(response.error);

   return;

 }

 alert(response.value);

}

从上面的代码我们可以看出我们拥有一个指定的额外参数。ServerSideAdd_CallBack (同样显示了上述代码)是被用来处理从服务器响应的客户端函数。这个回叫函数接受了一个响应对象,他显示了出了四个主要的属性。

 

value

服务器端的实际返回值(它可能是一个字符串,一个用户对象或者是一个数据集)

error

一个所有可能的错误信息.

request

Xmlhttprequest原始响应.

context

背景对象.

 

首先我们检查error的值以确认是否有错误发生。你可以很简便地通过在服务器函数抛出异常的方式显示error属性。然后,在这个简单的案例中,我们应注重valueRequest属性可以用来获取额外的信息(参看下面的表格)。

了解更多的XmlHttprequest知识,请参看以下的链接:

http://www.quirksmode.org/blog/archives/2005/02/xmlhttp_linkdum.html

 

类型处理

Ajax组件能够处理更多的ServerSideAdd函数返回值而不仅仅只是整型数。它普遍支持整型,字符串型,双精度型,布尔型,时间日期类型,数据集和数据表,也支持用户类和队列中的简单类型。所有的其他类型都具有传化为字符串型的返回值。

返回数据集的操作和真正的.Net的数据集类似。给出一个服务器端返回数据集的函数,我们可以可以通过下面的方法在客户端显示起内容:

 

 

<script language="JavaScript">

//异步调用服务器端的"GetDataSet"方法

 

function getDataSet(){

 AjaxFunctions.GetDataSet(GetDataSet_callback);   

}

function GetDataSet_callback(response){

 var ds = response.value;

 if(ds != null && typeof(ds) == "object" && ds.Tables != null){

  var s = new Array();

  s[s.length] = "<table border=1>";

  for(var i=0; i<ds.Tables[0].Rows.length; i++){

   s[s.length] = "<tr>";

   s[s.length] = "<td>" + ds.Tables[0].Rows[i].FirstName + "</td>";

   s[s.length] = "<td>" + ds.Tables[0].Rows[i].Birthday + "</td>";

   s[s.length] = "</tr>";

  }

  s[s.length] = "</table>";

  tableDisplay.innerHTML = s.join("");

 }

 else{

  alert("Error. [3001] " + response.request.responseText);

 }

}

</script>

 

Ajax 同样可以返回用户类。所需要做的就是在类的上面用标明其Serializable属性,以下是类的实例:

 

[Serializable()]

public class User{

 private int _userId;

 private string _firstName;

 private string _lastName;

 

 public int userId{

  get { return _userId; }

 }

 public string FirstName{

  get { return _firstName; }

 }

 public string LastName{

  get { return _lastName; }

 }

 

 public User(int _userId, string _firstName, string _lastName){

  this._userId = _userId;

  this._firstName = _firstName;

  this._lastName = _lastName;

 }

 public User(){}

 

 [AjaxMethod()]

 public static User GetUser(int userId){

  //DB hit 或者其他的东西代替这部分:)

  return new User(userId,"Michael", "Schwarz");

 }

}

 

我们应当通过访问RegisterTypeForAjax注册一个GetUser代理:

 

private void Page_Load(object sender, EventArgs e){

 Utility.RegisterTypeForAjax(typeof(User));

}

允许我们能在诸如以下客户端代码异步地访问GetUser方法:

 

<script language="javascript">

function getUser(userId){

 User.GetUser(GetUser_callback);

}

function GetUser_callback(response){

 if (response != null && response.value != null){

  var user = response.value;

  if (typeof(user) == "object"){         

   alert(user.FirstName + " " + user.LastName);

  }

 }

}

getUser(1);

</script>

响应返回的值实际上一个揭示了相同属性的服务器端对象(FirstName, LastName UserId)。

用户转换器

正如我们看到的,Ajax .Net组件可以处理不同的.Net数据类型。尽管如此,除此之外,对于一些有害的.Net类和内建类型,组件用ToString()的方法将这些类型转换,这样它就不能准确地被返回。为了改变这种方案,Ajax .Net组件允许程序员创建对象转换器,用以使服务器端和客户端的复杂对象能稳定地进行通信。

这篇指南迅速地更新与用户转换器相关的附加信息(见谅)。

杂项

在其他类中注册方法

在上述例子中,我们的服务器端函数都存在与代码后置文件当中。但是,你没有理由不能将这些函数放在分开的类文件当中. 记住,组件工作的目的是为了寻找各种被Ajax.AjaxMethod标记的方法. 该类是经指定的第二个脚本标签. ajax.utility.registertypeforajax我们可以找到我们想要的任何一个类. 例如,它可以合理地保持服务器端函数,在一个分开的类中:

 

Public Class AjaxFunctions

 <Ajax.AjaxMethod()> _

 Public Function Validate(username As String, password As String) As Boolean

  'do something

  'Return something

 End Function

End Class

 

我们可以让Ajax组件为这个类创造一个委托指明这一类的类型,而非页:

 

'Vb.Net

Private Sub Page_Load(sender As Object, e As EventArgs) Handles MyBase.Load

 Ajax.Utility.RegisterTypeForAjax(GetType(AjaxFunctions))

 '...

End Sub

 

//C#

private void Page_Load(object sender, EventArgs e){

 Ajax.Utility.RegisterTypeForAjax(typeof(AjaxFunctions));

 //...

}

记住,客户端代理的名字有<类名>.<服务器端函数名称组成>。所以,如果我们的ServerSideAdd 函数位于虚构ajaxfunctions类之上,我们的客户端调用将是: ajaxfunctions.serversideadd ( 1,2 ) :

代理究竟是如何工作的

Ajax(或用你手动插入代替)创建的第二个脚本标签将传递名字空间, 类的名称和页面的集合. 由这些信息修饰后, ajax.pagehandlerfactory能够使用反射和获得相关的任何函数的细节,这些函数有它独特的属性. 显然,这种处理方式通过AjaxMethod属性查找函数,得到他们的签名(返回类型,名称, 和参数) ,因而能够创造必要的客户端代理. 具体来说,组件创建了一个名称和你的那些包含一些属性的类的名称相同的JavaScript对象. 换句话说,只要给出一个服务器端类ajaxfunctions与读取它Ajax方法的serversideadd , 我们应该需要一个名为ajaxfunctionJavaScript对象,它表示了serversideadd函数. 你可以看到这一点通过指明你的浏览路径的第二个脚本标签来实现.

返回统一字符

Ajax .Net组件能够从服务器返回统一编码字符到客户端,这些在被从服务器端返回的时候必须同html编码。例如:

[Ajax.AjaxMethod]

public string Test1(string name, string email, string comment){

 string html = "";

 html += "Hello " + name + "<br>";

 html += "Thank you for your comment <b>";

 html += System.Web.HttpUtility.HtmlEncode(comment);

 html += "</b>.";

 return html;

}

 

SessionState会话状态

也有可能你需要在服务器端的函数中访问会话. 为了达到这个目的,你需要简单地告诉服务器, 通过一个参数传递给ajax.ajaxmethod属性使这种功能.

在我们看到组件的会话操作功能的同时,让我们看看其他两个特点. 在这个例子中, 我们有一个文档管理系统,当它被一个用户编辑的时候,该文档将被锁定. 当这个文档可用的时候,其他用户可以申请访问它. 离开了Ajax,我们要等到页面回调才可以检查他或她队列中的文档可用. 这显然不够理想. 通过使用有会话状态的支持的Ajax,这是件相当容易的事.

首先我们将编写服务器端函数,它目的是为了循环用户所想编辑(被存储在会话当中)的文档并且返回所有释放的文件.

'Vb.Net

<Ajax.AjaxMethod(HttpSessionStateRequirement.Read)> _

Public Function DocumentReleased() As ArrayList

 If HttpContext.Current.Session("DocumentsWaiting") Is Nothing Then

  Return Nothing

 End If

 Dim readyDocuments As New ArrayList

 Dim documents() As Integer = CType(HttpContext.Current.Session("DocumentsWaiting"), Integer())

 For i As Integer = 0 To documents.Length - 1

  Dim document As Document = document.GetDocumentById(documents(i))

  If Not document Is Nothing AndAlso document.Status = DocumentStatus.Ready Then

   readyDocuments.Add(document)

  End If

 Next

 Return readyDocuments

End Function

 

//C#

[Ajax.AjaxMethod(HttpSessionStateRequirement.Read)]

public ArrayList DocumentReleased(){

 if (HttpContext.Current.Session["DocumentsWaiting"] == null){

  return null;

 }

 ArrayList readyDocuments = new ArrayList();

 int[] documents = (int[])HttpContext.Current.Session["DocumentsWaiting"];

 for (int i = 0; i < documents.Length; ++i){

  Document document = Document.GetDocumentById(documents[i]);

  if (document != null && document.Status == DocumentStatus.Ready){

    readyDocuments.Add(document);

  }       

 }

 return readyDocuments;

 }

}

 

注意我们需要指明HttpSessionStateRequirement.Read的值(是只读还是可读可写)

Now we write our JavaScript to take advantage of this method:

现在我编写JavaScript以突出这种方法的好处.

 

<script language="javascript">

function DocumentsReady_CallBack(response){

 if (response.error != null){

  alert(response.error);

  return;

 }

 if (response.value != null && response.value.length > 0){

  var div = document.getElementById("status");

  div.innerHTML = "The following documents are ready!<br />";

  for (var i = 0; i < response.value.length; ++i){

   div.innerHTML += "<a href=\"edit.aspx?documentId=" + response.value[i].DocumentId + "\">" + response.value[i].Name + "</a><br />";

  }      

 }

 setTimeout('page.DocumentReleased(DocumentsReady_CallBack)', 10000);

}

</script> 

<body onload="setTimeout('Document.DocumentReleased(DocumentsReady_CallBack)', 10000);">  

 

我们的服务器端的函数是在一旦页面加载的时候将被调用,然后每隔10秒钟后被再次调用. 该回调函数检查响应看看是否有值回, 若有,通过给用户一个层标签显示新的可以使用的文件.

结束语

Ajax技术已经提供了平滑且丰富的web接口,先前只为桌面开发所保留的东西。Ajax .Net组件能够让你轻松的享受由它的巨大力量带来的好处。敬请关注Ajax .Net组件和文档的开发。

 

通过网络随时关注AJAX .Net组件:

http://ajax.schwarz-interactive.de/

需要一个实际的例子,请参照以下的运用演示:

http://ajax.schwarz-interactive.de/download/ajaxsample.zip

 

 






原文

 

Background

Asynchronous JavaScript and XML (AJAX) has recently become the craze thanks, in no small part, to Google’s usage of it in Google Suggest as well as Google Maps.  In ASP.Net terms, AJAX allows server-side processing to occur without requiring postback, thus enabling clients (browsers) with rich server-side capabilities.  In other words it provides a framework for asynchronously dispatching and processing requests and responses from the server. AJAX leverages a number of existing technologies, which aren't particularly new, however fondness for what these technologies have to offer (collectively, they are AJAX) has recently soared.

 

Enter Michael Schwarz's AJAX .Net wrapper which allows ASP.Net developers to quickly and easily deploy pages capable of easily capitalizing on AJAX.  Users should be cautioned that the wrapper is early in development, and as such isn't fully matured. 

 

It should be pointed out that technologies such as AJAX are very likely going to lead to violations of layered architectures (N-Tier).  My opinion is that AJAX increases the likelihood that the presentation logic layer (or worse, the business layer) will leak into the presentation layer.  Strict architects, like me, might cringe at this notion.  I feel that even if AJAX is used in a manor which slightly violates layer boundaries, the payoffs are well worth it. Of course, that's something you'll need to look at for your specific project and environment.

 

To learn more about AJAX, visit:
http://en.wikipedia.org/wiki/AJAX
http://www.adaptivepath.com/publications/essays/archives/000385.php

 

How it works - Overview

AJAX relies on a broker to dispatch and process requests to and from the server.  For this task, the .Net wrapper relies on the client-side XmlHttpRequest object. The XmlHttpRequest object is well supported by most browsers, making it the solution of choice.  Since the goal of the wrapper is to hide the implementation of XmlHttpRequest, we'll forgo any detailed discussion about it.

 

The wrapper itself works by marking .Net functions as AJAX methods.  Once marked, AJAX creates corresponding JavaScript functions which can be called client-side (liky any JavaScript functions) and that serve as proxies, using XmlHttpRequest.  These proxies map back to the server-side function.

 

Complicated?  It isn't.  Let's look at a simplified example.  Given the .Net function:

 

'VB.Net

public function Add(firstNumber as integer, secondNumber as integer) as integer

  return firstNumber + secondNumber

end sub

 

//C#

public int Add(int firstNumber, int secondNumber)

{

   return firstNumber + secondNumber;

}

 

The AJAX .Net wrapper will automatically create a JavaScript function called "Add" which takes two parameters.  When this function is called using JavaScript (on the client), the request will be passed to the server and the return value back to the client. 

Initial Setup

We'll first go through the steps of "installing" the .dll for use in your project.  If you know how to add a reference to a .dll file, skip this section.

 

First, if you don’t already have it, download the latest version of AJAX.  Unzip the downloaded file and place the ajax.dll within a ref folder of your project.  In Visual Studio.Net, right click the "References" node in the Solution Explorer and select Add Reference.  In the newly opened dialog, click Browse and navigate to the ref/ajax.dll file.  Click Open followed by Ok.  You are now ready to start programming with the AJAX .Net wrapper.

 

If you’re having difficulty setting up the reference, check out:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vbcon/html/vbtskaddingremovingreferences.asp

 

Setting up the HttpHandler

The first step necessary to make everything work is to set up the wrapper's HttpHandler in the web.config.  Without going into detailed explanation of what and how HttpHandlers work, it's sufficient to know that that they are used to process ASP.Net requests.  For example, all requests for *.aspx pages are handled by the System.Web.UI.PageHandlerFactory class.  Similarly we'll make all requests to ajax/*.ashx handled by the Ajax.PageHandlerFactory:

 

<configuration>

  <system.web>

    <httpHandlers>

      <add verb="POST,GET" path="ajax/*.ashx"
          
type="Ajax.PageHandlerFactory, Ajax" />

    </httpHandlers> 

    ...

  <system.web>

</configuration>

 

The above code basically tells ASP.Net that any requests made that match the specified path (ajax/*.ashx) are to be handled by Ajax.PageHandlerFactory, instead of the default handler factory.  You don’t need to create an ajax subdirectory, this is a mythical directory simply used so that other HttpHandlers can use the .ashx extension with their own made-up subdirectories.

 

Setting up the Page

We are now ready to start coding.  Create a new page, or open an existing one and in the code behind file, add the following code in the Page_Load event:

 

'vb.net

Public Class Index

  Inherits System.Web.UI.Page

 

  Private Sub Page_Load(sender As Object, e As EventArgs) Handles MyBase.Load

    Ajax.Utility.RegisterTypeForAjax(GetType(Index))

  '...

  end sub

  '...

End Class

 

//C#

public class Index : System.Web.UI.Page{

   private void Page_Load(object sender, EventArgs e){

      Ajax.Utility.RegisterTypeForAjax(typeof(Index));     

      //...                                       

   }

   //... 

}

 

The call to RegisterTypeForAjax emits the following JavaScript on the page (alternatively, you could manually place the following two lines on the page):

 

<script language="javascript" src="ajax/common.ashx"></script>

<script language="javascript"
          
src="ajax/NAMESPACE.PAGECLASS,ASSEMBLYNAME.ashx"></script>

 

Where the bolded parts have the following meaning:

NAMESPACE.PAGECLASS

The namespace and class of the current page

(this will typically be the value of the Inherits attribute in the @Page directive)

ASSEMBLYNAME

The name of the assembly the current page is part of

(this will typically be the name of your project)

 

Bellow is a sample output for the sample.aspx page in an AjaxPlay project:

 

<%@ Page Inherits="AjaxPlay.Sample" Codebehind="sample.aspx.cs" ... %>

<html>

 <head>

  <script language="javascript" src="ajax/common.ashx"></script>

  <script language="javascript"
         
src="ajax/AjaxPlay.Sample,AjaxPlay.ashx"></script>

 </head>

  <body>

    <form id="Form1" method="post" runat="server">
      
...

    </form> 

  </body>

</html>

 

 

You can test that everything is working properly by manually navigating to the src paths in your browser (view source and copy and paste the paths). If both paths output some (seemingly) meaningless text, you’ve done well so far.  If they’re outputting nothing or ASP.Net errors, something was improperly done.

 

Even if you don’t know how HttpHandlers work, the above should be understandable.  Via the web.config, we’ve made sure that all requests that go to ajax/*.ashx are processed by the custom handler.  Obviously, the two script tags will be processed by this custom handler.

 

Creating the Server-Side Functions

We’ll now create a server-side function that’ll be asynchronously be available from a client-side call.  Since not all return types are currently supported (don’t worry, upcoming versions will build on what’s currently there), we’ll stick with our simple ServerSideAdd functionality.  In the code behind file, add the following method to your page class:

 

'VB.Net

<Ajax.AjaxMethod()> _

Public Function ServerSideAdd (byval firstNumber As Integer, byval secondNumber
                                                
As Integer) As Integer

 Return firstNumber + secondNumber

End Function

 

//C#

[Ajax.AjaxMethod()]

public int ServerSideAdd(int firstNumber, int secondNumber)

{

  return firstNumber + secondNumber;

}

 

Note that the functions have the Ajax.AjaxMethod attribute set.  The attribute serves to tell that wrapper to create JavaScript proxies for these methods so that they might be called client-side.

Making our client-side call

The last step is to call the function using JavaScript. The AJAX wrapper took care of creating a JavaScript function called Sample.ServerSideAdd function which takes two parameters.  For the most basic functionality, all we need to do is call the method and pass two numbers:

 

<%@ Page Inherits="AjaxPlay.Sample" Codebehind="sample.aspx.cs" ... %>

<html>

 <head>

  <script language="javascript" src="ajax/common.ashx"></script>

  <script language="javascript"
         
src="ajax/AjaxPlay.Sample,AjaxPlay.ashx"></script>

 </head>

  <body>

    <form id="Form1" method="post" runat="server">

      <script language="javascript">

        var response = Sample.ServerSideAdd(100,99);

        alert(response.value);

      </script>

    </form> 

  </body>

</html>

 

Of course, we’ll want to use this powerful capability for more than simply alerting the user.  That’s why all client-side proxies (such as the JavaScript Sample.ServerSideAdd function), also accept an additional property.  The property is the callback function called in order to process the response:

 

Sample.ServerSideAdd(100,99, ServerSideAdd_CallBack);

function ServerSideAdd_CallBack(response){

 if (response.error != null){

   alert(response.error);

   return;

 }

 alert(response.value);

}

 

We can see from the above code that we’ve specified an extra parameter.  ServerSideAdd_CallBack (also shown above) is the client-side function used to process the response from the server.   The callback function receives a response object which exposes three key properties:

 

value

The actual return value (be it a string, custom object or dataset) of the server-side function.

error

An error message, if any.

request

The raw response from the xml http request.

context

A context object.

 

 

First we check the error value to see if any errors occurred.  You can easily play with the error property by throwing an exception in the server-side function.  Then, in this simplified case, we alert the value.  The request property can be used to get additional information (see box bellow).

 

To learn more about the Xml Http request, check out the following link dump:

http://www.quirksmode.org/blog/archives/2005/02/xmlhttp_linkdum.html

 

Dealing with Types

Returning complex types

The Ajax wrapper is capable of handling more than just the integer our ServerSideAdd function returned.  It currently supports integers, strings, double, booleans, DateTime, DataSets and DataTables, as well as the primitive types of custom classes and arrays.  All other types have their ToString values returned.

 

Returned DataSets work much like real .Net DataSet.  Given a server side function which returns a DataSet, we could display the contents client side via:

 

<script language="JavaScript">

//Asynchronous call to the mythical "GetDataSet" server-side function

function getDataSet(){

 AjaxFunctions.GetDataSet(GetDataSet_callback);   

}

function GetDataSet_callback(response){

 var ds = response.value;

 if(ds != null && typeof(ds) == "object" && ds.Tables != null){

  var s = new Array();

  s[s.length] = "<table border=1>";

  for(var i=0; i<ds.Tables[0].Rows.length; i++){

   s[s.length] = "<tr>";

   s[s.length] = "<td>" + ds.Tables[0].Rows[i].FirstName + "</td>";

   s[s.length] = "<td>" + ds.Tables[0].Rows[i].Birthday + "</td>";

   s[s.length] = "</tr>";

  }

  s[s.length] = "</table>";

  tableDisplay.innerHTML = s.join("");

 }

 else{

  alert("Error. [3001] " + response.request.responseText);

 }

}

</script>

 

 

Ajax can also return custom classes. All that is required is that the class be marked with the Serializable attribute.  Given the following class:

 

[Serializable()]

public class User{

 private int _userId;

 private string _firstName;

 private string _lastName;

 

 public int userId{

  get { return _userId; }

 }

 public string FirstName{

  get { return _firstName; }

 }

 public string LastName{

  get { return _lastName; }

 }

 

 public User(int _userId, string _firstName, string _lastName){

  this._userId = _userId;

  this._firstName = _firstName;

  this._lastName = _lastName;

 }

 public User(){}

 

 [AjaxMethod()]

 public static User GetUser(int userId){

  //Replace this with a DB hit or something :)

  return new User(userId,"Michael", "Schwarz");

 }

}

 

We would register the GetUser proxy via a call to the RegisterTypeForAjax:

 

private void Page_Load(object sender, EventArgs e){

 Utility.RegisterTypeForAjax(typeof(User));

}

 

Allowing us to asynchronously call the GetUser in client-side code with code such as:

 

 

<script language="javascript">

function getUser(userId){

 User.GetUser(GetUser_callback);

}

function GetUser_callback(response){

 if (response != null && response.value != null){

  var user = response.value;

  if (typeof(user) == "object"){         

   alert(user.FirstName + " " + user.LastName);

  }

 }

}

getUser(1);

</script>

 

The value returned in the response is actually an object which exposes the same properties as a server-side object (FirstName, LastName and UserId).

 

Custom Converters

As we’ve seen, the Ajax .Net wrapper is able to deal with many different .Net types.  However, aside from a handful of .Net classes and the built-in types, the wrapper simply calls ToString() on anything it can’t properly return.  To get around this, the Ajax .Net wrapper allows developers to create object converters which can be used to smoothly communicate complex objects between the server and the client. 

 

This guide will be updated with additional information on custom converters shortly (sorry).

 

Miscellaneous

Registering functions in another class

In the above example, our server-side functions resided within the code behind of the executing page.  However, there’s no reason why these functions can’t be in a separate class file.  Remember, the way the wrapper works is to find all methods within the specified class that have the Ajax.AjaxMethod. The class in question is specified via the second script tag.  Using Ajax.Utility.RegisterTypeForAjax we can specify any class we want.  For example, it would be reasonable to keep our server-side functions in a separate class:

 

Public Class AjaxFunctions

 <Ajax.AjaxMethod()> _

 Public Function Validate(username As String, password As String) As Boolean

  'do something

  'Return something

 End Function

End Class

 

We could have the Ajax wrapper create proxies for this class by specifying this class’s type instead of the pages:

'Vb.Net

Private Sub Page_Load(sender As Object, e As EventArgs) Handles MyBase.Load

 Ajax.Utility.RegisterTypeForAjax(GetType(AjaxFunctions))

 '...

End Sub

 

//C#

private void Page_Load(object sender, EventArgs e){

 Ajax.Utility.RegisterTypeForAjax(typeof(AjaxFunctions));

 //...

}

 

Remember, the client-side proxy takes the name of <ClassName>.<ServerSideFunctionName>.  Therefore, if our ServerSideAdd function was located in the fictional AjaxFunctions class above, our client-side call would be: AjaxFunctions.ServerSideAdd(1,2)

How the proxy really works

The second script tag generated by the Ajax utility (or manually inserted by you) passes the namespace, class name and assembly of the page.  Armed with this information, the Ajax.PageHandlerFactory is able to use reflection and get the details about any functions which have a certain attribute.  Obviously, the handler looks for functions with the AjaxMethod attribute, gets their signature (return type, name, and parameters) and is thus able to create the necessary client-side proxy.  Specifically, the wrapper creates a JavaScript object named the same name as your class which exposes the proxy.  In other words, given a server-side class AjaxFunctions with an Ajax method ServerSideAdd, we should expect a JavaScript object named AjaxFunction which exposes a ServerSideAdd function.  You can see this in action by pointing your browser to the path of the second script tag.

Returning Unicode characters

Ajax .Net wrapper is able to return Unicode characters from the server to the client.  To do so, values must be html encoded on the server before being returned.  For example:

 

[Ajax.AjaxMethod]

public string Test1(string name, string email, string comment){

 string html = "";

 html += "Hello " + name + "<br>";

 html += "Thank you for your comment <b>";

 html += System.Web.HttpUtility.HtmlEncode(comment);

 html += "</b>.";

 return html;

}

 

SessionState

It’s likely that you’ll need to access session information in your server side function.  To do so, you must simply tell Ajax to enable such functionality via a parameter passed to the Ajax.AjaxMethod attribute. 

 

While looking at the session capabilities of the wrapper, let’s look at a couple other features.  In this example, we have a document management system which puts a lock on a document while a user is editing it.  Other users can request to be notified when the document because available.   Without AJAX, we’d need to wait until the user posted back in order to check if his or her queued documents were available.  This is obviously not ideal.  Using Ajax with session state support, this is quite easy.

 

First we’ll write our server side function, the goal of which is to loop through the documentIds the user wishes to edit (stored in a session) and return all released documents.

 

 

'Vb.Net

<Ajax.AjaxMethod(HttpSessionStateRequirement.Read)> _

Public Function DocumentReleased() As ArrayList

 If HttpContext.Current.Session("DocumentsWaiting") Is Nothing Then

  Return Nothing

 End If

 Dim readyDocuments As New ArrayList

 Dim documents() As Integer = CType(HttpContext.Current.Session("DocumentsWaiting"), Integer())

 For i As Integer = 0 To documents.Length - 1

  Dim document As Document = document.GetDocumentById(documents(i))

  If Not document Is Nothing AndAlso document.Status = DocumentStatus.Ready Then

   readyDocuments.Add(document)

  End If

 Next

 Return readyDocuments

End Function

 

 

//C#

[Ajax.AjaxMethod(HttpSessionStateRequirement.Read)]

public ArrayList DocumentReleased(){

 if (HttpContext.Current.Session["DocumentsWaiting"] == null){

  return null;

 }

 ArrayList readyDocuments = new ArrayList();

 int[] documents = (int[])HttpContext.Current.Session["DocumentsWaiting"];

 for (int i = 0; i < documents.Length; ++i){

  Document document = Document.GetDocumentById(documents[i]);

  if (document != null && document.Status == DocumentStatus.Ready){

    readyDocuments.Add(document);

  }       

 }

 return readyDocuments;

 }

}

 

Notice that we specify the HttpSessionStateRequirement.Read value (alternatives being Write and ReadWrite).

 

Now we write our JavaScript to take advantage of this method:

 

<script language="javascript">

function DocumentsReady_CallBack(response){

 if (response.error != null){

  alert(response.error);

  return;

 }

 if (response.value != null && response.value.length > 0){

  var div = document.getElementById("status");

  div.innerHTML = "The following documents are ready!<br />";

  for (var i = 0; i < response.value.length; ++i){

   div.innerHTML += "<a href=\"edit.aspx?documentId=" + response.value[i].DocumentId + "\">" + response.value[i].Name + "</a><br />";

  }      

 }

 setTimeout('page.DocumentReleased(DocumentsReady_CallBack)', 10000);

}

</script> 

<body onload="setTimeout('Document.DocumentReleased(DocumentsReady_CallBack)', 10000);">  

 

Our server side function is called once on page load and subsequently every 10 seconds.  The call back function checks the response to see if any values were returned, and if so displays the newly available documents to the user in a div tag.

 

Conclusion

AJAX technology has already lead to sleek and rich web interfaces, previously reserved for desktop development.  The Ajax .Net wrapper allows you to easily take advantage of this new power.  Note that both the Ajax .Net wrapper and the documentation are under development. 

 

Keep a close eye on the AJAX .Net wrapper website:

http://ajax.schwarz-interactive.de/

For a good hands-on sample, check out the following demo application:

http://ajax.schwarz-interactive.de/download/ajaxsample.zip

 


 

Copyright © 2024 Ekin.S.Sun
Powered by .NET 8.0 on Kubernetes