前言
node已经使用过大概1年多的时间,之前一直使用Express来做应用开发,选择Express框架的原因是该框架时当前最流行的的框架,拥有的社区无疑是已经存在框架中最大的。但该框架也有自己的缺点,如:需要手动处理很多单调乏味的工作
- 错误处理
- 配置任务不统一(没有内置的配置途径,session、数据库、环境、端口等一些常用配置)
- 无法适应多环境配置(不同环境的配置可能会不一样)
- 对路由的过滤需要自己写中间件来实现(如:登陆验证,并不是所有路由都需要经过登录验证。首页和登录都不应该做登录验证)
当然,我们无法去否定Express,但是我们可以尝试使用其他的框架,也许你可能习惯了这个框架,但是如果你不去尝试其他的框架,永远不会成为一个好的开发者。之后,我接触到了sails,sails其实是内置的Express,所以,如果你之前使用的是Express,那么你将会很快的入门该框架,因为sails基本的语法同Express相同,只不过sails集成了很多其他的工具,使得我们开发更简单。
介绍篇
sails如同ruby on rails一样,也是一种为了使web开发变得简单的框架。就我这段时间的了解,其完全继承Express&Socket.io的一些API,并且使用了waterline(ORM)进行统一地数据库交互,使得可以在不同的数据库环境中无需直接修改代码即可完成CRUD操作;使用policies中间件来进行安全验证交互 自身的blueprints在前后台中无需码一行即可通过RESTfull API进行数据交互。
优点:
- 解决Express中一些基本问题
- 配置高于开发
- 一般自定义模块无需require来引用,sails提供了一套全局变量的引用
- 基本的RESTfull 无需手动配置
- 加入了grunt任务,已基本满足基本需求
- 其他还没用过的功能
目录结构
- myApp
- api
- controllers(控制器相关,业务逻辑)
- models(模块开发)
- policies(用于路由过滤)
- responses(定制所需的响应:如404 res.notFound())
- services(定制一些常用的工具类–全局的)
- assets(静态资源文件)
- images
- js
- styles
- templates
- favicon.ico
- config(整个项目的配置文件系统)
- env(配置不同环境的变量)
- *.js
- tasks
- views
- api
安装
1、npm -g install sails
2、sails new testProject
3、cd testProject
运行:sails lift/node app.js
访问:localhost:1337,成功访问
controllers篇
为什么要从controllers开始说,主要是只要有controllers那么项目就能正常运行,这儿我们在api/controllers下面新建一个文件,UserController.js(命名规则:model名称如User+Controller,首字母大写),代码如下:
module.exports={
hello:function(req,res){
res.send('hello man!!!');
}
}
此时访问localhost:1337/user/hello,此时你会发现访问成功并返回数据hello man!!!;
原理
在UserController下面我们定义了一个hello 的动作(action),那么sails会自动生成action routers(可以禁用该路由访问方式,详见下面的config篇),生成规则:domain/:user(不需要加controller)/:actionName
注意:默认的action routers 只能是get请求方式,如果需要其他请求方式需要在配置文件中配置
这个时候可能有人会问,如何渲染页面,sails提供了自己渲染方式,新建sails项目默认使用ejs作为模板。当然sails支持常用的一些模板,如:ejs、jade、hbs、swig等(详见sails官网)。渲染方式:
module.exports={
hello:function(req,res){
res.view({data:{name:'test123'}}) //该方式会默认去找views/user/hello.ejs文件
//res.view('hello',{data:{name:'test123'}}) //指定页面views/hello.ejs
}
}
model篇
model,即数据模型,定义数据结构,一般定义好后需要定义相应的controller,定义如下:
module.exports={
attributes:{
username:{
type:'string',
required:true
},
password:{
type:'string',
required:true
}
}
}
attributes
定义模型的属性:属性类型
string、text、integer、float、date、datetime、boolean、binary、array、json、email
collection:关联其他model数组
model:关联其他model
如下定义特殊的属性
attributes:{
uuid:{
type:'string',
size: 24 //定义属性的大小,必须保证适配器支持,如mysql
primaryKey: true, //设置主键,每个模型只有一个主键,尽量在autoPK设置为false才使用
required:true
},
state:{
type:'string',
enum: ['pending', 'approved', 'denied'] //保证该属性的值必须为该数组中的值
required:true
},
username:{
type:'string',
required:true,
columnName: 'full_name' //当我们需要兼容其他表的结构,而又需要自己设计合理的字段的时候
//可以使用该属性指定表中的字段名,而在sails中还是使用username字段
},
password:{
type:'string',
defaultsTo: '123456' //没有初始化的默认值
minLength: 6, //设置值最小长度
required:true //是不是必须的
},
count:{
type:'integer',
autoIncrement: true //当我们没有指定值时,属性自动递增,一般用于integer的类型
unique:true //是否唯一,如果设置为true保证数据库中的数据该属性具有唯一性
required:true
},
knownDialects:{
collection:'Dialect'
},
spouse: { model: 'Person' }
}
生命周期回调
1、创建时的回调
beforeValidate: fn(values, cb)
afterValidate: fn(values, cb)
beforeCreate: fn(values, cb)
afterCreate: fn(newlyInsertedRecord, cb)
2、修改的时候回调
beforeValidate: fn(valuesToUpdate, cb)
afterValidate: fn(valuesToUpdate, cb)
beforeUpdate: fn(valuesToUpdate, cb)
afterUpdate: fn(updatedRecord, cb)
3、销毁的时候回调
beforeDestroy: fn(criteria, cb)
afterDestroy: fn(destroyedRecords, cb)
如在创建用户的时候对密码机密操作:
var bcrypt = require('bcrypt');
module.exports = {
attributes: {
username: {
type: 'string',
required: true
},
password: {
type: 'string',
minLength: 6,
required: true,
columnName: 'encrypted_password'
}
},
beforeCreate: function (values, cb) {
bcrypt.hash(values.password, 10, function(err, hash) {
if(err) return cb(err);
values.password = hash;
cb();
});
}
};
方法
1、模型已经自带有某些方法:.create(), .update(), .destroy(), .find(), etc.
create():创建一个记录
User.create({username:'jy',password:'123456'}).exec(function (err,created){
console.log(created); //返回的是创建的对象
})
findOne({username:'jy'}): //返回第一个对象
find({username:'jy'}): //返回一个数组
count({username:'jy'}): //返回结果为一个该结果集的条数
destroy({name:'Flynn'}): //销毁找到的结果
findOrCreate({username:'jy'},{username:'jyjy'})://查询是否有第一个参数的记录,没有就创建第二个参数的记录
update({username:'jy'},{username:'jyjy'}): //修改记录
User.update({username:'jy'},{username:'Flynn'}).exec(function (err,updated){
if (err) {
// handle error here- e.g. `res.serverError(err);`
console.log(err)
}else{
console.log(updated)
}
});
以上方法都是使用的exec来执行生产结果(返回query对象)||也可以直接传回调参数
query: 用于直接调用底层的数据库驱动程序,如:直接使用原生的sql语句,适用于mysql或者相关的数据库
User.query('select * from user',function(err,results){
console.log(results);
})
native:只适用于使用mongodb的情况,返回mongodb的对象直接操作数据库
stream: 一般用于socket的操作
var getSocket = req.socket;
User.stream({name:'Walter'}).pipe(getSocket.emit);
2、我们也可以自定义方法:定义好后我们可以在任何动作中通过moduleName.methodName()调用
module.exports={
attributes:{
username:{
type:'string',
required:true
},
password:{
type:'string',
required:true
}
},
findOnePerson:function(str){
console.log('findOnePerson---'+str)
}
}
3、sails动态生成的方法
如:User模型中有一个属性为username,User会为你生成一个findByUsername('someone').exec(function(err,user){})