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的處理機制。。。