node-framework
Version:
node-framework
156 lines (131 loc) • 9.81 kB
Markdown
# controllers
控制器(controllers)是web应用的核心,用以处理各种http请求。控制器内部由一个或多个行为(action)构成,每个action即为web应用对外暴露的最小单元。用户通过输入URL,经过route的路由映射到特定的controller和action,从而完成特定的处理后返回相应的内容。
不同于[express](https://github.com/strongloop/express)框架,node-framework的controller独立于route之外,是单独包装成的类。controllers目录中的每个文件均为一个controller。单个文件内部的每个exports的方法或对象均为一个action。
## 使用方法
因为默认路由配置中已经包含了如下基础路由规则:
```javascript
// 默认路由配置规则
'/:controller/:action/:id((\\d+))': {
controller: '{controller}',
action: '{action}'
},
'/:controller/:action': {
controller: '{controller}',
action: '{action}'
},
'/:controller': {
controller: '{controller}'
}
```
因此当用户访问`host:port/myController/myAction`时,不需额外配置或者注册,系统会自动调用controllers/myController.js中exports的名为'myAction'的方法或对象。下面将主要关注controller的编写方式,有关路由设置规则请参见[这里](/config#routes)
controller共有两种编写方式,对象方式用以处理较为简单的应用场景;类方式因为拥有构造函数从而能够在其内部创建共用变量或逻辑,因此常用于处理略微复杂的应用场景。
### 对象方式
对象方式要求controller.js内部直接exports一个对象。该对象的key为action的名字;value为处理方法,接受两个参数,分别为request对象和response对象。通过调用response对象的`send`、`redirect`、`render`等方法返回响应,完成一次请求。示例代码如下:
```javascript
// 对象方式编写的示例controller文件:controller/api.js
module.exports = {
// 正常响应
normal: function (req, res) {
res.send('normal');
},
// 报错响应
error: function (req, res) {
setTimeout(
function () {
throw new Error();
},
20
)
},
// 长时间响应
responseWarning: function (req, res) {
setTimeout(
function () {
res.send('response in 1000ms');
},
1000
);
}
};
```
### 类方式
类方式要求controller.js内部创建并exports一个类。这个类必须继承`require('node-framework').Controller`,并且必须在构造函数第一句调用`Controller.apply(this, Array.prototype.slice.call(arguments, 0));`。创建的类的每个原型方法为一个action。这个方法并不接受参数,但是在内部可以通过调用`this.req`和`this.res`来获取request和response对象。这种编写方式在多个action需要共用同一个变量时推荐使用,如下述示例中共用`monitorModel`的情况。
```javascript
// node-monitor模块controller/api.js,因为共用monitorModel,所以采用类方式编写。
var util = require('util');
var Controller = require('node-framework').Controller;
var MonitorModel = require('../model/Monitor');
function ApiController() {
// 必须位于构造函数第一句
Controller.apply(this, Array.prototype.slice.call(arguments, 0));
// 创建共用变量
this.monitorModel = new MonitorModel(this.app);
}
// 必须继承node-framework提供的Controller基类
util.inherits(ApiController, Controller);
// 创建action
ApiController.prototype.actionStats = function () {
// 调用共用变量monitorModel的方法
var def = this.monitorModel.insert('stats', {
id: body.appid,
appkey: body.appkey
}, insertObj);
def.then(function () {
me.res.send('success');
}, function (err) {
console.log(err);
me.res.send('fail');
});
};
// 创建第二个action
ApiController.prototype.actionStatus = function () {
// 调用共用变量monitorModel的方法
this.monitorModel.insert('status', {
id: body.appid,
appkey: body.appkey
}, {
appid: body.appid,
timestamp: body.timestamp,
data: body.data,
host: body.host
}).then(function () {
me.res.send('success');
}, function (err) {
console.log('actionStatus error occured!\n');
console.log(err);
me.res.send('fail');
});
};
```
## API
虽然node-framework并非基于express,但其内部实现和包装使得request和response对象基本等价于express框架的req和res。这里简单列出request对象和response对象包含的常用信息,以供参考,更详细的信息也可参见[express API](http://expressjs.com/api.html)
### request对象包含属性如下:
名称 | 类型 | 使用示例 | 说明
-----|------|----------|------
path | string(@CLASS=type type-string) | `req.path` | 返回请求地址,例如用户输入`example.com:3000/users?sorc=desc`,返回`'/users'`
hostname | string(@CLASS=type type-string) | `req.hostname` | 返回请求域名,不带端口号。例如用户输入`example.com:3000/users?sorc=desc`,返回`'example.com'`
cookies | object(@CLASS=type type-object) | `req.cookies` | 获取所有cookie,返回的对象key为cookie具体字段的名字,value为cookie值。如要获取具体某项字段的值,也可以直接使用`requrest.cookies.someKey`。
ip | string(@CLASS=type type-string) | `req.ip` | 返回请求者IP地址
xhr | boolean(@CLASS=type type-boolean) | `req.xhr` | 返回是否为xhr请求。内部是通过检查`req.header('X-Requested-With') === 'XMLHttpRequest'`实现的。
### request对象包含方法如下:
名称 | 参数类型 | 返回值类型 | 使用示例 | 说明
-----|----------|------------|----------|------
param | string(@CLASS=type type-string) | string(@CLASS=type type-string) | `req.param('key')` | 获取请求附带的参数,获取顺序按照params(URL上?之后的参数),body(POST的参数),query(路由规则中:后的参数)。如果不包含该参数返回`undefined`
get | string(@CLASS=type type-string) | string(@CLASS=type type-string) | `req.get('referrer')` | 获取请求头特定字段的信息
header | string(@CLASS=type type-string) | string(@CLASS=type type-string) | `req.header('referrer')` | 同`get`
accepts | string(@CLASS=type type-string) | string(@CLASS=type type-string) | `req.accepts(‘text/html')` | 检查输入参数的类型是否被接受。如果接受返回最佳接受的类型,否则返回`undefined`。
is | string(@CLASS=type type-string) | boolean(@CLASS=type type-boolean) | `req.is('text/html')` | 检查请求是否包含`Content-Type`字段并且是否匹配输入参数的类型。
### response对象包含方法或属性如下:
名称 | 参数类型 | 返回值类型 | 使用示例 | 说明
-----|----------|------------|----------|------
render | string(@CLASS=type type-string)\[object\](@CLASS=type type-object) | void(@CLASS=type) | `res.render('some/view', (@BR){key1: 'value1', key2: 'value2'});` | 选择某个前端模板进行渲染并作为响应输出。前端模板的基准地址在`config/global.js`的`templateDir`字段。
redirect | \[number\](@CLASS=type type-number)string(@CLASS=type type-string) | void(@CLASS=type) | `res.redirect(301, '/other/path');` | 将响应重定向到某个地址,可以是站内地址也可以是站外地址。第一个参数表示HTTP状态码(例如301),该参数可选。
send | string(@CLASS=type type-string) | void(@CLASS=type) | `res.send('Hello World!');` | 发送指定字符串作为HTTP响应。参数也可以是字符流或者对象,详情参见[express API](http://expressjs.com/api.html#response)。
json | object(@CLASS=type type-object) | void(@CLASS=type) | `res.json({message: 'Hello World!'});` | 发送指定对象以JSON格式作为HTTP响应。
jsonp | object(@CLASS=type type-object) | void(@CLASS=type) | `res.jsonp({message: 'Hello World!'});` | 发送一个jsonp响应。该响应会根据URL参数中`callback`字段的值作为方法名,并将参数对象作为方法参数响应。例如用户访问`/info?callback=sayHi`,示例方法会响应`sayHi({message: 'Hello World!'})`(@BR)jsonp的方法名称字段名可以在`config/server.js`中的`jsonpCallback`配置项更改,默认为`'callback'`
sendFile | string(@CLASS=type type-string) | void(@CLASS=type) | `res.sendFile('/path/to/file');` | 将文件内容读取出来并写入响应。内部使用[send](https://github.com/visionmedia/send#options)。更多信息也可参见[express API](http://expressjs.com/api.html#response)。
end | - | void(@CLASS=type) | `res.end();` | 不发送任何数据直接关闭连接,避免因超时而抛错。
status | number(@CLASS=type type-number) | object(@CLASS=type type-object) | `res.status(400)` | 设置HTTP状态码,返回response本身,因此可以继续链式调用,如`res.status(404).send('Not Found!');`。
set | string(@CLASS=type type-string)string(@CLASS=type type-string) | void(@CLASS=type) | `res.set('Content-Type', 'text/plain')` | 设置响应头信息。也可以将一个对象传入作为唯一参数,对象的key为响应头字段,value为响应头的值。
get | string(@CLASS=type type-string) | string(@CLASS=type type-string) | `res.get('Content-Type')` | 获取响应头信息。
cookie | string(@CLASS=type type-string)string(@CLASS=type type-string)\[object\](@CLASS=type type-object) | void(@CLASS=type) | `res.cookie('rememberme', '1', (@BR){(@BR)httpOnly: true, (@BR)expires: new Date(Date.now() + 900000)(@BR)})` | 设置用户cookie,其他options参数可参见[express API](http://expressjs.com/api.html#response)。