在.Net中有兩種常用的Web開發方式,一種是Asp.Net WebForm,另一種是Asp.Net MVC。我先簡單的給大家介紹下這兩種開發方式的特點,然后再應用自定義腳本映射,反射,json2template.js,htm等技術演示一個純靜態的Web框架。
Asp.Net WebForm
在Asp.Net WebForm中,請求大都以.aspx為后綴,那么.Net是如何處理.aspx請求呢? 打開.Net的配置文件,
.Net4.0配置文件地址(64位):C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Config\web.config;
.Net4.0配置文件地址(32位):C:\Windows\Microsoft.NET\Framework\v4.0.30319\Config\web.config。
找到HttpHandlers的配置信息如下:
<httpHandlers>
<add path="eurl.axd" verb="*" type="System.Web.HttpNotFoundHandler" validate="True" />
<add path="trace.axd" verb="*" type="System.Web.Handlers.TraceHandler" validate="True" />
<add path="WebResource.axd" verb="GET" type="System.Web.Handlers.AssemblyResourceLoader" validate="True" />
<add verb="*" path="*_AppService.axd" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" validate="False" />
<add verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" validate="False"/>
<add path="*.axd" verb="*" type="System.Web.HttpNotFoundHandler" validate="True" />
<add path="*.aspx" verb="*" type="System.Web.UI.PageHandlerFactory" validate="True" /> ................................................
從以上配置信息可以看出所有的*.aspx請求都是交給PageHandlerFactory處理工廠去處理的。PageHandlerFactory定義如下:
public class PageHandlerFactory : IHttpHandlerFactory2, IHttpHandlerFactory
{
public virtual IHttpHandler GetHandler(HttpContext context, string requestType, string virtualPath, string path);
private IHttpHandler GetHandlerHelper(HttpContext context, string requestType, VirtualPath virtualPath, string physicalPath);
public virtual void ReleaseHandler(IHttpHandler handler);
IHttpHandler IHttpHandlerFactory2.GetHandler(HttpContext context, string requestType, VirtualPath virtualPath, string physicalPath);
}
調用PageHandlerFactory的GetHandler會生成一個Page(IHttpHandler)類,然后調用Page類的ProcessRequest方法,再渲染*.aspx中的控件,最后生成Html給Response。如圖:
Asp.Net MVC
在Asp.Net MVC中,所有的請求都交給UrlRoutingModule處理。通過UrlRoutingModule得到一個MvcRouteHandler。調用MvcRouteHandler的GetHttpHandler方法返回MvcHandler。然后調用MvcHandler的ProcessRequest方法,通過反射創建Controller實例,並執行其方法。如圖:
在Asp.Net WebForm和Asp.Net Mvc中,其實都是生成一個IHttpHandler的類,並調用其ProcessRequest方法,通過不同的渲染方式,把最終結果返回給用戶。
框架
好了,WebForm和MVC就介紹到這里,下面開始寫自己的框架了。我們知道Asp.Net MVC 包括:UrlRoutingModule(Url處理) + View(*.aspx)+Controller+ViewEngine,而我的框架包括:
WebMethodHttpHandler(處理中心)+View(*.htm)+Controller(業務類)+Js(json2template.js渲染器),呵呵,跟Asp.Net MVC有點像。如圖:
框架中,靜態頁面都是以.htm為后綴,如:login.htm。動態請求都是以mthd為后綴,如:/User/CheckLogin.mthd。動態請求URL地址有一定的含義:如:/User/CheckLogin.mthd中,User代表業務處理類 UserController,CheckLogin就是UserController的一個方法(驗證用戶登錄的方法)。
我們已經知道,不管是WebForm還是Mvc,最終都需要生成一個IHttpHandler的類去處理請求。在我的框架中,我打算模仿WebForm對於*.aspx的處理方式,定義一個IHttpHandler去處理*.mthd請求。這個IHttpHandler的類定義為 WebMethodHttpHandler。WebMethodHttpHandler定義如下:

1 public class WebMethodHttpHandler : IHttpHandler, IRequiresSessionState 2 { 3 public bool IsReusable 4 { 5 get { return false; } 6 } 7 8 public void ProcessRequest(HttpContext context) 9 { 10 var request = context.Request; 11 var arr = request.Path.Split('/'); 12 13 string controllerName = arr[arr.Length - 2]; 14 string methodName = Path.GetFileNameWithoutExtension(arr[arr.Length - 1]); 15 16 var factory = ControllerBuilder.Current.GetControllerFactory(); 17 var obj = factory.CreateController(controllerName, methodName); 18 var result = obj.Execute(request.Params); 19 20 var js = new JavaScriptSerializer(); 21 string json = js.Serialize(result); 22 context.Response.ContentType = "json/plain"; 23 context.Response.Write(json); 24 } 25 }
代碼很簡單,WebMethodHttpHandler實現了兩個接口,分別是IHttpHandler和 IRequiresSessionState。在方法ProcessRequest中把請求的路徑URL根據"/"拆分成一個數組,分別獲得業務類Controller和它的方法名,再調用DefaultControllerFactory使用反射技術動態生成RemotingObject實例,最后調用RemotingObject的Execute方法返回Json數據。好了,我們看看ControllerBuilder 的定義:

1 public class ControllerBuilder 2 { 3 private static ControllerBuilder _instance = null; 4 private static object syncObj = new object(); 5 6 private Func<DefaultControllerFactory> func = null; 7 8 private ControllerBuilder() 9 { 10 SetControllerFactory(new DefaultControllerFactory()); 11 } 12 13 public static ControllerBuilder Current 14 { 15 get 16 { 17 if (_instance == null) 18 { 19 lock (syncObj) 20 { 21 if (_instance == null) 22 { 23 _instance = new ControllerBuilder(); 24 } 25 } 26 } 27 return _instance; 28 } 29 } 30 31 public void SetControllerFactory(DefaultControllerFactory controllerFactory) 32 { 33 if (controllerFactory == null) 34 { 35 throw new ArgumentNullException("controllerFactory"); 36 } 37 38 func = () => controllerFactory; 39 } 40 41 public DefaultControllerFactory GetControllerFactory() 42 { 43 return func(); 44 } 45 }
ControllerBuilder類定義很簡單就是一個單例模式,用於返回DefaultControllerFactory。DefaultControllerFactory定義:

1 public sealed class DefaultControllerFactory 2 { 3 private static object _controllerTypeCacheLock = new object(); 4 private static ControllerTypeCache _controllerTypeCache = new ControllerTypeCache(); 5 private readonly string key = "{0}.{1}"; 6 7 /// <summary> 8 /// 創建Controller-Method 9 /// </summary> 10 /// <param name="controllerName">Controller名稱</param> 11 /// <param name="methodName">方法名稱</param> 12 /// <returns></returns> 13 public RemotingObject CreateController(string controllerName, string methodName) 14 { 15 if (string.IsNullOrEmpty(controllerName) || string.IsNullOrEmpty(methodName)) 16 { 17 throw new Exception("controllerName or methodName is null"); 18 } 19 20 if (!_controllerTypeCache.Initialized) 21 { 22 lock (_controllerTypeCacheLock) 23 { 24 if (!_controllerTypeCache.Initialized) 25 { 26 _controllerTypeCache.Initialize(); 27 } 28 29 var assemblies = BuildManager.GetReferencedAssemblies(); 30 31 foreach (Assembly asm in assemblies) 32 { 33 foreach (Type t in asm.GetTypes()) 34 { 35 //類型t是否繼承IController 36 if (typeof(IController).IsAssignableFrom(t) && t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase) 37 && t != typeof(IController)) 38 { 39 var methods = t.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.DeclaredOnly); 40 41 foreach (var method in methods) 42 { 43 if (!_controllerTypeCache.ContainsController(string.Format(key, t.Name, method.Name))) 44 { 45 _controllerTypeCache.AddControllerType(CreateRemotingObject(t, method.Name)); 46 } 47 } 48 } 49 } 50 } 51 } 52 } 53 54 controllerName = controllerName.EndsWith("Controller") ? controllerName : controllerName + "Controller"; 55 56 return _controllerTypeCache.GetControllerType(string.Format(key, controllerName, methodName)); 57 } 58 59 public RemotingObject CreateRemotingObject(Type type, string methodName) 60 { 61 if (type == null) 62 { 63 throw new Exception(string.Format("類型null不存在!")); 64 } 65 66 var method = type.GetMethods().ToList().Find(p => p.Name.ToLowerInvariant() == methodName.ToLowerInvariant()); 67 68 if (method == null) 69 { 70 throw new Exception(string.Format("類:{0}不存在方法名:{1}", type.FullName, methodName)); 71 } 72 73 return new RemotingObject 74 { 75 Request = string.Format(key, type.Name, methodName), 76 ControlObject = (IController)Activator.CreateInstance(type), 77 Method = method 78 }; 79 } 80 }
RemotingObject定義如下:

1 public class RemotingObject 2 { 3 /// <summary> 4 /// 請求地址 5 /// </summary> 6 public string Request { get; set; } 7 8 /// <summary> 9 /// control實例 10 /// </summary> 11 public IController ControlObject { get; set; } 12 13 /// <summary> 14 /// 所調用方法 15 /// </summary> 16 public MethodInfo Method { get; set; } 17 18 public object Execute(NameValueCollection collection) 19 { 20 var parameters = new object[Method.GetParameters().Length]; 21 int i = 0; 22 23 foreach (var parameter in Method.GetParameters()) 24 { 25 var item = 26 collection.AllKeys.ToList().Find( 27 p => string.Equals(parameter.Name, p, StringComparison.OrdinalIgnoreCase)); 28 29 if (item != null) 30 { 31 parameters[i] = Convert.ChangeType(collection[item], parameter.ParameterType); 32 i++; 33 } 34 } 35 36 return Method.Invoke(ControlObject, parameters); 37 } 38 }
ControllerTypeCache定義如下:

1 /// <summary> 2 /// Controller集合緩存 3 /// </summary> 4 internal sealed class ControllerTypeCache 5 { 6 private Dictionary<string, RemotingObject> _controllerCache = new Dictionary<string, RemotingObject>(StringComparer.OrdinalIgnoreCase); 7 private bool _initialized; 8 9 /// <summary> 10 /// RemotingObject個數 11 /// </summary> 12 public int Count 13 { 14 get 15 { 16 return _controllerCache.Count; 17 } 18 } 19 20 /// <summary> 21 /// 是否已加載 22 /// </summary> 23 public bool Initialized 24 { 25 get 26 { 27 return _initialized; 28 } 29 } 30 31 public void Initialize() 32 { 33 _initialized = true; 34 } 35 36 /// <summary> 37 /// 添加controllerType 38 /// </summary> 39 /// <param name="name"></param> 40 /// <param name="controllerType"></param> 41 public void AddControllerType(RemotingObject controllerType) 42 { 43 _controllerCache[controllerType.Request] = controllerType; 44 } 45 46 public bool ContainsController(string name) 47 { 48 return _controllerCache.ContainsKey(name); 49 } 50 51 /// <summary> 52 /// 根據name獲取Controller-Method 53 /// </summary> 54 /// <param name="name"></param> 55 /// <returns></returns> 56 public RemotingObject GetControllerType(string name) 57 { 58 return _controllerCache[name]; 59 } 60 }
介紹完核心類后,在介紹下Controller,我的Controller結構如圖:
UserController定義如下:

1 /// <summary> 2 /// /用戶Controller 3 /// </summary> 4 5 public class UserController : IController 6 { 7 public object GetUserInfo() 8 { 9 return new 10 { 11 ID=1, 12 Name="喜羊羊", 13 Age=31, 14 Address="北京" 15 }; 16 } 17 }
UserController定義很簡單,跟普通類沒什么區別,只是繼承了IController,IController只是一個標志接口,定義如下:

public interface IController { }
好了,介紹完框架的核心類。要讓網站運行起來,我們還需要再做兩件事情。
第一步:在web.config的httpHandlers中注冊處理mthd的Handler。
<httpHandlers>
<add path="*.mthd" verb="*" type="Elong.Sticker.Core.HttpHandlers.WebMethodHttpHandler,Elong.Sticker.Core" />
</httpHandlers>
第二步:添加腳本映射。首先打開IIS,找到處理程序映射,雙擊打開,找到左邊菜單“添加腳本映射”,編輯如下:
"確定"完成,會在站點的web.config中添加
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
<handlers>
<add name="aspnet4Method" path="*.mthd" verb="*" modules="IsapiModule" scriptProcessor="C:\Windows\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" resourceType="Unspecified" preCondition="classicMode,runtimeVersionv4.0,bitness64" />
</handlers>
</system.webServer>
新建UserInfo.htm文件,內容如下:

<html xmlns="http://www.w3.org/1999/xhtml"> <head> <title></title> <script src="lib/jquery-1.4.1.min.js" type="text/javascript"></script> <script src="lib/json2template.js" type="text/javascript"></script> </head> <body> <div id="userlist"> <div id="template-userinfo"> <table width="400" align="center"> <tr><td>編號</td><td>姓名</td><td>年齡</td><td>地址</td></tr> <tr> <td width="100">{{ID}}</td> <td width="100">{{Name}}</td> <td width="100">{{Age}}</td> <td width="100">{{Address}}</td> </tr> </table> </div> </div> <script type="text/javascript"> $(document).ready(function () { var dataSouce = {}; $.ajax( { url: '/User/GetUserInfo.mthd', dataType: "json", success: function (data) { dataSouce.item = data; $("#userlist").bindTemplate({ source: dataSouce, template: $("#template-userinfo") }); } }); }); </script> </body> </html>
現在可以預覽了,在瀏覽器中輸入UserInfo.htm頁面地址,會等到如下結果:
好了,感謝閱讀,希望這篇文章能給你帶來幫助!