关于调用AjaxPro.AjaxHandlerFactory,AjaxPro.2的问题



http://www.cnblogs.com/Hedonister/

冰戈--真诚的平凡

二是AJAX.net本身就自带,如果细节化,建议用jqueryajax,也很简单。

好早就想分析下AjaxPro的代码实现机制了,一直苦于没时间,现在嘛总算有那么丁点了,开篇了,慢慢分析……

以一个最简单的例子开始:
点击一个客户端button,触发一个javascript函数,执行一个只有一个string参数的服务端方法,返回一个处理过的string,处理方法是将传入的string变成“Hi”+string +“;够简单了,为了是不希望罗嗦的代码影响简单的分析;
所有代码如下:

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Test.aspx.cs" Inherits="Test" %>

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

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    
<title>无标题页</title>
    
<script type="text/javascript">
     
    
function doTest() 
    
{
        AJAXDemo.Examples.Test.TestMethod.GetTest("AjaxPro",doTest_callback);
    }

    
function doTest_callback(res) {
        alert(res.value);
    }
    
    
</script>
</head>
<body>
    
<form id="form1" runat="server">
    
<div>
        
<input id="Button1" type="button" onclick="doTest()" value="测试"/></div>
    
</form>
</body>
</html>


Test.aspx.cs

public partial class Test : System.Web.UI.Page
{
    
protected void Page_Load(object sender, EventArgs e)
    
{
        Utility.RegisterTypeForAjax(
typeof(AJAXDemo.Examples.Test.TestMethod));
    }
}


AJAXDemo.Examples.Test

using System;
using AjaxPro;

namespace AJAXDemo.Examples.Test
{
    
public class TestMethod
    
{
        
public TestMethod()
        
{}

        [AjaxMethod]
        
public string GetTest(string testText)
        
{
            
return "Hi," + testText + "!";
        }
    }
}


1.
首先我们看AjaxPro在页面上给我们生成了什么?
Test[1]

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

<html xmlns="http://www.w3.org/1999/xhtml" >
<head><title>
    
无标题页
</title>
    
<script type="text/javascript">
     
    
function doTest() 
    
{
        AJAXDemo.Examples.Test.TestMethod.GetTest("AjaxPro",doTest_callback);
    }

    
function doTest_callback(res) {
        alert(res.value);
    }
    
    
</script>
</head>
<body>
    
<form name="form1" method="post" action="Test.aspx" id="form1">
<div>
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUJNzgzNDMwNTMzZGRFekXifzWDNb+qFWPbJumdlZh/dQ==" />
</div>

<script type="text/javascript" src="/AJAXDemo.2/ajaxpro/prototype.ashx"></script>
<script type="text/javascript" src="/AJAXDemo.2/ajaxpro/core.ashx"></script>
<script type="text/javascript" src="/AJAXDemo.2/ajaxpro/converter.ashx"></script>
<script type="text/javascript" src="/AJAXDemo.2/ajaxpro/AJAXDemo.Examples.Test.TestMethod,App_Code.un7rskvh.ashx"></script>

    
<div>
        
<input id="Button1" type="button" onclick="doTest()" value="测试"/></div>
    
</form>
</body>
</html>


一定要注意这几行

<script type="text/javascript" src="/AJAXDemo.2/ajaxpro/prototype.ashx"></script>
<script type="text/javascript" src="/AJAXDemo.2/ajaxpro/core.ashx"></script>
<script type="text/javascript" src="/AJAXDemo.2/ajaxpro/converter.ashx"></script>
<script type="text/javascript" src="/AJAXDemo.2/ajaxpro/AJAXDemo.Examples.Test.TestMethod,App_Code.urx4hqkg.ashx"></script>


通过使用http://localhost:3578/AJAXDemo.2/ajaxpro/prototype.ashxhttp://localhost:3578/AJAXDemo.2/ajaxpro/core.ashx不难发现,其中前面两个是源代码中带的两个js文件(core.jsprototype.js)转化出来的,基本内容也跟原来的文件一样,而converter.ashxAJAXDemo.Examples.Test.TestMethod,App_Code.urx4hqkg.ashx里面有什么呢?看下面:
AJAXDemo.Examples.Test.TestMethod,App_Code.urx4hqkg.ashx

addNamespace("AJAXDemo.Examples.Test");
AJAXDemo.Examples.Test.TestMethod_class = Class.create();
AJAXDemo.Examples.Test.TestMethod_class.prototype = (
new AjaxPro.AjaxClass()).extend({
    GetTest: 
function(testText) {
        
return this.invoke("GetTest", {"testText":testText}, this.GetTest.getArguments().slice(1));
    },
    initialize: 
function() {
        
this.url = '/AJAXDemo.2/ajaxpro/AJAXDemo.Examples.Test.TestMethod,App_Code.un7rskvh.ashx';
    }
});
AJAXDemo.Examples.Test.TestMethod = 
new AJAXDemo.Examples.Test.TestMethod_class();


converter.ashx

 

addNamespace("Ajax.Web");

Ajax.Web.NameValueCollection = 
function()
{
    
this.__type = "System.Collections.Specialized.NameValueCollection";

    
this.add = function(key, value) {
        
if(this[key] == null{
            
this[key] = value;
        }
    }
    
    
this.getKeys = function() {
        
var keys = [];
        
        
for(key in this)
            
if(typeof this[key] != "function")
                keys.push(key);
            
        
return keys;
    }
    
    
this.getValue = function(key) {
        
return this[key];
    }
    
    
this.toJSON = function() {
        
var o = this;
        o.toJSON = 
null;
        
delete o.toJSON;
        
return AjaxPro.toJSON(o);
    }
}




addNamespace("Ajax.Web");

Ajax.Web.DataTable = 
function(columns, rows) {

    
this.__type = "System.Data.DataTable, System.Data";
    
this.Columns = new Array();
    
this.Rows = new Array();

    
this.addColumn = function(name, type) {
        
var c = new Object();
        c.Name = name;
        c.__type = type;
        
        
this.Columns.push(c);
    }

    
this.toJSON = function() {
        
var dt = new Object();

        dt.Columns = [];
        
for(var i=0; i<this.Columns.length; i++)
            dt.Columns.push([
this.Columns[i].Name, this.Columns[i].__type]);

        dt.Rows = [];
        
for(var i=0; i<this.Rows.length; i++) {
            
var row = [];
            
for(var j=0; j<this.Columns.length; j++)
                row.push(
this.Rows[i][this.Columns[j].Name]);
            dt.Rows.push(row);
        }

        
return AjaxPro.toJSON(dt);
    }

    
this.addRow = function(row) {
        
this.Rows.push(row);
    }

    
if(columns != null{
        
for(var i=0; i<columns.length; i++) {
            
this.addColumn(columns[i][0], columns[i][1]);
        }
    }

    
if(rows != null{
        
for(var i=0; i<rows.length; i++) {
            
var row = new Object();
            
for(var c=0; c<this.Columns.length && c<rows[i].length; c++) {
                row[
this.Columns[c].Name] = rows[i][c];
            }
            
this.addRow(row);
        }
    }
}


addNamespace("Ajax.Web");

Ajax.Web.DataSet = 
function(tables) {
    
this.__type = "System.Data.DataSet, System.Data";
    
this.Tables = new Array();

    
this.addTable = function(table) {
        
this.Tables.push(table);
    }

    
if(tables != null{
        
for(var i=0; i<tables.length; i++) {
            
this.addTable(tables[i]);
        }
    }
}







function Person(id) {
    
this.FirstName = "";
    
this.FamilyName = "";
    
this.Age = 0;
    
this.ID = id;
    
this.__type = 'AJAXDemo.Examples.Classes.Person, App_Code.un7rskvh, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null';
}

Person.prototype.get_FullName = 
function() {
    
return this.FirstName + " " + this.FamilyName;
}

Person.prototype.toJSON = 
function() {
    
var o = new Object();

    o.firstName = 
this.FirstName;
    o.familyName = 
this.FamilyName;
    o.age = 
this.Age;
    o.id = 
this.ID;

    
return AjaxPro.toJSON(o);
}

Person.prototype.save = 
function() {
    
return Person.save(this);
}

Person.save = 
function(p) {
    
var ps = new PersonSaver();
    
return ps.savePerson(p);    // synchronous call
}

var PersonSaver = Class.create();
PersonSaver.prototype = (
new AjaxPro.Request()).extend({
    savePerson: 
function(p) {
        
return this.invoke("SavePerson", {"p":p}).value;
    },
    initialize: 
function() {
        
this.url = "ajaxpro/AJAXDemo.Examples.Classes.Person, App_Code.un7rskvh, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null.ashx";
    }
})


正因为是有了上面四个ashx文件我们的
function doTest() 
{
   AJAXDemo.Examples.Test.TestMethod.GetTest("AjaxPro",doTest_callback);
}
才得以异步执行,这些ashx文件又是怎么生成到页面上的,那得归功于web.config的相关配置和下面这句代码:
Utility.RegisterTypeForAjax(
typeof(AJAXDemo.Examples.Test.TestMethod));

至于Utility.RegisterTypeForAjax方法产生的一序列动作我将在后文中继续说明,有兴趣的可以自己跟踪下这些代码的执行。
[
未完待续]

 

上篇 说到AjaxPro为我们生成了四个ashx文件来达到异步调用的目的,这次呢,就说说这些ashx文件是怎么生成的;

核心部分是其web.config配置了httpHandlers 元素

<httpHandlers>
      <add verb="POST,GET" path="ajaxpro/*.ashx" type="AjaxPro.AjaxHandlerFactory, AjaxPro"/>
</httpHandlers>

作用是:对ajaxpro目录下的*.ashx文件的POST,GET请求交由程序集AjaxPro下的AjaxPro.AjaxHandlerFactory类来处理;

ps:关于httpHandlers的说明可以参看下面几个连接
1.httpHandlers 元素(ASP.NET 设置架构)
2.MicrosoftASP.NET 快速入门教程
3.ASP.NET中的Http Handles

先看看实现IHttpHandlerFactory接口的AjaxHandlerFactory类,只实现了GetHandler方法

其中对coreprototype的处理没什么可说,只是简单的通过EmbeddedJavaScriptHandlerProcessRequest中将js写入.ashx文件,而converter比较复杂留代下次再做详细分析;这里就重点看看AJAXDemo.Examples.Test.TestMethod,App_Code.urx4hqkg.ashx是怎么来的了;

我跟踪了下代码的运行,在页面加载的时候自定义 HttpHandler 启用 HTTP Web 请求的处理ProcessRequest,也就是进入AjaxHandlerFactoryGetHandler方法的如下入口

default:

                            
if(Utility.Settings.UrlNamespaceMappings.Contains(filename))
                                t = Type.GetType(Utility.Settings.UrlNamespaceMappings[filename].ToString());

                            
if(t == null)
                                t = Type.GetType(filename);

                            
return new TypeJavaScriptHandler(t);


TypeJavaScriptHandler也实现了IHttpHandler接口,其ProcessRequest方法如下:

public void ProcessRequest(HttpContext context)
        {
            
// The request was not a request to invoke a server-side method.
            // Now, we will render the Javascript that will be used on the
            // client to run as a proxy or wrapper for the methods marked
            // with the AjaxMethodAttribute.

            
if(context.Trace.IsEnabled) context.Trace.Write(Constant.AjaxID, "Render class proxy Javascript");
        
            System.Reflection.MethodInfo[] mi = type.GetMethods();
            
            
// Check wether the javascript is already rendered and cached in the
            // current context.
            
            
string etag = context.Request.Headers["If-None-Match"];
            
string modSince = context.Request.Headers["If-Modified-Since"];
            
string path = type.FullName + "," + type.Assembly.FullName.Split(',')[0];

            
if(Utility.Settings != null && Utility.Settings.UrlNamespaceMappings.ContainsValue(path))
            {
                
foreach(string key in Utility.Settings.UrlNamespaceMappings.Keys)
                {
                    
if(Utility.Settings.UrlNamespaceMappings[key].ToString() == path)
                    {
                        path = key;
                        
break;
                    }
                }
            }

            
if(context.Cache[path] != null)
            {
                CacheInfo ci = (CacheInfo)context.Cache[path];

                
if(etag != null)
                {
                    
if(etag == ci.ETag)        // TODO: null check
                    {
                        context.Response.StatusCode = 304;
                        
return;
                    }
                }
                
                
if(modSince != null)
                {
                    
try
                    {
                        DateTime modSinced = Convert.ToDateTime(modSince.ToString()).ToUniversalTime();
                        
if(DateTime.Compare(modSinced, ci.LastModified.ToUniversalTime()) >= 0)
                        {
                            context.Response.StatusCode = 304;
                            
return;
                        }
                    }
                    
catch(Exception)
                    {
                        
if(context.Trace.IsEnabled) context.Trace.Write(Constant.AjaxID, "The header value for If-Modified-Since = " + modSince + " could not be converted to a System.DateTime.");
                    }
                }
            }

            etag = type.AssemblyQualifiedName; 
// + "_" + type.Assembly. DateTime.Now.ToString(System.Globalization.DateTimeFormatInfo.CurrentInfo.SortableDateTimePattern);
            etag = MD5Helper.GetHash(System.Text.Encoding.Default.GetBytes(etag));

            DateTime now = DateTime.Now;
            DateTime lastMod = 
new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second); // .ToUniversalTime();

            context.Response.AddHeader("Content-Type", "application/x-javascript");
            context.Response.ContentEncoding = System.Text.Encoding.UTF8;
            context.Response.Cache.SetCacheability(System.Web.HttpCacheability.Public);
            context.Response.Cache.SetETag(etag);
            context.Response.Cache.SetLastModified(lastMod);

            
// Ok, we do not have the javascript rendered, yet.
            // Build the javascript source and save it to the current
            // Application context.

            System.Text.StringBuilder sb = 
new System.Text.StringBuilder();


            AjaxNamespaceAttribute[] cma = (AjaxNamespaceAttribute[])type.GetCustomAttributes(
typeof(AjaxNamespaceAttribute), true);
            
string clientNS = type.FullName;

            
if(cma.Length > 0 && cma[0].ClientNamespace != null)
            {
                
if(cma[0].ClientNamespace.IndexOf(".") > 0)
                    sb.Append("addNamespace(\"" + cma[0].ClientNamespace + "\");\r\n");

                clientNS = cma[0].ClientNamespace;
            }
            
else
            {
                sb.Append("addNamespace(\"" + (type.FullName.IndexOf(".") > 0 ? type.FullName.Substring(0, type.FullName.LastIndexOf(".")) : type.FullName) + "\");\r\n");
            }

            sb.Append(clientNS);
            sb.Append("_class = Class.create();\r\n");

            sb.Append(clientNS);
            sb.Append("_class.prototype = (new AjaxPro.AjaxClass()).extend({\r\n");

            System.Reflection.MethodInfo method;

            
for(int y=0; y<mi.Length; y++)
            {
                method = mi[y];

                
if(!method.IsPublic)
                    
continue;

                AjaxNamespaceAttribute[] cmam = (AjaxNamespaceAttribute[])method.GetCustomAttributes(
typeof(AjaxNamespaceAttribute), true);
                AjaxMethodAttribute[] ma = (AjaxMethodAttribute[])method.GetCustomAttributes(
typeof(AjaxMethodAttribute), true);

                
if(ma.Length == 0)
                    
continue;

                System.Reflection.ParameterInfo[] pi = method.GetParameters();

                
// Render the function header

                sb.Append("\t");

                
if(cmam.Length == 0)
                    sb.Append(method.Name);
                
else
                    sb.Append(cmam[0].ClientNamespace);

                sb.Append(": function(");


                
// Render all parameters

                
for(int i=0; i<pi.Length; i++)
                {
                    sb.Append(pi[i].Name);

                    
if(i<pi.Length -1)
                        sb.Append(", ");
                }
    
                sb.Append(") {\r\n");


                
// Create the XMLHttpRequest object

                sb.Append("\t\treturn this.invoke(\"" + method.Name + "\", {");        
// must be the original method name

                
for(int i=0; i<pi.Length; i++)
                {
                    sb.Append("\"");
                    sb.Append(pi[i].Name);
                    sb.Append("\":");
                    sb.Append(pi[i].Name);

                    
if(i<pi.Length -1)
                        sb.Append(", ");
                }

                sb.Append("}, this.");
                
                
if(cmam.Length == 0)
                    sb.Append(method.Name);
                
else
                    sb.Append(cmam[0].ClientNamespace);

                sb.Append(".getArguments().slice(");
                sb.Append(pi.Length.ToString());
                sb.Append("));\r\n\t},\r\n");
            }

            
string url = context.Request.ApplicationPath + (context.Request.ApplicationPath.EndsWith("/") ? "" : "/") + Utility.HandlerPath + "/" + (context.Session != null && context.Session.IsCookieless ? "(" + context.Session.SessionID + ")/" : "") + path + Utility.HandlerExtension;

            sb.Append("\tinitialize: function() {\r\n\t\tthis.url = '" + url + "';\r\n\t}\r\n");
            sb.Append("});\r\n");


            sb.Append(clientNS);
            sb.Append(" = new ");
            sb.Append(clientNS);
            sb.Append("_class();\r\n");


            
// save the javascript in current Application context to
            // speed up the next requests.

            // TODO: was knen wir hier machen??
            // System.Web.Caching.CacheDependency fileDepend = new System.Web.Caching.CacheDependency(type.Assembly.Location);

            context.Cache.Add(path, 
new CacheInfo(etag, lastMod), null
                System.Web.Caching.Cache.NoAbsoluteExpiration, System.Web.Caching.Cache.NoSlidingExpiration,
                System.Web.Caching.CacheItemPriority.Normal, 
null);

            context.Response.Write(sb.ToString());
            context.Response.Write("\r\n");

            
            
if(context.Trace.IsEnabled) context.Trace.Write(Constant.AjaxID, "End ProcessRequest");
        }


代码很明了,只是注释少了点,可能是作者认为没什么难理解的吧,感兴趣的可以到http://www.ajaxpro.info/找源代码跟踪一下,具体的运行过程,可能就更明白是怎么回事了,下篇该来说说这些ashx是用来干什么的了!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值