在我前面一篇博文《逃脱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

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

自己动手做Web框架—MVC+Front Controller的更多相关文章

  1. MVC+Front Controller

    MVC+Front Controller 在我前面一篇博文<逃脱Asp.Net MVC框架的枷锁,使用Razor视图引擎>发表之后,很多人关心,脱离了之后怎么办?那么这可以说是它的续篇了. ...

  2. python web框架 MVC MTV

    WEB框架 MVC Model View Controller 数据库 模板文件 业务处理 MTV Model Template View 数据库 模板文件 业务处理

  3. 自己动手写web框架----1

    本文可作为<<自己动手写struts–构建基于MVC的Web开发框架>>一书的读书笔记. 一个符合Model 2规范的web框架的架构图应该如下: Controller层的Se ...

  4. 自己动手写web框架----2

    在上一节,我们自己写的web框架,只能运行显示一个HelloWorld.现在我们对其进行一次加工,让他至少能运行一个登陆程序. 首先看login.jsp <%@ page contentType ...

  5. web框架--MVC、MTV

    一.MVC框架: MVC 是一种使用 MVC(Model View Controller 模型-视图-控制器)设计创建 Web 应用程序的模式:[1] Model(模型)表示应用程序核心(比如数据库记 ...

  6. JAVA web 框架集合

    “框架”犹如滔滔江水连绵不绝, 知道有它就好,先掌握自己工作和主流的框架: 在研究好用和新框架. 主流框架教程分享在Java帮帮-免费资源网 其他教程需要时间制作,会陆续分享!!! 152款框架,你还 ...

  7. jS正则和WEB框架Django的入门

    JS正则 -test 判断字符串是否符合规定的正则表达式 -exec 获取匹配的数据 test的例子: 从上述的例子我们可以看出,如果rep.test匹配到了就返回true,否则返回false exe ...

  8. Python 17 web框架&Django

    本节内容 1.html里面的正则表达式 2.web样式简介 3.Django创建工程 Html里的正则表达式 test 用来判断字符串是否符合规定的正则       rep.test('....')  ...

  9. 冰冻三尺非一日之寒--web框架Django

    1.JS 正则    test   - 判断字符串是否符合规定的正则        rep = /\d+/;        rep.test("asdfoiklfasdf89asdfasdf ...

随机推荐

  1. css部分的复习

    常见的块元素有<h1><h6>.<p><div><ul><li><ol>等,其中<div>标记是最典型的 ...

  2. BootLoader 详解(1)

    1. Boot Loader的概念 BootLoader就是在操作系统内核运行前之前运行的一段小程序.通过这段小程序,可以初始化硬件设备.建立内存空间映射图,从而将系统的软硬件带到一个合适的状态,以便 ...

  3. json 特殊字符 javascript 特殊字符处理(转载)

    特殊字符以前都是禁止页面输入,这样就简单不容易出错,但最近需求要求能输入特殊字符整理出java返回json时特殊字符的转义(不转义会破坏json数据格式导致页面读取数据出错) Java代码 publi ...

  4. PLAN表

    用得较多的PLAN表有以下三个ABPPMGR:MANUFACTURINGPLN.SHIPMENTPLAN.PROCUREMENTPLAN .这三个表都是执行StartFP中的exportFP进行数据导 ...

  5. 转 对菜鸟开发者的叮咛:花一万个小时练习Coding,不要浪费一万小时无谓地Debugging

    原文见http://blog.jobbole.com/74825/ Coding 之于科技的重要性不言可喻,也不再是软体工程师的专利,医师.律师.会计师.护理师.金融从业人员,甚至是听起来摸不着边的政 ...

  6. Linux 夸平台 移植 Win32

    1.代码格式 大量的 警告 不识别的字符(936),请保存为unicode 以免丢失数据,好多参考说忽略此警告. 但是很多错误都是由于这个警告引起的.将大量的.h .cpp 的utf 8 数据用txt ...

  7. 简单验证码实现(Ajax)

    前端页面: <!--验证码输入框 --> <input type="text" class="entry" value="" ...

  8. Selenium2+python自动化3-解决pip使用异常

    一.pip出现异常 有一小部分童鞋在打开cmd输入pip后出现下面情况:Did not provide a commandDid not provide a command?这是什么鬼?正常情况应该是 ...

  9. python--自动删除文件

    1.目的:定期自定删除7天前的数据 python脚本如下: #coding=utf-8 import os,time,datetime #需定时删除的目录的上一层路径 data_dir="/ ...

  10. 阅读笔记 The Impact of Imbalanced Training Data for Convolutional Neural Networks [DegreeProject2015] 数据分析型

    The Impact of Imbalanced Training Data for Convolutional Neural Networks Paulina Hensman and David M ...