treefrog 之控制器

来源:互联网 发布:unity3d百度地图映射 编辑:程序博客网 时间:2024/06/04 22:12

控制器

控制器是网页应用程序的关键类.它接收浏览器的请求, 从模型(model)的角度调用业务逻辑, 在基本的结果上生成视图(view)的HTML, 返回请求响应.

定义操作(Actions)

操作(action)是基于请求URL的一种叫法, 它决定了调用控制器(controller)定义的何种方法.让我们给已经生成的骨架上增加一些操作(Action).首先, 在头文件声明的’pulic slots’部分的操作(action).请注意,如果你希望增加操作(action)的参数时, 这些参数应该声明成QString类型.当你声明任何其类型时,它们将不被识别(通过正常的方法).最多可以定义10个参数.

class T_CONTROLLER_EXPORT FooController : public ApplicationController{    Q_OBJECT    :public slots:   // 动作(action)定义在这里    void bar();    void baz(const QString &str);    :

完成后, 按照往常一样实现这些操作(actions).

操作(action)的选择判断

当一个URL字符串被请求时, 需要决定哪个操作(action)被调用才是正确的.默认情况下,将使用下面的规则.

/controller-name/action-name/argument1/argument2/ ...

这些参数将被当成”操作(Action)参数”.如果使用了超过10个参数, 只有10个参数有效.如果你希望使用11个或者更多参数,请按照下面的方法使用Post或者URL数据参数.这里有一些具体的例子.BlogContrller类的操作(actions)被每个用例调用.

/blog/show        -> show();/blog/show/2      -> show(QString("2"));/blog/show/foo/5  -> show(QString("foo"), QString("5"));

如果省略操作(Action),默认情况下会调用index操作.这个看起来像这样:

/blog   -> index();

如果请求的URL对应的操作(action)没有定义, 将返回状态码500(内部服务器错误)给浏览器.在大多数情况下,每个调用的操作(action)都会进行以下处理过程:

  • 请求的审查
  • 访问cookies和会话
  • 访问上传的文件
  • 调用模型(业务逻辑)
  • 传递变量到视图(view)
  • 请求建立HTML反馈

这些对于编程者来说,通常意味着许多的工作和大量的代码编写.因此, 导致的结果就是一些人编写了一些庞大而且复杂的控制器(controller).为了防止这种情况, 请仅在模型(model)实现业务逻辑.你应该一直保存控制器尽可能的简单.一旦你明白了操作(action)如何调用的机制, 还能够通过URL客户化操作(action).更多信息请参见URL 路由.

获取请求

一个HTTP请求由THttpRequest类表示.在控制器(controller)中, 你可以用httpRequest()方法获得ThttpRequest对象.

const THttpRequest & TActionController::httpRequest () const;

你可以在这里获得各自数据.

接收请求数据

从浏览器发送的HTTP请求包含方法(method), 头(header), 主体(body).到达服务器的数据,可以通过以下方式:

  • post数据 - 使用POST方式从表单提交的数据
  • URL参数(查询参数) - 附加在URL上在”?”后的参数,(格式为Key=value&…)*Action参数 - 在Action后的参数(“blog/edit/3”的第三部分) <-见上面的内容.

接下来的例子说明了如何在控制器中获取post数据.我们假设视图中已经有了一个input标签.

<input type="text" name="title" />

在服务器端要获取浏览器发送给控制器的值, 使用下面的代码:

QString val = httpRequest().formItemValue("title")

同时, 如果你希望将值转变为int整形, 可以使用Qt的toInt()方法.如果有大量的数据项发送,一个一个的获取显得有点麻烦.这里有另外的一种方法一次性获取所有数据.例如, 你可以将标签写成这样:

<input type="text" name="blog[title]" /><input type="text" name="blog[body]" />

在控制器内, 可以这样获得数据:

QVariantMap blog = httpRequest().formItems("blog");QVariant t = blog["title"];QVariant b = blog["body"]; :

请求的数据可以表现为hash格式.如何获取URL参数(查询参数)?这里有个例子:

http://example.com/blog/index?mode=normal

你可以使用下面的方法获得blog控制器(controller)的Index操作(action)的mode的值.

QString val = httpRequest().queryItemValue("mode");  //val ="narmal"

同时, 如果你想获得不是URL参数和post数据的数据, 你可以使用allParameters()和methods().为了在应用服务器端检查请求的数据是否是要求的形式, 还提供了验证器功能.更多信息请参见验证器章节.

传递变量到视图(view)

要传递变量到视图(view), 使用texport(variable)或者T_EXPORT(variable)宏.你可以指定的参数类型为QVariant,int,QString, QList, 或者 QHash. 当然你也可以指定一个自定义的模型(model)这儿是它的用法:

QString foo = "Hello world";texport(foo);// 或者int bar;bar = ...texport(bar);

说明:texport的参数必须指定为变量.你不能直接指定一个字符串(“Hello world”)或者数字(例如100).在视图(view)内要使用变量, 你必须首先用tfetch(Type,variable)声明变量.更多详细内容请参见视图(view)章节.

简单地说: 使用tfetch()传递一个对象到视图(view).

** 用户自定义的类的情况**当你需要传递一个新的类(自定义的类)到视图(view), 请件下面的宏增加到头文件的尾部.更多详细的信息请参见 “新建原始模型(original model)”节.

Q_DECLARE_METATYPE(ClassName)     // 请更换类名

Qt Q_DECLARE_METATYE已经提供的类不需要再声明.但是,如果需要传递一个模版类,声明还是需要的,如QHash和QList在这个列子中, 请在helpers/applicationhelper.h尾部添加下面的内容

:Q_DECLARE_METATYPE(QList<float>)

Q_DECLARE_METATYPE 宏的参数是一个类名. 但是,如果你包含了一个逗号(如 QHash <Foo, Bar>), 将会产生编译错误.在这种情况下, 名字应该使用typedef QHash <Foo, Bar>来声明.

typedef QHash<Foo, Bar> BarHash;Q_DECLARE_METATYPE(BarHash)

导出对象

我们视导出对象到视图(view)(在texport()方法设置的对象)为”导出对象”.

请求建立反馈内容

在处理完业务逻辑后,处理的结果作为HTML反馈将会返回.如果BlogController show 操作(action)被执行, 在views/blog/show.xxx中的反馈内容被模版名(扩展名基于模版系统)生成.要请求反馈内容, 你可以使用render()方法.

bool render(const QString &action = QString(), const QString &layout = QString());

如果你想反馈一个不同的模版,指定操作(action)名为参数即可.布局(layout)文件名可以被指定为布局参数.方法render()表示”请求 view/template 建立反馈内容”.这也称之为”绘图(drawing)”.

简单地说:要渲染模版,使用 render() 方法.

布局(Layout)

当年创建一个网站的是, 网页页眉,网页页脚还有一些其他的部分通常所有的页面中是通用的, 只是网页的内容不同而已.术语布局(layout)是给这些通用的部分使用的, 模版基于这些通用的部分.布局(layout)文件应该放在views/layouts文件夹内.

关于模版系统

TreeFrog目前为止采用了两种模版系统:OtamaERB.在ERB内,代码通过<%…%>嵌入.在Treefrog内,Otama系统有不同的处理方式, 因为它完全分离了逻辑(.tom) 和界面模版(.html).

  • ERB 使用文件扩展名: xxx.erb
  • Otama 使用文件扩展名: xxx.otm和xxx.html
    (xxx指操作(action)的名称)

定向到渲染一个字符串

要直接渲染一个字符串,使用 renderText()方法.

// "Hello world" 渲染这个字符串renderText("Hello world");

默认情况下, 是不使用布局(layout)的.如果想要使用布局, 你需要指定第二个参数为true.

// 渲染字符串 "Hello world" 同时使用布局(layout)renderText("Hello world", true);

关于布局(layout)的更多详细的信息请参见 视图(view) 章节.

重定向

要重定向浏览器到另外的URL, 可以使用redirect()方法.第一个参数, 指定一个QUrl类的实例.

// 重定向到 www.example.orgredirect(QUrl("www.example.org"));

你可以重定向到同一台主机的其他操作(action)上.

/ 重定向到Blog控制器的index操作redirect( url("blog", "index") );

下面是另外一个有用的url方法.它在你传递控制器名和操作名时返回相应的QUrl实例.要重定向到同一个控制器内的其他操作,可以忽略控制名.

// 重定向到同一个控制器的show操作redirect( urla("show") );

使用 urla()方法是非常重要的.

在重定向目标页显示信息

重定向指向了另外一个URL.这是因为, 从服务器的视角看, URL接收到了一个新的目标页,这样产生了调用另外一个控制器的操作的效果.Treefrog框架 有一种机制(通过变量)可以实现传递信息到重定向的控制器上.见下面的例子使用tflash()或T_FLASH()方法传递变量.

// foo - 瞬时对象(flash object)QString foo = "successfully";tflash( foo );

传递的对象在这里叫做”瞬时对象(flash object)”.瞬时对象(flash object)转换成重定向后的视图(view)的输出对象.正因为如此, 它能够被echo()或者eh()方法输出显示信息.

使用瞬时对象(flash object)的好处

事实上, 你可以创建一个完全没有瞬时对象(flash object)的网页应用.然而, 在遇到合适的情况下, 如果你使用它, 代码变得非常容易理解(一旦你开始用它).我个人认为保持每个操作(action)完全对立,减少各个操作(action)之间的依赖是比较好的也比更容易理解.我建议不要在单个请求上调用超过一个以上的操作(action).尽可能的保存一个请求一个操作(action)的关系.使用瞬时对象(flash object)时, 如果为几乎相同的内容建立独立分离的显示, 代码可以变得简单些.blogapp (教程)使用createshow 操作(action)的方式就是一个好的例子.虽然这些操作(action)的处理是不一样的,但都是仅仅显示一条blog的处理结果.在create操作, 成功完成录入后, 这个结果将显示在重定向的show操作上.同时,这个信息”Created successfully.”通过使用瞬时对象(flash object)显示.事实上, 一个应用不会都是这么简单的, 因为不是经常需要获取和使用瞬时对象(flash object).因此, 适度地使用它好了.综上所述, 在实践中大部分的操作(action)都归结于使用redirect()或者render()方法.

顺便说一下
按照上面所说的方式, 但一个重定向发生时,一旦访问到达服务器时处理就被中断了(服务器返回新URL,客户端按新URL再次发出请求).鉴于瞬时对象(flash object)是通过会话的方式实现的, 存活下来的对象可以在其他操作(action)环境下使用.

staticInitialize() 方法

当启动后, 程序只有一个进程.你可能希望提前从数据库读取一些初始化信息.这种情况下, 将处理过程写在ApplicationController#staticInitialize()内.

void ApplicationController::staticInitialize(){// 处理过程..}

当服务器进程启动后, staticInitialize()方法只会调用一次.然而,当你选择在MPM)中描述的PreFork 后,你必须要关注它, 不要让每次进程启动时建立太多的加载功能.

控制器(controller)实例的生命周期

控制器(controller)的实例在被调用前创建, 当操作(action)执行完后被销毁.这意味着控制器的创建和销毁是基于每个HTTP请求的.
这样规定后, 控制器(controller) 通常没有实例变量.请按同样的方式实现ApplicationController.

原创粉丝点击