URL重写及干掉ASP.NET试图状态

来源:岁月联盟 编辑:exp 时间:2011-11-19

 

1、URL重写已经很普遍了,但基本上大部分的URL重写都不支持页面的相对路径,所有如果想在已经开发好的项目中添加还是有压力的,第二就是例如微软的那个URL重写是根据正则表达式来处理的,那样是很好,但也有不足之处,就是不方便定位到某个页面只能有哪些参数。

我觉得要解决的问题有一下几个:

 1、解决如图片js等不能使用相对路径的文件

 2、解决某个页面能有几个参数和哪些参数是可选的

    下面就是解决掉这些问题了

    添加处理程序MyHttpModule,下面是我的一个简单的处理程序(我只是做了一个简单的,并没有考虑性能,而且我是写死的一个url重写就是重写成没有扩展名的)

     

using System;

using System.Collections.Generic;

using System.Web;

using System.IO;

using System.Text;

 

namespace MyClass

{

    public class MyHttpModule : IHttpModule

    {

        #region IHttpModule 成员

        ///<summary>

        /// 释放所有资源

        ///</summary>

        public void Dispose()

        {

        }

        ///<summary>

        /// 初始化模块,并使其为处理请求做好准备

        ///</summary>

        ///<param name="context"> 一个System.Web.HttpApplication,它提供对ASP.NET 应用程序内所有应用程序对象的公用的方法、属性和事件的访问</param>

        public void Init(HttpApplication context)

        {

            context.AuthorizeRequest += new

         EventHandler(this.BaseModuleRewriter_AuthorizeRequest);

        }

        ///<summary>

        /// 当安全模块已验证用户授权时发生

        ///</summary>

        ///<param name="sender"></param>

        ///<param name="e"></param>

        protected virtual void BaseModuleRewriter_AuthorizeRequest(

   object sender, EventArgs e)

        {

            System.Web.HttpApplication app = (System.Web.HttpApplication)sender;

            Rewrite(app.Request.Path, app);

        }

        ///<summary>

        /// 重写url

        ///</summary>

        ///<param name="requestedPath">url的虚拟路径</param>

        ///<param name="app"></param>

        protected void Rewrite(string requestedPath, System.Web.HttpApplication app)

        {

            List<string> qeryString;

            string virtualPath;

            string inputFile = GetInputFile(app.Context, out virtualPath, out qeryString);//获取到真实的文件信息

            if (System.IO.Path.GetExtension(inputFile) == ".aspx")//如果是aspx文件 那么则把保留重写的url

            {

                app.Context.RewritePath(requestedPath, string.Empty, string.Empty);//这里查询参数我没去处理了,也就是Request.QueryString的信息,如果取qeryString 然后去处理成一个字符串

                return;

            }

            app.Context.RewritePath(virtualPath, string.Empty, app.Context.Request.QueryString.ToString());//其它文件则使用找到的路径

        }

        ///<summary>

        /// 获取url对应的绝对路径和虚拟路径及查询参数

        ///</summary>

        ///<param name="context"></param>

        ///<param name="virtualPath">虚拟路径</param>

        ///<param name="qeryString">查询参数  如果为null请取HttpContext.Request.QueryString</param>

        ///<returns>url对应的绝对路径</returns>

        public static string GetInputFile(HttpContext context, out string virtualPath, out List<string> qeryString)

        {

            string executionFilePath = context.Request.AppRelativeCurrentExecutionFilePath.Remove(0, 2);//获取当前对应的虚拟路径并干掉“~/”

            string inputFile = context.Request.PhysicalPath;//获取当前url对于的绝对路径

            virtualPath = context.Request.AppRelativeCurrentExecutionFilePath;

            qeryString = null;

            List<string> qeryList = new List<string>();

            if (!File.Exists(inputFile))//判断文件是否存在,也就是没有被重写的url获取使用绝对路径的资源等等

            {

                bool b = false;

                string fileName;

                string extension;

                string applicationPath = context.Request.PhysicalApplicationPath;//获取网站的跟目录

                var tempPath = GetFileInfo(inputFile, out fileName, out extension);

                while (!b)//根据目录循环获取有效的文件目录

                {

                    b = File.Exists(tempPath + "//" + extension);//判断文件是否存在

                    if (tempPath + "//" == applicationPath)//如果查找到根目录还没有查找到那么则不需要在查了

                    {

                        break;

                    }

                    if (!string.IsNullOrWhiteSpace(fileName))

                    {

                        qeryList.Add(fileName);//如果不存在那么这个就是参数  例如http://localhost:4688/WebForm1/2011/  (对应http://localhost:4688/WebForm1.aspx?xxx=2011)

                    }

                    tempPath = GetFileInfo(tempPath, out fileName, out extension);

                }

                if (b)//如果查找到了就把查找到的路径复制给输出或返回参数

                {

                    inputFile = tempPath + "//" + extension;

                    virtualPath = "~/" + inputFile.Replace(applicationPath, null);

 

                }

                if (Path.GetExtension(extension) == ".aspx")//如果是asp.net那么则把list复制给输出参数qeryString

                {

                    qeryString = qeryList;

                }

            }

            return inputFile;

        }

        ///<summary>

        /// 获取指定目录+文件是否有效

        ///</summary>

        ///<param name="inputFile">目录</param>

        ///<param name="fileName"></param>

        ///<param name="extension"></param>

        ///<returns></returns>

        private static string GetFileInfo(string inputFile, out string fileName, out string extension)

        {

            var tempPath = Directory.GetParent(inputFile).FullName;//获取传进来目录的父目录

            fileName = inputFile.Replace(tempPath + "//", null);//获取子目录名称

            extension = Path.GetExtension(inputFile);//获取扩展名

            if (string.IsNullOrWhiteSpace(extension))//如果扩展名为null那么则认为是aspx文件

            {

                extension = fileName + ".aspx";

            }

            else

            {

                extension = fileName + extension;

            }

            return tempPath;

        }

        #endregion

    }

}

 因为我在处理aspx页面时还是传入的重写后的路径,所有我们还有添加一个继承IHttpHandlerFactory的类

 代码如下:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Web;

using System.IO;

using System.Web.UI;

 

 

namespace MyClass

{

  

    public class MyHttpHandlerFactory:IHttpHandlerFactory

    {

        #region IHttpHandlerFactory 成员

        ///<summary>

        /// 返回实现System.Web.IHttpHandler 接口的类的实例

        ///</summary>

        ///<param name="context">System.Web.HttpContext 类的实例,它提供对用于为HTTP 请求提供服务的内部服务器对象(如Request、Response、Session和Server)的引用</param>

        ///<param name="requestType">客户端使用的HTTP 数据传输方法(GET 或POST)</param>

        ///<param name="url">所请求资源的System.Web.HttpRequest.RawUrl</param>

        ///<param name="pathTranslated">所请求资源的System.Web.HttpRequest.PhysicalApplicationPath</param>

        ///<returns>处理请求的新的System.Web.IHttpHandler 对象</returns>

        public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated)

        {

            List<string> qeryString;

            string virtualPath;

            string inputFile =MyHttpModule.GetInputFile(context, out virtualPath, out qeryString);//这里跟那里是一样的

            object[] obj = new object[] { };

            Dictionary<string, string> qeryStringDictionary = new Dictionary<string, string>();

            var receiveMembers = System.Web.Compilation.BuildManager.GetCompiledType(virtualPath).GetMember("ReceiveParameters");//获取访问当前页面的所有ReceiveParameters成员 (这个是我自己加的,就是想做成和mvc的那种模式,但可能不是很好)

            System.Reflection.MethodInfo receiveParameters=null;

            if (qeryString != null&&qeryString.Count>0)//如果查找到没有参数则不去反射了

            {

                foreach (System.Reflection.MemberInfo receiveMember in receiveMembers)//遍历所有ReceiveParameters成员

                {

                    if (receiveMember.MemberType == System.Reflection.MemberTypes.Method)//因为上面获取到的是成员 但我们要的是方法所有要判断下

                    {

                        System.Reflection.MethodInfo methodInfo = receiveMember as System.Reflection.MethodInfo;

                        if (methodInfo != null)

                        {

                            var parameters = methodInfo.GetParameters();//获取ReceiveParameters方法的所有参数

                            int optionalCount = parameters.Count(i => i.IsOptional);//获取ReceiveParameters参数里面有多少个可选参数

                            bool b = qeryString.Count == parameters.Length - optionalCount;

                            if (qeryString.Count == parameters.Length || b)//如果当前查询的参数或ReceiveParameters的所有参数-去可选择的查询参数相等

                            {

                                receiveParameters = methodInfo;//记录这个方法

                                int i = 0;

                                obj = new object[parameters.Length];//记录参数值,到后面调用ReceiveParameters时用

                                for (; i < parameters.Length; i++)

                                {

                                    string name = parameters[i].Name;//获取参数的名称

                                    string value = string.Empty;

                                    if (qeryString.Count > i)//如果ReceiveParameters参数没到可选参数那么则去查询的字符串

                                    {

                                        value = qeryString[i];

                                    }

                                    obj[i] = value;//把查询的字符串保存起来,到后面调用ReceiveParameters时用

                                    qeryStringDictionary.Add(name, value);//添加到自定义的集合里面

                                }

                               

                                break;

                            }

                        }

                    }

                }

                if (receiveParameters == null)//判断是否已经找到,如果没找到就把以前找的文件信息全部赋为重写的文件信息,也就是不存在的

                {

                    virtualPath = context.Request.Path;

                    inputFile = context.Request.PhysicalPath;

                }

            }

            var temp= System.Web.UI.PageParser.GetCompiledPageInstance(virtualPath, inputFile, context);//编译页面

            if (receiveParameters != null)//这个里面的内容其实应该写到ReleaseHandler里面去的,但我写在这里了

            {

                System.Web.UI.Page page = (System.Web.UI.Page)temp;

                page.Init+=new EventHandler(page_Init);//添加一个事件,//还有就是本来应该添加一个PageBase类的,那样就可以把真实的路径信息和查询参数放进去

                sss = receiveParameters;

                sssobj = obj;

                //receiveParameters.Invoke(temp, obj);

            }

            return temp;

 

        }

        public System.Reflection.MethodInfo sss { get; set; }

        public object[] sssobj { get; set; }

        protected void page_Init(object sender, EventArgs e)

        {

            sss.Invoke(sender, sssobj);//当page执行到这里时就去调用ReceiveParameters方法 在这里还可以做其它的判断。。。 但不符合编程规范(我的理解)

          

        }

        ///<summary>

        /// 使工厂可以重用现有的处理程序实例

        ///</summary>

        ///<param name="handler">要重用的System.Web.IHttpHandler 对象</param>

        public void ReleaseHandler(IHttpHandler handler)

        {

        }

        #endregion

    }

}

页面代码就是多放几个方法

///<summary>

        /// 一个参数的  如果需要多个则手动添加如public void ReceiveParameters(string name,string value)等等 这样页面编译后就会根据参数自动运行这个方法并转递参数值

        ///</summary>

        ///<param name="name">参数名称为name</param>

        public void ReceiveParameters(string name)

        {

            var temp = Request;

        }

 

 

 url的解决了,在来看看干掉试图的。。。

我只写了把事件的实体状态去掉了,然后手动去激发控件的事件,而且就是在url中写里面解决的  代码如下:

protected void page_Init(object sender, EventArgs e)

        {

            sss.Invoke(sender, sssobj);

            Page page = (Page)sender;

            foreach (string name in page.Request.Form.AllKeys)//查找form里面所有的字典  其实应该取__EVENTARGUMENT隐藏域的

            {

                try

                {

                   

                    System.Web.UI.Control control = page.FindControl(page.Page.Request.Form[name]);//查找这个控件

                    if (control != null)

                    {

                        string value = page.Request.Form[Page.postEventSourceID];

                        IPostBackEventHandler ip = control as IPostBackEventHandler;

                        if (ip != null)//能转换成IPostBackEventHandler  那么就激发它

                        {

                            ip.RaisePostBackEvent(value);

                            break;

                        }

                        IPostBackDataHandler backDataHandler = control as IPostBackDataHandler;

                        if (backDataHandler != null)//能转换成IPostBackDataHandler 就把__EVENTTARGET隐藏域的值传给控件 然后激发更改事件

                        {

                            System.Collections.Specialized.NameValueCollection nameValueCollection=new System.Collections.Specialized.NameValueCollection();

                            nameValueCollection.Add(page.Request.Form[control.ClientID],page.Request.Form[control.ClientID]);

                            backDataHandler.LoadPostData(control.ClientID, nameValueCollection);

                            backDataHandler.RaisePostDataChangedEvent();

                        }

                    }

                    break;

                }

                catch

                {

                   

                }

            }

        }

 

这样简单的处理就完了,

 

我希望各位来帮我改进改进,因为我毕竟还不太了解ASp.net的处理机制。。。

  

摘自 快乐你和我