sails(v0.12) 目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
├── Gruntfile.js
├── README.md
├── api // 核心
│   ├── controllers // 充当model和view的桥梁
│   ├── models
│   ├── policies
│   ├── responses
│   └── services
├── app.js
├── assets
│   ├── favicon.ico
│   ├── images
│   ├── js
│   ├── robots.txt
│   ├── styles
│   └── templates
├── config
│   ├── blueprints.js
│   ├── bootstrap.js
│   ├── connections.js
│   ├── cors.js
│   ├── csrf.js
│   ├── env
│   ├── globals.js
│   ├── http.js
│   ├── i18n.js
│   ├── local.js
│   ├── locales
│   ├── log.js
│   ├── models.js
│   ├── policies.js
│   ├── routes.js
│   ├── session.js
│   ├── sockets.js
│   └── views.js
├── config.yaml
├── package.json
├── tasks // 不启用grunt,可以直接删除tasks文件夹
│   ├── README.md
│   ├── config
│   ├── pipeline.js
│   └── register
└── views // 不需要服务端渲染,可以直接删除views文件夹
├── 403.ejs
├── 404.ejs
├── 500.ejs
├── homepage.ejs
└── layout.ejs

controller

由一系列被称为action的方法构成,actionroutes绑定在一起,所以当请求某一个路由时,触发对应的对应的路由方法,并返回一个响应(这里的响应主要指路由的分发)

controller的定义

sailscontroller定义在controller文件夹下,按照约定,全部采用pascal-case写法(及每个单词的首字母都是大写),并且必须必须以Controller.js结尾。

  • NOTE: 可以将controller分文件夹定义,那么此时子文件夹也会成为路由标示的一部分。

代码演示

SayController.js

1
2
3
4
5
6
7
8
9
10
11
// 一个controller就是一个纯对象,对象的属性为action的名字,值为对应的action的处理函数
module.exports = {
// req, res跟express中的一样,不过没有第三个参数next
// 如果有next的需求,那么应该见该逻辑写到policies中
hi: function (req, res) {
return res.send('Hi there!');
},
bye: function (req, res) {
return res.redirect('http://www.sayonara.com');
}
}

隐式路由(sails自动绑定)

1
2
/:controllerIdentity/:nameOfAction
/controller标识符/action的名字

拿上面的SayController.js来说,那么当程序启动时,就会自动绑定到这样的路由/say/hi, /say/bye
如果上面的controller在一个子文件夹中/we中,那么就会绑定到这样的路由/we/say/hi, /we/say/bye

显式路由(自定义路由)

手动绑定路由,可以在config/routes.js中按照如下规则进行路由的配置:
http请求方法(POST,GET等)+ 路由地址 : controller的名字+.+action的名字

1
2
3
4
'POST /make/a/sandwith': 'SandwichController.makeIt'

// 当controller在子文件夹中定义时,子文件夹是controller的一部分
'/do/homework': 'stuff/things/HomeworkController.do'

显式路由常见定义方式

1
2
3
4
5
6
7
8
module.exports.routes = {
'get /signup': { view: 'conversion/signup' },
'post /signup': 'AuthController.processSignup',
'get /login': { view: 'portal/login' },
'post /login': 'AuthController.processLogin',
'/logout': 'AuthController.logout',
'get /me': 'UserController.profile'
}

自定义响应和responses文件夹

一般情况下,controller中的响应都会定义在responses文件夹中.
任何定义在/api/responses.js中方法都是通过res.[responsesName]这种形式在controller中调用的。
responses函数中,可以使用this.req, this.res来获取RequestResponse对象。

数据库适配器Adapters

可以连接不同的数据库,主要用来和数据库之间的通信。
adapter中,我们主要关注的是CRUD操作。
sails中,主要指这个几个方法create(), find(), update(), destroy()

可选的Adapter

  • 官方支持的数据库器
数据库名称 简介
sails-disk sails默认捆绑的,不需要做任何的配置。直接将数据写入磁盘,不适合用在生成环境,一般用在开发环境或者一些小的项目中
sails-memory 将数据写入内存中,不会正在的持久化,所以一般适合用在开发环境或者磁盘空间不足的时候
sails-mysql 链接mysql(关系型数据库)
sails-postgres 链接postgres(关系型数据库)
sails-mongo 链接mongo(非关系型数据库)
sails-redis 链接redis
  • 社区支持的数据库

全局变量

全局变量可以在sails加载完毕之后全局访问,这些全局变量都可以在config/globals.js中禁用。

  • models(文件名作为全局变量名)
  • services(文件名作为全局变量名)
  • sails
  • _(Lodash实例,默认提供,无需安装)
  • async(Async实例,默认提供,无需安装)

NOTE: 必须等到sails加载完成之后,才可以访问这些全局变量。也就是说,不能再导出的函数外部使用(此时sails尚未加载完毕),必须导出的函数内部使用

1
2
3
4
5
6
7
// 禁用所有
module.exports.globals = false

module.exports.globals = {
_: false, // 禁用具体的
async: true
}

日志(Logging)

sails内建一个日志输出,可以在/config/logs.js中配置日志输出等级。
详情参看sails内建日志

原则(Policies)

该文件夹中定义的脚本主要用来处理一些权限认证第三方单点登录等具有一些通用逻辑,有点类似于spring中的AOP
policies只应该在controlleractions中使用,不应该在views中使用。如果是在config/routes.js中直接定义的视图,那么就无法使用policies了。如果想要继续使用使用policies,因该使用controller+.+action的方式。
如下:

1
2
3
4
5
6
7
8
9
// routes.js

module.exports.routes = {
// 这种形式无法使用policies
'/': 'homepage',

// 可以这种方式
'POST /article/comment': 'ArticleController.comment'
}

Policies的定义

定义在api/policies目录下,每个policy应该对应一个文件,只处理一件事情,导出为单一的函数。
事实上,每个policies都是作为Connect/Express中间件的函数,在controller之前执行。

1
2
3
4
// 只处理一件事情,导出为单一函数
module.exports = function canWrite(req, res, next){

}

api/policiesconfig/policies.js的关系

config/policies.js中配置controllerpolicies之间的映射关系。就是说哪些controller使用哪些policies来做权限控制。

1
2
3
4
5
6
7
module.exports.policies = {
CommentController: { // controller名称
"*": 'isLoggedIn', // 对controller中所有的action生效
create: 'isLoggedIn', // key: action名称, value: policies
tag: ['isLoggedIn', 'isAdmin']
}
}
1
2
3
4
5
6
7
module.exports.policies = {
"*": 'isLoggedIn', // 映射到所有的controller中的actions
CommentController: {
// 如果某一个action显式的声明了policy, 那么全局的`isLoggedIn`不会映射到该action上
create: 'isAdmin'
}
}

Services

一个导出各种函数的纯对象,对象中的每个方法被称为helper
因为是全局的,所以可以在各个地方使用:controlleractions, 其他的services, 自定义的model方法中,甚至在命令行脚本中。

Service的创建

api/services文件夹中创建,文件名必须以Service.js结尾。

Service函数参数说明

1
2
3
4
5
6
7
8
9
10
11
12
13
var Mailgun = require('machinepack-mailgun');

module.exports = {
sendWelcomeEmail: function (options, done) {
Mailgun.sendHtmlEmail({
toEmail: options.emailAddress,
toName: options.firstName
}).exec(function (err) {
if (err) { return done(err); }
return done();
});
}
}

sendWelcomeEmail方法中option,done参数说明:

  • option: 一个自定义属性的对象。
  • done: 一个函数。这个一个可选的参数,当helper为一个异步方法时,那么当helper被调用时,必须传入done回调函数作为第二个参数。
  • 按照nodejs的约定,done中第一个参数为一个error对象,第二个参数为需要的数据结果。

Session

sails中,session主要由一下3部分要素组成:

  • session store: 保存信息
  • 管理session的中间件
  • 每次请求都会携带cookie,并且session id(默认的sails.sid)都会被存储到cookie

    session store既可以是内存(sails中默认使用内存存储),也可以是数据库。sailsConnect中间件的最顶层管理session,包括将session id保存到cookie中。

Views

views的使用,一般有2种方法:

  • res.view(), 由于可以在各个都可以使用res对象,所以可以用这种方式方式将view编译成功html返回给客户端
  • /config/routes.js中对路由进行配置

locals

在模板引擎中使用的变量称为locals, sails默认传入一些变量作为locals,比如lodash。如果某些数据是通过硬编码的方式填充到模板中,那么可以在config/routes.js中配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
module.exports.routes = {
'get /profile': {
view: 'backOffice/profile',
locals: { // 硬编码的数据,可以直接通过locals配置
user: {
name: 'Frank',
emailAddress: 'frank@enfurter.com'
},
corndogs: [
{ name: 'beef corndog' },
{ name: 'chicken corndog' },
{ name: 'soy corndog' }
]
}
}
}

在模板中使用动态数据

大多数时候,数据都是动态获取到的(通过action从model中获取),所以大多时候是按照如下方式进行渲染数据的:

1
2
3
4
5
6
7
8
9
10
// in api/controllers/UserController.js...

profile: function (req, res) {
// ...
return res.view('backOffice/profile', {
user: theUser,
corndogs: theUser.corndogCollection
});
},
// ...

Partials(模板包含)

支持模板包含,及在一些模板中,将另外一些模板包含进来,达到模板的复用。在sails中使用模板包含,需要注意的就是路径都是相对于view文件夹的。如

1
2
3
4
// views/partials/widget.ejs

// views/pages/dashboard/user-profile.ejs
<%- partial('../../partials/widget.ejs') %>

关于EJS模板

ejs中,模板的标签就以下3种:

  • <%= %>: 转义输出
  • <%- %>: 不转义输出
  • <% %>: 里面直接写js代码

models文件夹

全局访问

services文件夹

全局访问

responses文件夹

里面的方法都会挂载到controllerreq对象上