• 自己动手做Web框架—MVC+Front Controller


    在我前面一篇博文《逃脱Asp.Net MVC框架的枷锁,使用Razor视图引擎》发表之后,很多人关心,脱离了之后怎么办?那么这可以说是它的续篇了。
    同时,这也是eLiteWeb开源软件的一部分。

    MVC + Front Controller

    我 们常常提到的MVC中作为Controller的C。其实有两项任务,一个是处理Http请求,另一个是对请求中的用户数据进行的处理。前者,有:安全认 证,Url映射等。Front Controller 模式就是把这个C进一步分离。两个责任两个类(单一责任原则)。因此,这里给我的MVC模式,赋予新的内涵C => Command,以诠释两个模式的融合。

    非我族类,拒之门外 --- 转换器BasicHttphandler

    这是一个Adapter目的就是为了把ASP.Net环境转化为我自定义的Web抽象。

    首先就是BasicHttphandler本身实现了IHttpHandler,并在Web.config中设置为默认的系统HttpHandler,把控制权拿了过来,我的世界我做主。

    其次,把HttpContext转换为自定义的WebRequest,然后传递给Front Controller作进一步的处理处理。

    public class BasicHttpHandler:IHttpHandler
        { public class BasicHttpHandler:IHttpHandler
        {
            private FrontController front_controller;
            private WebRequestAdapter web_request_adapter;
    
            public BasicHttpHandler(WebRequestAdapter webRequestAdapter, FrontController frontController)
            {
                web_request_adapter = webRequestAdapter;
                front_controller = frontController;
            }
            public BasicHttpHandler()
                : this(Container.get_a<WebRequestAdapter>(),Container.get_a<FrontController>()) {}
    
            public void ProcessRequest(HttpContext context)
            {
                front_controller.process(web_request_adapter.create_from(context));
            }
    
            public bool IsReusable
            {
                get {return true; }
            }
        }

    总阀门 --- Front Controller

    它的实现也很简单,就是通过命令解析器CommandResolver,找到可执行的命令,传入WebRequest进行处理。

    [RegisterInContainer(LifeCycle.single_call)]
        public class FrontControllerImpl : FrontControllers.FrontController
        {
            private CommandResolver command_resolver;
    
            public FrontControllerImpl(CommandResolver commandResolver)
            {
                command_resolver = commandResolver;
            }
    
            public void process(WebRequest request)
            {
                command_resolver.get_command_to_process(request).process(request);
            }
        }

    从流程上,到这里整个处理已经完成;剩下的可以看作是你自己功能的扩展。

    以下可以看作是我的一个具体简单实现。

    Command系列接口

    前 面提到的命令解析器我就是简单用到一个Command集合(IEnumerable<Command>),而寻找执行命令这一逻辑,是通过 Command自身的方法can_process(WebRequest)的调用,从而巧妙的把责任分布到每个具体Command自身去了。这就是集中规则,分散责任。其实,依赖注入的实现中,声明式注入(RegisterInContainerAttribute)也是类似的场景。

    [RegisterInContainer(LifeCycle.single_call)]
        public class CommandResolverImpl : CommandResolver
        {
            private IEnumerable<Command> available_commands;
    
            public CommandResolverImpl(IEnumerable<Command> availableCommands)
            {
                available_commands = availableCommands;
            }
    
            public Command get_command_to_process(WebRequest request)
            {
                return available_commands.First(x => x.can_process(request));
            }
        }


    仔细看看Command,这一接口又分解为两个粒度更小的接口:DiscreteCommand和过滤器Command。

    public  interface Command : DiscreteCommand, CommandFilter
        {
        }
    
     public interface DiscreteCommand
        {
            void process(WebRequest request);
        }
    
     public interface CommandFilter
        {
            bool can_process(WebRequest request);
        }


    从它们各自带的方法可以清晰的看到它们的角色分工,前者是具体处理用户数据,之后的所有具体命令处理类,如Index, Home都要实现这个接口,一个方法,从而其间简洁与单纯性已是做到了极致;后者就是命令过滤,承担选择可执行命令的责任,Url的路由映射就实现这个接 口,我这里只简单实现了用正则映射(过滤)器 RegularExpressFilter。

    public class RegularExpressFilter:CommandFilter
        {
            private readonly Regex regex;
    
            public RegularExpressFilter(string match)
            {
                regex = new Regex(match);
            }
    
            public bool can_process(WebRequest request)
            {
                return regex.IsMatch(request.Input.RequestPath);
            }
        }

    View

    视图的这一部分,就到跳到每一个具体的命令类中了,如 Index类中,通过调用WebRequest.Output.Display(View, Model),后台把调用传递到ViewEngin的一个实现类。需要知道更详细,可以到参考前文《代码整洁之道------Razor Compiler的重构

    便用示例

    当要为你的Web程序创建一个页面时,只有三步:

    第一步:创建一个类实现DiscreteCommand接口,并注册到Container中。在process(WebRequest)完成你需要的功能,我这只是显示一些文本,作为演示。

    [RegisterInContainer(LifeCycle.singleton)]
        public class Index:DiscreteCommand
        {
            public void process(WebRequest request)
            {
    
                request.Output.Display(new View("Index"),
                    @" <h3>卓越之行</h3>
    <p>宏卓科技公司专注于最新软件开发技术、开发流程和业务服务。让所有这些技术为了一个目标---您的业务服务. </p>
    <ul>
        <li> 使用行为/测试驱动方式追溯需求,驱动开发,不丢需求 </li>
        <li> 利用敏捷流程提高用户体验,降低风险 </li>
        <li> 使用良好的架构提高系统的扩展性和维护性,同时降低开发的可变成本 </li>
        <li> 利用对业务流程的深入了解,开发适用软件,提供业务服务,使服务与软件无缝结合、同步发展。</li>
    <ul>
    <p>
    终极目标:动成长软件,让我们的系统与你公司的业务一起成长。
    </p>
    "
                    );
            }


    第二步:在映射注册类RoutesRegistration中,填加一条映射记录.。因为我不已经用命令工厂类封装了正则过滤器,所以代码看起来简单而易读一些。

    public class RoutesRegistration:StartupCommand
        {
            private Registration registration;
    
            public RoutesRegistration(Registration registration)
            {
                this.registration = registration;
            }
    
            public void run()
            {
                var routes = Container.Current.get_a<RoutingTable>();
                var factory = new CommandFactory();
                routes.add(factory.match<Home>("Home.do"));
                routes.add(factory.match<Index>("Index.do"));
            }
        }


    第三步:创建Razor页面

    @inherits Skight.eLiteWeb.Presentation.Web.ViewEngins.TemplateBase<string>
    @{
        Layout = "_Layout.cshtml";
    }
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
    
      <title>Index 页面</title>
    </head>
    
      <body>
        <h2>宏卓科技 与你公司的业务一起成长!</h2> 
    
     <img src="/Theme/Index_pepole.jpg" style="float: left; margin-right: 50px;" />
    
        @Model
    
    </body>
    
    </html>

    总结:是的,这里的具体功能很简单,但是,相信你也看到了其强大的扩展性,如Url映射的扩展和Command扩展。与Asp.Net不同,我这里一个Web请求是用一个类来处,而不是一个方法,这样,继承、重用和扩展都很方便。

    最后一个优势:所有的处理类都是自定义的轻型类,继承层次较少,对外部的依赖为0,这个于性能是大有裨益的。

    这也是把轻型作为框架名称的含义:对外依赖的轻型,性能上的

    (本文版权属于© 2012 - 2013 予沁安 | 转载请注明作者和出处WangHaoBlog.com

    最后,一全景类图和序列图做结。

    1

    2

    3

  • 相关阅读:
    SimpleDateFormat解析的时区问题
    linux之cp/scp命令+scp命令详解
    java.net.SocketException: java.security.NoSuchAlgorithmException
    Gradle使用手册(一):为什么要用Gradle?
    js_实用
    exp.validate.js
    MySQL实用技巧
    MongoDB 用户配置
    js 图片处理 Jcrop.js API
    MySQL连接池
  • 原文地址:https://www.cnblogs.com/Wonner/p/diy_web_framework_mvc_plus_front_controller.html
Copyright © 2020-2023  润新知