自己動手做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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM