写一个简单的Web框架_.NET_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > .NET > 写一个简单的Web框架

写一个简单的Web框架

 2013/10/22 18:55:36  喜洋羊  博客园  我要评论(0)
  • 摘要:在.Net中有两种常用的Web开发方式,一种是Asp.NetWebForm,另一种是Asp.NetMVC。我先简单的给大家介绍下这两种开发方式的特点,然后再应用自定义脚本映射,反射,json2template.js,htm等技术演示一个纯静态的Web框架。Asp.NetWebForm在Asp.NetWebForm中,请求大都以.aspx为后缀,那么.Net是如何处理.aspx请求呢?打开.Net的配置文件,.Net4.0配置文件地址(64位):C:\Windows\Microsoft
  • 标签:Web 一个

  在.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页面地址,会等到如下结果:

  

  好了,感谢阅读,希望这篇文章能给你带来帮助!

     源代码

发表评论
用户名: 匿名