在.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定义如下:
logs_code_hide('9390f417-0857-424d-bb19-93c913ad5ce5',event)" style="display: none;" />
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 }View Code
代码很简单,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 }View Code
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 }View Code
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 }View Code
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 }View Code
介绍完核心类后,在介绍下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 }View Code
UserController定义很简单,跟普通类没什么区别,只是继承了IController,IController只是一个标志接口,定义如下:
public interface IController { }View Code
好了,介绍完框架的核心类。要让网站运行起来,我们还需要再做两件事情。
第一步:在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>View Code
现在可以预览了,在浏览器中输入UserInfo.htm页面地址,会等到如下结果:
好了,感谢阅读,希望这篇文章能给你带来帮助!
源代码