Node 框架之sails
发布于 9 年前 作者 a4712020502 24052 次浏览 最后一次编辑是 8 年前 来自 分享

前言

	node已经使用过大概1年多的时间,之前一直使用Express来做应用开发,选择Express框架的原因是该框架时当前最流行的的框架,拥有的社区无疑是已经存在框架中最大的。但该框架也有自己的缺点,如:需要手动处理很多单调乏味的工作
  • 错误处理
  • 配置任务不统一(没有内置的配置途径,session、数据库、环境、端口等一些常用配置)
  • 无法适应多环境配置(不同环境的配置可能会不一样)
  • 对路由的过滤需要自己写中间件来实现(如:登陆验证,并不是所有路由都需要经过登录验证。首页和登录都不应该做登录验证)

	当然,我们无法去否定Express,但是我们可以尝试使用其他的框架,也许你可能习惯了这个框架,但是如果你不去尝试其他的框架,永远不会成为一个好的开发者。之后,我接触到了sails,sails其实是内置的Express,所以,如果你之前使用的是Express,那么你将会很快的入门该框架,因为sails基本的语法同Express相同,只不过sails集成了很多其他的工具,使得我们开发更简单。

Sails官网:http://sailsjs.org

介绍篇

	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

安装

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){})
回到顶部