zoukankan      html  css  js  c++  java
  • web form中自定义HttpHandler仿mvc

    web form中自定义HttpHandler仿mvc

    前言

      在mvc大行其道的今天,仍然有不少公司的项目还是使用web form来实现的(其实mvc也是基于web form的),如果要在项目中引入mvc,不得不新建一个mvc的项目,然后将当前项目的功能一点点的转移过去,实在是很麻烦的一件事情,而且项目的改造周期也会加长,更别说一边改造一边添加新功能了,那么如果中间出现那么一点点的小差错,那么开发人员和测试人员估计想死的心都有了。

      基于以上的情景,我们可以通过自定义HttpHandler来仿造mvc的模式,大概的实现思路如下:

    1. 给页面提供一个PageBase<TModel>的类来继承,中间类似于mvc中存放Model的容器
    2. 通过类似/mvc/controller/action方式的url对于Controller内Action的调用(之前《C#实现简易ajax调用后台方法》这篇文章有简单介绍过)
    3. 不同的action返回不同的ActionResult(如文本、Json等)
    4. 将自定义的MvcHandler在web.config中进行配置并引用相关的库即可

    实现

      首先我们需要自定义一个IHttpHandler来处理我们定义的mvc规则,并对其进行解析,其实原理就是上面提到的文章,只是Controller的Action会跟mvc的相似,返回ActionResult,代码大致如下:

    publicabstractclassActionResult{publicabstractvoidExecuteResult(HttpContext context);}publicclassMvcHandler:IHttpHandler,IRequiresSessionState{publicconststring PREFIX ="/mvc/";//其他代码略publicvoidProcessRequest(HttpContext context){string path = context.Request.AppRelativeCurrentExecutionFilePath.Substring(PREFIX.Length);Int32 index = path.LastIndexOf("/");string route = path.Substring(0, index).ToLower();string actionName = path.Substring(index +1);//反射获取Controller和Actionvar controller =null;var action =null;var actionParamters = action.GetParameters();object[] parameters =Array.ConvertAll(actionParamters, p =>{if(p.ParameterType==typeof(HttpPostedFile)){return context.Request.Files[p.Name];}returnConvert.ChangeType(collection[key], type);});var result = action.Invoke(controller, parameters,null)asActionResult;if(result !=null)
                result.ExecuteResult(context);}  

      然后在web.config内的HttpHandlers内添加<add path="/mvc/*/*" type="Infrastructure.MvcHandler" verb="POST,GET"/>,规则可以任意定制,但是得注意url的格式,如果定义成了*/*/*那么多拦截到全部的请求,那么难度就增加了。

      接下来是页面,与以往aspx页面不同的是,我们需要在页面上调用到相应的Model,那么对于PageBase<TModel>就需要一个可以get Model的属性,代码如下:

    publicclassDynamicPageBase:Page{public T Model{protectedget;set;}}

      但是由于我们在页面内调用Model之前,是要对其赋值的,因此就需要一个接口,代码改造如下:

    publicinterfaceIMvcPage{voidSetModel(object model);}publicclassDynamicPageBase:Page,IMvcPage{private T m_Model =default(T);protected T Model{get{return m_Model;}}publicvoidSetModel(object model){if(model !=null)
                m_Model =(T)model;}}

      在页面上,我们就可以使用<%=Model.XXX%>的方式来获取Model内的相关属性了,对于页面的改造大致已经完成了

      那么我们怎么样像mvc那样通过/controller/action的方式来返回html呢,使用过mvc的朋友应该知道,我们的view是要放在一些特定的位置下的,如相应的Controller文件夹内包含着相应的Action aspx页面或razor页面

      因此我们也可以在Web Form的目录下创建一个Views的文件夹,专门用来存放所有对应的Action页面,然后通过对url的解析来获取相应的页面,并将页面转化为html返回给客户端,ViewResult大致代码如下:

    string html ="";try{string childPath = context.Request.AppRelativeCurrentExecutionFilePath.Replace(MvcHandler.PREFIX,string.Empty);string virtualPath =string.Format("~/Views/{0}.aspx", childPath);IMvcPage page =PageParser.GetCompiledPageInstance(virtualPath, context.Server.MapPath(virtualPath), context)asIMvcPage;if(page !=null)
            page.SetModel(m_model);using(StringWriter sw =newStringWriter()){
            context.Server.Execute(page, sw,false);
            html = sw.ToString();}}catch(Exception){
        html ="无法访问该视图";}
    context.Response.Write(html);

      其他的ActionResult都是根据返回类型的不同而有不同的实现,我就不详细列举出来了。

    扩展

      相信留意过老赵博客的朋友都看过《技巧:使用User Control做HTML生成》《方案改进:直接通过User Control生成HTML》这两篇关于UserControl的文章,那么我们可以参考里面的实现来对页面也添加相似的功能,并整合两种方案,让你的ViewResult可以生成aspx或ascx的html,我自己实现的规则是在页面不存在的情况下,则查找对应的UserControl是否存在,如果存在则返回UserControl的html,不存在的话则返回以上的无法访问视图的提示,代码改造大致如下:

    //MvcHandler内string pageVirtualPath ="页面虚拟路径";string controlVirtualPath ="用户控件虚拟路径";//aspxif(File.Exists(context.Server.MapPath(pageVirtualPath))){var page = manager.LoadPage(pageVirtualPath)asIMvcPage;if(page !=null)
            page.SetModel(m_model);
        html = manager.RenderView();}//ascxelseif(File.Exists(context.Server.MapPath(controlVirtualPath))){var control = manager.LoadControl(controlVirtualPath);
        html = manager.RenderView();}else{
        html ="无法访问该视图";}publicclassViewManager{//其他代码略publicPageLoadPage(string virtualPath){
            m_page =PageParser.GetCompiledPageInstance(virtualPath, m_context.Server.MapPath(virtualPath), m_context)asPage;
            s_cache.SetViewPropertyValue(m_page, m_context.Request);return m_page;}publicControlLoadControl(string virtualPath){
            m_page =newPage();
            m_control = m_page.LoadControl(virtualPath);
            m_page.Controls.Add(m_control);
            s_cache.SetViewPropertyValue(m_control, m_context.Request);return m_control;}}

      对于MvcHandler而言,我们可以将部分的可变参数抽离出去,然后额外的进行实现,那么仿mvc的代码就可以整理到一个dll中,可以让其他的项目重用了。

      然后就是可以在MvcHandler内再添加一些Filter的功能,抽离出过滤的接口,来对于一些请求的过滤,那么功能上就可以被进一步的扩展了。

    结尾

      由于以往在写文章的时候,都会提供详细的实现源码,但是后来发现这样并不能给其他人自己实现的机会,因此这次就不提供源码了,大部分重点的想法已经在文章中了,大家可以尝试自己去实现,由于写的文章也不多,如果有阅读上的困难,请告诉我,我会发源码给各位,文章中如有任何错误和遗漏请大家指出,谢谢大家。

     

    简单的mvc之一:简单的开始

      mvc学习到现在,相对所学到的一系列的知识做一个总结,于是就有了这个标题—简单的mvc。文如名,写的是简单的mvc的知识,目标群也不言而喻。这一篇来个简单的开始,从头建立一个web项目,比如hello world。

      asp.net项目的请求处理核心是IHttpHandler,不论是之前的Page,还是之后MVC。所以最简单的web项目,就是只有一个IHttpHandler的项目。项目只有两个文件,一个.ashx文件,内容:

    <%@ WebHandler Class="Danyuers.SimpleMvc.Hello" Language="C#" %>

    另外一个是相应的代码文件,建立一个名为Hello的类,也就是上面的Class属性所指向的类别,代码如下:

    复制代码
    namespace Danyuers.SimpleMvc {
        public class Hello : IHttpHandler {
            public bool IsReusable {
                get { return false; }
            }
    
            public void ProcessRequest(HttpContext context) {
                context.Response.Write("<h1>hello world!</h1>");
                context.Response.End();
            }
        }
    }
    复制代码

    然后编译(请将编译目标文件改为bin),映射到虚拟目录,通过localhost:xxxx/xx.ashx即可访问到建立的项目。记得添加相应的引用(system.web)。

      好了,简单的项目搭建已经完成,但这不是真正的mvc,mvc最直观的表现就是路由映射,区别于webform的文件映射。一个简单的mvc项目需要哪些东西呢?第一,global.asax文件,定位到HttpApplication;第二,路由映射表,定义路由;第三,路由映射对象,也就是控制器。如同上面的ashx一样,global.asax也只是包含一行指令:

    <%@ Application Codebehind="Global.cs" Inherits="Danyuers.SimpleMvc.MvcApplication" Language="C#" %>

    还是同上,建立global.cs,在其中建立SimpleMvcApplication,代码如下:

    复制代码
    using System;
    using System.Web;
    using System.Web.Mvc;
    using System.Web.Routing;
    
    namespace Danyuers.SimpleMvc{
        public class MvcApplication : HttpApplication {
            protected void Application_Start() {
                RouteRegister.Regist(RouteTable.Routes);
            }
        }
    }
    复制代码

    注意其中的RouteRegister,其中定义了我们需要的路由映射。建立单独的代码文件RouteRegister.cs,代码如下:

    复制代码
    using System;
    using System.Web.Mvc;
    using System.Web.Routing;
    
    namespace Danyuers.SimpleMvc{
        internal class RouteRegister {
            public static void Regist(RouteCollection routes) {
                routes.RouteExistingFiles = false;
                routes.Ignore("{resources}.axd/{pathInfo*}");
    
                routes.MapRoute(
                    name: "default",
                    url: "{controller}/{action}/{id}",
                    defaults: new { controller = "Hello", action = "Index", id = UrlParameter.Optional },
                    namespaces:new String[]{"Danyuers.SimpleMvc.Controllers*"}
                );
            }
        }
    }
    复制代码

    从其中可以很明显看到建立一个通用路由:{controller}/{action}/{id}。最后一步,建立控制器。建立Hello.cs,代码如下:

    复制代码
    using System;
    using System.Web.Mvc;
    
    namespace Danyuers.SimpleMvc.Controllers {
        public class HelloController:Controller{
            public void Index() {
                Response.Write("<h1>Hello,world!!</h1>");
                Response.End();
            }
        }
    }
    复制代码

    好了,最后编译,iis映射,运行即可,相较于最初的单httphandler项目,这次直接访问域名即可。

      相较于webform,mvc的代码分布更加分散,却也更加整洁,也更加富有弹性。

     
    分类: C#想法
  • 相关阅读:
    tyvj 1031 热浪 最短路
    【bzoj2005】 [Noi2010]能量采集 数学结论(gcd)
    hdu 1394 Minimum Inversion Number 逆序数/树状数组
    HDU 1698 just a hook 线段树,区间定值,求和
    ZeptoLab Code Rush 2015 C. Om Nom and Candies 暴力
    ZeptoLab Code Rush 2015 B. Om Nom and Dark Park DFS
    ZeptoLab Code Rush 2015 A. King of Thieves 暴力
    hdoj 5199 Gunner map
    hdoj 5198 Strange Class 水题
    vijos 1659 河蟹王国 线段树区间加、区间查询最大值
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3289305.html
Copyright © 2011-2022 走看看