您的位置:首页 > 编程语言 > ASP

Asp.Net中实现自己的Mvc开发框架

2010-07-14 22:02 721 查看
如今mvc框架越来越流行,像现在微软推出的asp.net mvc都已经到了2.0版本了,而在java界的struts也早已成了j2ee开发的首选。工作之后,看到公司也有自己的一套mvc框架,所以闲暇之余就想自己来实现一个mvc框架。

我个人觉得这个mvc框架实现并不难,关键在于设计思路以及对于mvc本身的理解。废话不多说了,直接步入正题(对于mvc的基本知识在此不再多说)。

1.架构分析

1.1熟悉mvc请求过程:




对于web应用程序来说,只有一个事件就是请求,所有的交互都是通过“请求响应”的形式出现的。整个过程可以说就是得到请求、处理请求、显示给用户的过程,而之所以引入mvc就是为了将逻辑处理和页面显示分开。上图显示了一般mvc框架对于请求的处理过程,我们的框架也会沿用此过程,所以我们分析一下这个过程:一个请求到来之后首先会根据请求内容,将请求交给控制器,控制器根据请求具体内容做出相应的处理,而我们知道不管对怎样的逻辑进行处理最终都会转化为对数据的组织,因此这是就会调用相应的数据模块,最终将数据组织好最终交给视图来显示。

整个过程是很好理解,那么我们用程序实现呢?我想此时就会出现几个问题:如何交给controller?controller如何知道什么样的请求做出什么用的处理?Controller知道处理什么请求之后如何具体实施?如何将处理组织好的数据交给view?带着几个问题继续往下看。

1.2引入HttpHandler

我们知道如果在应用中加入HttpHandler程序,并且在web.config中做出相应的配置的话其HttpHandler程序就会执行。那么我们不妨利用HttpHandler程序根据请求的url来执行相应的controller程序(确切地说是controller中的一个动作action)。

1.3引入Asp.Net Mvc的url机制

我们可以得到url,那么如何根据url处理相应action动作?解决办法现在大致分为两种:一种就是像struts一样,使用配置文件,根据一个特定后缀得到对应的名称,然后在配置文件中找到此名称对应的程序;另一种就是像asp.net mvc一样做出相应的约定,根据url格式进行相应的提取,找到对应的程序。而我个人认为后者更有利于开发,因此我们采用后者。



1.4引入反射

即使知道要调用哪个action,那么如何去调用呢?这个问题其实就要引入.Net的反射机制,我们将通过反射动态创建Controller的实例并且根据的得到的信息调用相应的action。

1.5引入NVelocity

如果像上面所说的,可以根据请求的不同执行相应的controller程序,那么关键就变成了如果将处理好的数据显示到相应的View中。这个问题我们可以借助于NVelocity这个模板引擎来解决。我们知道NVelocity可以将后台的数据以变量的形式存放到Velocity上下文中,在前台的模板中只需要读取相应的变量即可的到相应的数据。所以我们只要在cotroller的action中加载视图模板,存放组织好的数据,再在对应的视图模板中读取这些数据就可了。

上的四个个"引入"已经解决了我们提出的所有问题,其实也就是我们框架的大致思路,因此接下来就是具体实现了。

2.程序设计

2.1框架主程序

首先我们的给url以约定,我们上面已经说了,我们采用contro/action/parameter(parameter可以没有)的形式,例如http://127.0.0.1/Home/Index/1,这样这样的请求我们就可以得到Controller名字是Home,而Action名字是Index,参数是1。然后就是执行相应的Controller中的action程序。如何执行呢?我们知道其实Controller是一个类,而action是类中的一个方法。现在我们知道名字如何来执行程序呢?上面已经说了,利用反射我们加载程序集调用其方法。这里由于我们不知道程序集处于何处,但是我们可以得到当前项目所用的所有程序集,然后根据约定(将所有的controller都放在项目根目录的controllers文件夹下)就可以知道所有的controller类的全名(包含完整命名空间)都会出现controllers,根据这个条件过滤出所有的controller类,再根据action名利用反射执行相应的方法。具体程序:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.SessionState;
using Cmj.Common;
namespace Cmj.MyWeb.MyMvcFramwork
{
    public class MyMvc : IHttpHandler, IRequiresSessionState //必须实现这个借口否则没有对session对写权限,使用时提示为null
    {
        #region IHttpHandler Members
        public bool IsReusable
        {
            get { return false; }
        }
        public void ProcessRequest(HttpContext context)
        {
            string url = context.Request.Url.ToString();
            UrlHelper urlHelper = new UrlHelper(url);
            string controllerName = urlHelper.GetControllerName();//得到controller名称
            string actionName = urlHelper.GetActionName();//得到action名称
            object controllerInstance = ControllerObjectContainer.GetInstance()[controllerName];//得到controller实例
            Reflector reflector = new Reflector();
            reflector.InvokeMethod(controllerInstance, actionName);//执行相应的action
        }
        #endregion
    }
}


2.2Controller程序

为了更好的设计,这里我们采用asp.net mvc的方式,使所有的controller类都继承于我们controller基类,对于整个action向view传值的过程我们也封装成controller类的一个方法。除此之外controller基类还包含诸如response、request、server、session这样的对象,以及它能够像asp.net mvc一样在action之间跳转…具体我们看看代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Diagnostics;
using Cmj.Common;
using Cmj.MyData;
namespace Cmj.MyWeb.MyMvcFramwork
{
    public class Controller
    {
        public ViewDataDictionary ViewData;
        public TempDataDictionary TempData;
        public Controller()
        {
            this.ViewData = new ViewDataDictionary();
            this.TempData = new TempDataDictionary();
        }
        public System.Web.HttpContext HttpContext
        {
            get{return System.Web.HttpContext.Current;}
        }
        public System.Web.HttpResponse Response
        {
            get{return System.Web.HttpContext.Current.Response;}
        }
        public System.Web.HttpRequest Request
        {
            get { return System.Web.HttpContext.Current.Request; }
        }
        public System.Web.HttpServerUtility Server
        {
            get{return System.Web.HttpContext.Current.Server;}
        }
        public System.Web.SessionState.HttpSessionState Session
        {
            get{return System.Web.HttpContext.Current.Session;}
        }
        public void View()
        {
            string url= System.Web.HttpContext.Current.Request.Url.ToString();
            UrlHelper urlHelper = new UrlHelper(url);
            string controllerName=urlHelper.GetControllerName();
            StackTrace st = new StackTrace();
            string actionName=st.GetFrame(1).GetMethod().Name;//得到当前执行的方法的名称
            //上面之所以没有使用urlHelper方法得到actionName,主要是那样取的换对于redirectToAction情况可能会造成错误,因为redirectToAction时候url中的action很可能不是其要执行的action的名字
            NVelocityHelper nvelocityHelper = new NVelocityHelper(@"/Views/" + controllerName,actionName+".htm");
            foreach (string name in ViewData.Data.Keys)
            {
                nvelocityHelper.Put(name, ViewData.Data[name]);
            }
            ViewData.Data.Clear();
            foreach (string name in TempData.Data.Keys)
            {
                nvelocityHelper.Put(name, TempData.Data[name]);
            }
            nvelocityHelper.Display();
        }
        public void View(string viewName)
        {
            string url = System.Web.HttpContext.Current.Request.Url.ToString();
            UrlHelper urlHelper = new UrlHelper(url);
            string controllerName = urlHelper.GetControllerName();
            NVelocityHelper nvelocityHelper = new NVelocityHelper("/Views/" + controllerName, viewName + ".htm");
            foreach (string name in ViewData.Data.Keys)
            {
                nvelocityHelper.Put(name, ViewData.Data[name]);
            }
            ViewData.Data.Clear();
            foreach (string name in TempData.Data.Keys)
            {
                nvelocityHelper.Put(name, TempData.Data[name]);
            }  
            nvelocityHelper.Display();
        }
        public void File(string fileFullName)
        {
            FileDownload.DownLoadByResponseOutPutStream(HttpContext.Current.Response, fileFullName);
        }
        public void File(string fileName,string content)
        {
            FileDownload.DownLoadWithDynimic(fileName, content);
        }
        public void Content(string content)
        {
            HttpContext.Current.Response.Write(content);
        }
        public void Json(object obj)
        {
            HttpContext.Current.Response.ContentType = "application/json";
            //HttpContext.Current.Response.ContentType = "text/html";
            HttpContext.Current.Response.Write(JsonHelper.GetJsonFromObject(obj));
        }
        public void Javascript(string script)
        {
            HttpContext.Current.Response.ContentType = "application/x-javascript";
            //HttpContext.Current.Response.ContentType = "text/html";
            HttpContext.Current.Response.Write(script);
        }

        public void Redirect(string url)
        {
            this.Response.Redirect(url);
        }
        public void RedirectToAction(string actionName)
        {
            string url = HttpContext.Current.Request.Url.ToString();
            UrlHelper urlHelper = new UrlHelper(url);
            string controllerName = urlHelper.GetControllerName();
            object cotrollerInstance = ControllerObjectContainer.GetInstance()[controllerName];
            Reflector reflector = new Reflector();
            reflector.InvokeMethod(cotrollerInstance, actionName);
        }
    }
}


2.3主要的类图:

上面是主要程序代码(注意不是主要的代码,可以说是主干),其他细致的代码我就不再贴出来了,可以动动脑子,这里主要类图。




3.使用效果

3.1目录结构




这是程序目录结构,基本上和asp.net mvc是完全一样的,注意我们是基于HttpHandler实现的,所以别忘了在web.config中注册一下。

3.2程序代码

这里是controller代码,实现了我们的基类controller,里面有三个action,是一个简单的登录操作(注意这里简单起见就不在再Modules中构建数据了)。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Cmj.MyWeb.MyMvcFramwork;
namespace WebApplicationForTest.Controllers
{
    public class HomeController:Controller
    {
        public void Index()
        {
            this.ViewData["cs"] = "欢迎使用cmj mvc开发框架!!!";
            this.View();
        }
        public void Login()
        {
            if (this.Request["userPwd"] == "123")
            {
                this.ViewData["info"] = this.Request["userName"] + "您好,欢迎光临本站!";
                this.View("Default");
            }
            else
            {
                View("Error");
            }
            
        }
    }
}


3.3运行效果

程序运行,首先我们是进入默认界面(注意在Global.asax中我们已经设置了程序默认的controller和action,这点很容易实现在urlHelper中实现,我们设置的默认controller是Home,action是Index)所以直接访问进入我们的HomeController中的Index中




我们输入用户信息,用户密码我输入的是123,所以依照程序会到Default中(注意事实上是用Login这个action处理的,所以url中是显示的是Home/Login)




如果输入不正确呢,依照程序跳转到Error这个View中




4.总结

程序主要思路就是根据请求提炼出controller和action,然后通过反射调用相应的action,而在action中我们将数据放入NVelocity的上下文中,在前台视图中读取并组织数据。

Ok,很简单吧,使用起来也还不错O(∩_∩)O~,就到这里吧,如果不是太明白,可以和我联系(jianxin160@hotmail.com),源代码的话就先不放到这里了吧,原理也不复杂,大家动动脑筋。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: