aspx頁面第一次加載時,HttpHandler 里面是如何編譯指定頁面的呢?Framework提供了編譯頁面的API如下:
BuildManager.CreateInstanceFromVirtualPath(url, typeof(System.Web.UI.Page));根據虛擬路徑生成實例。
但是url頁面此時必需繼承System.Web.UI.Page,就是我們常見的ASPX頁面。但是這樣編譯時會調用aspx視圖引擎來解析aspx和對應的CodeBehind類。
對於前台純粹用JSON渲染的JS插件,解析編譯ASPX頁面似乎是多余的,aspx的生命周期也是復雜和沒什么卵用的,那問題來了,能不能只編譯類文件(當然要繼承IHttpHandler),而aspx頁面用html替換呢?
如下圖的結構,(如何一鍵建立這樣的文件結構下次再說,MVC中View和Controller分的太開了,不太習慣,中小型項目還是這樣覺的更好。)
目標:html上發送ajax請求到.cs上,並返回數據到.html
Framework提供一個方法:
var ass = BuildManager.GetCompiledAssembly(url);根據虛擬路徑得到程序集。第一次加載需要編譯慢一點,之后會很快。
url可以是這樣:/Portal/ListPageTmp.cs,即根據class文件路徑生成Assembly,
當然此時瀏覽器url是/Portal/ListPageTmp.html
我們可以在前台ajax請求時就把路徑由/Portal/ListPageTmp.html轉換成/Portal/ListPageTmp.cs,再發送ajax,也可以在后台GetHandler方法里面轉換。
我是在前台轉換的如下:(把$.ajax封裝一下,確保所有AJAX都調用此方法以便轉換url)
$.ajax({
type: 'post',
url: ‘/Portal/ListPageTmp.cs’,
async: async,
data: data,
success: function (data) { rtn = data; },
error: function (data) { rtn["result"] = "fail"; alert("操作失敗") },
dataType: 'json'
});
要想以這樣ajax請求(/Portal/ListPageTmp.cs)讓IIS接收到 那必然要改web.Config:不解釋
<system.webServer>
<handlers>
<add name="ddd" verb="*" path="*.cs" type="App.PageBase.CSHttpHandler" />
</handlers>
</system.webServer>
以下是IHttpHandlerFactory完整代碼:
1 public class CSHttpHandler : IHttpHandlerFactory 2 { 3 public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated) 4 { 5 6 try 7 { 8 var ass = BuildManager.GetCompiledAssembly(url);//重要的一個方法,也見過其它的跟據url生成實例的方法,但是如果不同命名空間有同名類就不好辦了。 9 // var d = BuildManager.CreateInstanceFromVirtualPath(url, typeof(System.Web.UI.Page)); 10 11 TypeInfo pageType = ass.DefinedTypes.Where(p => p.IsClass == true && p.BaseType.BaseType == typeof(BasePage)).FirstOrDefault(); 12 BasePage basepage = Activator.CreateInstance(pageType) as BasePage; 13 return (IHttpHandler)basepage; 14 } 15 catch (Exception ex) 16 { 17 throw ex; 18 } 19 20 } 21 22 public void ReleaseHandler(IHttpHandler handler) 23 { 24 25 26 } 27 28 }
BasePage類,和System.Web.UI.Page類功能相當。
1 public class BasePage : IHttpHandler 2 { 3 4 private bool _IsAjaxRequest; 7 private string _Action; 8 private ActionResult _ActionResult; 9 AppCache cache = new AppCache(); 10 public BasePage() 11 { 12 this.IsCheckLogin = true; 13 14 this.IsCheckAuth = true; 15 16 _ActionResult = new ActionResult(); 17 18 19 20 } 21 22 public bool IsReusable { get; set; } 23 protected HttpRequest Request 24 { 25 get; 26 set; 27 } 28 protected HttpResponse Response 29 { 30 get; 31 set; 32 } 33 34 public virtual void ProcessRequest(HttpContext context) 35 { 36 this.Request = context.Request; 37 this.Response = context.Response; 38 this.OnInit(); 39 40 } 41 protected string Action 42 { 43 get 44 { 45 return _Action; 46 } 47 48 49 }
//判斷是否AJAX請求,這種模式,到這里的請求都是AJAX請求,因為HTML加載時不用觸發請求到后端的。 50 protected bool IsAjaxRequest 51 { 52 get 53 { 54 return _IsAjaxRequest; 55 } 56 57 58 } 59 protected virtual void OnInit() 60 { 61 _IsAjaxRequest = this.Request.Headers["X-Requested-With"] == "XMLHttpRequest" ? true : false; 62 _Action = Request.QueryString["Action"] == null ? "" : Request.QueryString["Action"].ToString(); 63 form = new FormData(Request.Form);
//根據URL上的Action參數找到繼承此類上的方法,並反射調用。
MethodInfo method = this.GetType().GetMethod(Action);
if (method == null)
{
throw new Exception("找不到【" + method.Name + "】方法.");
}
else
{
try
{
//綁定參數並調用,當然調用前也可以也可以做ActionFilter檢查
InvokMethod(this, method);
}
catch (Exception ex)
{
actionResut.hasError = true;
actionResut.message = ex.InnerException.Message;
}
}
//返回json數據
ReturnData(actionResut);
64 }
//綁定Action方法的參數。 65 protected object InvokMethod(object ins, MethodInfo method) 66 { 67 68 69 var methodParam = method.GetParameters(); 70 object[] param = new object[methodParam.Length]; 71 for (int i = 0; i < methodParam.Length; i++) 72 { 73 string name = methodParam[i].Name; 74 Type paramType = methodParam[i].ParameterType; 75 if (paramType.IsGenericType) paramType = paramType.GetGenericArguments()[0]; 76 string ArgValue = Request.QueryString[name]; 77 string FormValue = Request.Form[name]; 78 string value = string.IsNullOrEmpty(ArgValue) ? FormValue : ArgValue; 79 if (!string.IsNullOrEmpty(value)) 80 { 81 if (paramType.IsValueType) 82 { 83 param[i] = Convert.ChangeType(value, paramType); 84 } 85 else if (paramType == typeof(string)) 86 { 87 param[i] = value; 88 } 89 else 90 { 91 param[i] = JsonHelper.Json2Object(value.ToString(), paramType); 92 } 93 } 94 95 else 96 { 97 param[i] = null; 98 } 99 } 100 return method.Invoke(ins, param); 101 102 } 103 }
ListPageTmp.html對應的類文件是這樣的:
如果有ListPageTmp.html?Action=Test&Id=1&name=zhangsan的ajax請求,會根據名稱找到ListPageTmp.cs,實例化后將url參數綁定到Test方法參數上執行,返回JSON.
1 public partial class ListPageTmp :BasePage 2 { 3
[Auth(Role="Admin")]//反射調用前也可檢查是否有attribute並做 攔截 ,類似MVC的ActionFilter. 4 public void Test(int Id,string name) 5 { 6 7 actionResut.Add("Name",name);
actionResut.Add("Id",Id);
8 9 } 10 11 12 13 }
這樣前后端就分開了。html加載完成后,根據不同需要發送ajax從服務端取json數據綁定的前台。
也實現了MVC里面的一些功能,如ActionFilter,Action參數綁定,ActionResult和MVC的不太一樣。沒有很復雜的ActionResult,只是封裝json.
MVC是通過URL 路由 找到 Control和Action,由Action定向到View,
今天說的這個方案,是通過View找到Control和Action 執行后返回JSON給View.
唉,不知道說明白了沒有。。。。