【AjaxPro实现机制浅析二】*.ashx文件是怎么来的?

上篇 说到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.Microsoft ASP.NET 快速入门教程
3.ASP.NET中的Http Handles

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

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

我跟踪了下代码的运行,在页面加载的时候自定义 HttpHandler 启用 HTTP Web 请求的处理ProcessRequest,也就是进入AjaxHandlerFactory类GetHandler方法的如下入口
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是用来干什么的了!
posted @ 2006-03-21 14:05  冰戈  阅读(5821)  评论(2编辑  收藏  举报