Nodejs Express 开发必备知识点

npm 常用指令

npm install  安装Node.js依赖包

例如npm install express 就会默认安装express的最新版本,也可以通过在后面加版本号的方式安装指定版本,如npm install [email protected]

npm install  -g  将包安装到全局环境中

但是代码中,直接通过require()的方式是没有办法调用全局安装的包的。全局的安装是供命令行使用的,就好像全局安装了vmarket后,就可以在命令行中直接运行vm命令

npm install  --save  安装的同时,将信息写入package.json中

项目路径中如果有package.json文件时,直接使用npm install方法就可以根据dependencies配置安装所有的依赖包,所以移交项目时不用吧node_modules文件一起了

这样代码提交到github时,就不用提交node_modules这个文件夹了。

npm init  会引导你创建一个package.json文件,包括名称、版本、作者这些信息等

npm remove 移除

npm update 更新

npm ls     列出当前安装的了所有包

npm root 查看当前包的安装路径

npm root -g  查看全局的包的安装路径

npm help  帮助,如果要单独查看install命令的帮助,可以使用的npm help install

使用npm init 指令会生成一个package.json,如下所示。然后执行 npm install --save express 后package.json文件中的dependencies字段发生变更

{
  "name": "easy_express",
  "version": "0.1.0",
  "description": "my express test",
  "main": "index.js",
  "scripts": {
    "test": "make test"
  },
  "keywords": [
    "node",
    "esay"
  ],
  "author": "feng",
  "license": "MIT",
  "dependencies": {.
    "express": "^4.13.3"
  }
}

如何设置静态资源

express.static是Express内置的唯一一个中间件,负责托管express应用内的静态资源。它可以将一个或多个目录指派为包含静态资源的目录,其中的资源不经任何特殊处理直接发送给客户端(如css文件、js文件等)。因为这个目录完全开放,所以我们一般设置这个静态资源文件夹为public

app.use(express.static(__dirname + '/public'));
接下来我们在public下面创建一个子目录Img,并把logo.png放到其中,现在我们可以直接指向/img/logo.png(注意路径中并没有public,这个目录对客户端来说是隐形的),static中间件会返回这个文件,并正确设定内容类型
或者干脆在浏览器里这么访问
http://localhost:3000/images/kitten.jpg

Express中请求对象request、response的属性和方法

req.params:

    ·一个数组,包含命名过的路由参数

    · app.get('/student/:name',function(req,res){ var name = req.params.name}); 路由中使用了:name,他会跟任何字符串匹配(不包括反斜杠),并将其跟键name一起放到req,params对象中    

前端页面拼接请求字符串:url = '/apple/detail/'+code+'/'+miniCode;
设置路由:              app.get('/apple/detail/:code/:miniCode', controller.product.p2pMiniDetail);
在对应的p2pMiniDetail中读取参数:    var code = req.params.code;  var miniCode = req.params.miniCode;

req.query

    `一个对象,包含以键值对存放的查询字符串(通常称为GET请求参数)

前端页面拼接请求字符串:url = '/apple/detail/'+'?uid='+uid+'&kind='+kind;
设置路由:              app.get('/apple/detail/', controller.product.p2pMiniDetail);
在对应的p2pMiniDetail中读取参数:    var uid = req.query.uid;  var kind = req.params.kind;

req.body

    `一个对象,包含POST请求参数(一般用于解析表单中的值,需要中间件能够解析请求内容类型)

req.cookies

    `一个对象,包含从客户端传递过来的cookies值

req.headers

    `从客户端接收到的请求报头

req.ip

    `客户端的IP地址

req.path/req.host/req.protocol/

    `请求路径(不包含协议、主机、端口或查询字符串)/返回客户端报告的主机名(这些信息可以伪造,所以不应该用于安全目的)/协议

req.xhr

    `一个简便属性,如果请求有Ajax发起将会返回true

req.url/req.originalUrl

    `返回路径和查询字符串(不包含协议、主机、端口)

res.status(code)

    `设置HTTP状态代码,如 function(req,res){ res.status(404.render('not-found'))}

res.cookie(name,value,[options])/res.clearCookie(name,[options])

    `设置或者清楚客户端cookies值,需要中间件支持

res.redirect([status],url)

    `重定向浏览器,默认重定向代码是302。通常你经过该尽量减少重定向,除非永久移动一个页面,这时候状态码301

res.send(body)/res.send(status,body)  

    ·向客户端发送响应及可选状态码,如果body是一个对象或者一个数组,响应将会以JSON发送(这种情况为什么不使用res.json呢?)

res.json(status)/res.json(status,json)

    ·向客户端发送JSON以及可选的状态码

res.sendFile(path,[option],[callback])

    ·这个方法根据路径读取指定文件并将内容发送到客户端

res.locals,render(view,[locals],callback)

    ·res.locals是一个对象,包含用于渲染视图的默认上下文。

    · res.render使用配置的模板引擎渲染视图,默认响应码为200

app.get('/',function(req,res){
  res.render('index',{
    message:'welcome',
    style:req.query.style,
    userid:req.cookie.userid
  })
})
  在静态页面中(以ejs模板引擎为例)使用<%= message%>输出welcome。render中的第二个参数就是所谓的context(上下文环境),当你渲染一个模板(静态页面如html格式),便会传递给模板引擎一个对象,叫做上下文对象,它能让替换标志运行。刚才我们就是用上下文对象中的message对象的值替换掉<%= message%>中的占位符,从而实现了前台页面上的动态数据的渲染

Exports&&module.exports

   Nodejs使用exports和module.exports来完成功能的组织及重用。Nodejs模块允许你从被引入文件中选择要暴漏给程序的函数和变量。如果模块返回的函数或变量不止一个,那他可以通过设定exports对象的属性来指明他们;但如果模块只返回一个函数或变量,则可以设定module.exports属性。这样做可以选择把什么跟程序共享而不污染全局命名空间。

    在moduleName.js文件内使用exports.something = function(){}类似的代码将函数导出,然后在目标文件内引入模块的该函数:var  muModule = require('moduleName.js')     muModule.somthing();//可以用exports导出多个函数

    但是如果只是想用exports导出一个变量或函数,你可能会以为只要把exports设定成你想要的东西就行。但是这样是不行的,因为Node觉得不能用任何其他对象、函数或变量给exprts赋值。例如不能使用exports = something;应该用module.exports替换掉exports。用module.exports可以对外提供单个变量、函数或对象。如果创建了一个既有exports又有module.exports的模块,那么他会返回module.exports而exports则会被忽略

    记住三点:

    1.exxports是指向module.exports的引用,module.exports初始值为一个空对象,所以exports的值也为{},所以exports.myFun只是module.exports.myFun的简写

    2.当我们想让模块道出的是一个对象时,exports和module.exports都可以用;而当我们导出非对象接口时,就必须也只能覆盖module.exports

    3.如果把exports设置为别的,就打破了module.exports和exports之间的引用关系。那样exports就不能用了,因为他不在指向module.exports。如果想维持链接,需要让module.exports再指向exports:module.exports = exports

    创建模块系统的注意事项

    第一:如果模块是目录,在模块目录中定义的模块的文件必须命名为index.js,除非你在这个目录下一个叫package.json的文件里特别说明(其中一个名为main的键)

    第二:Node能把模块作为对象缓存起来-》“猴子补丁”


表单处理

 

  1.大体上来讲,向服务器端发送客户端数据分为两种方式:查询字符串(GET请求)和请求正文(POST请求),有一种普遍的误解是POST请求是安全的,而GET请求是不安全的。事实上,如果使用HTTPS协议两者都是安全的;如果不使用,则都不安全,因为这是入侵者会想查看GET请求的查询字符串一样轻松查看POST请求的报文数据。
  2.从服务器的角度来看,最重要的属性是域中的name属性,这样服务期才能识别字段。name属性与id属性时截然不同的,后者只适用于样式和前端功能(他并不会发送到服务器端),理解这一点非常重要。
  3.隐藏域:他不会出现在浏览器中,但是你不能使用他存放秘密和敏感信息,一旦用户审查源文件就会暴漏隐藏域。
  4.使用post提交表单,需要引入中间件来解析URL编码体。
     安装:npm install --save body-parser
     引入:app.use(require('body-parser')());
  一旦引入了body-parser,req.body变为可用,这样所有的表单字段都可用
  app.post('/process',function(req,res){
    console.log('color from filed : '+ req.body.color);
    res.redirect('303','/thank-you')
  })

  5.上传文件:使用Formidable模块(Express4.0开始不支持Connect的内置中间件multipart)

     安装:npm install --save formidable

     引用:var formidable = require('formidable')

     路由:

     app.post('/contest/photos',function(req,res){
       var form = new formidable.IncomingForm();
       form.parse(req,function(err,fields,files){
         if(err) return res.redirect(303,'/error');
         console.log('recieved fields: ');
         console.log(fields);
         console.log('received files');
         console.log(files);
         res.redirect(303,'/thank-you')
       });
     })

      HTML

Express中的cookie

  安装模块:npm install --save cookie-parser
  引入中间件:var cookieParser = require('cookie-parser'); app.use(cookieParser('some strings here'));
  在任何能够访问到响应对象的地方设置cookie:res.cookie('monster','feng');
  获得客户端传过来的cookie值:var monster = req.cookie.monster;
  删除cookie:res.clearCookie('monster');
  可以使用如下方法给cookie定值属性:
   cookieOptions:{
      maxAge: 86400 * 30,
      domain:'.google.com'
      path:''
    }
  function setCookie(res, key, value){
    res.cookie(key, value, cookieOptions);
  }
  setCookie(res, 'userName', user.userName);
  注:设置cookie的时候可以使用如下这些选项
  domain:控制跟cookie关联的域名,这样你可以将cookie分配给特定的子域名。但是你不能给cookie设置根服务器所用域名不同的域名,因为那样做它什么也不会做。
  maxAge: 指定客户端应该保存cookie多长时间,毫秒单位。如果忽略这一项浏览器会在关闭时自动将cookie删掉
  secure:指定cookie只能通过https连接发送
  httpOnly:将这个选项设置为true表明这个cookie只能由服务器端修改,也就是说客户端Javascript不能修改它,有助于防范XSS攻击。

中间件

    从功能上讲,中间件是一种功能的封装方式,具体来说就是封装在程序中处理HTTP请求功能。它是在管道中执行的,可以想象一个送水的真实管道。水从一端泵入,然后到达目的地之前还会经过仪表和阀门。这个比喻中很重要的一部分是顺序问题,你把压力表放到阀门之前和之后会有不同的效果。同样,如果你有个向水中注入什么东西的阀门,那么这个阀门“下游”的所有东西都会含有这个新添加的原料。在Express程序中,通过调用app.use向管道中插入中间件。
    在Express 4.0中,中间件和路由器是按照他们的连入顺序调用的,顺序更清晰。在管道的最后放一个“捕获一切”请求的处理器是常见的做法,用他来处理跟前面其他所有的路又都不匹配的请求,这个请求一般会返回状态码404。同时在这个中间件之后,还应该存在一个用来捕获所有uncaughtException的中间件,用来处理所有为捕获错误(包括上一个中间件发出的404error、静态页面数据错误、undefined引用等,注意when.js中的then{}不会被捕捉)
//拦截所有未匹配路由的请求,如果请求流到了这里,则创建一个404状态的error,next(err)给下一个中间件
app.use(function(req, res, next) {
    var err = new Error('Not Found');
    err.status = 404;
    next(err);
});
//这个中间件可以处理err请求,捕捉然后按照不同的环境处理
if (app.get('env') === 'dev' || app.get('env') === 'staging') {
    app.use(function(err, req, res, next) {
        res.render('error_dev', {
            message: err.message,
            error: err
        });
    });
}else {
    app.use(function(err, req, res, next) {
        res.render('error');
    });
}
    那么请求在管道中如何终止呢?这是又传给每个中间件的next函数来实现的如果不调用next(),请求就在那个中间件中终止了。这个时候你应该发送一个响应到客户端(res.send、res.json、res.render),如果你不这样做,客户端会被挂起并最终导致超时;切切相反,如果调用了next(),一般不宜在发送响应到客户端了。如果你发送了,管道中后续的中间件火炉有处理器还会执行,但是他们发送的任何响应都会被被忽略。
    模块可以输出一个以中间件为属性的对象,这个中间件可以放到正常的路由调用中,作为其中的几个阀门,如果不满足中间件要求,这个路由调用就会不通过
在controller/user.js中导出一个判断检查登录状态的中间件
module.exports = {
  /**
   * 登陆状态检查
   */
  authorize: function (req, res, next) {
    if (!req.cookies.userId) {
        res.redirect('/user/login');
    } else {
      next();
    }
  }
}
当用户点击个人资产按钮时,进行身份验证
app.get('/assets', controller.user.authorize, controller.assets.index);
用户请求/assets路径,首先调用user.authorize验证是否登陆,已登录则next()反之则重定向到/user/login路由
常用中间件:
1)body-parser:略;
2)cookie-parser:略;
3)cookie-session:放到cookie-parser后面一起使用,提供cookie存储的会话支持
4)static:   app.use(express.static(path.join(__dirname, 'public')));提供对静态文件的支持,可以连入多次,并可指定不同的目录。

开发与部署

     ·Nodejs支持执行环境的概念,这里笔者建议你使用标准的开发、生产和测试环境。
    ·使用环境变量NODE_ENV来设置,可以在两个地方,一个是在系统的环境变量中,如果是在windows系统下,则在系统环境变量中添加一组选项NODE_ENV 值设置为dev/production/staging中的一种,这样做每次开启命令行执行程序时都会自动运行该环境变量的程序;第二种是在运行代码的同时设置环境变量,set NODE_ENV=dev/staging/production  回车 node bin/www,这样做每次都需要设置环境你变量。
    ·你可以使用app.get('env')来获取当前的环境变量
    用应用集群扩展:Nodejs本身支持因攻击群,踏实一种简单的、单服务器形式的向外扩展。使用应用集群,你可以为系统的每个CPU创建一个独立的服务器,我们在bin目录下创建一个www文件,作为启动文件
var app = require('../app');
var cluster = require('cluster');
var numCPUs = require('os').cpus().length;


if (cluster.isMaster) {
  // Fork workers.
  for (var i = 0; i < numCPUs; i++) {
    cluster.fork();
  }
  cluster.on('exit', function(worker, code, signal) {
    console.log('worker ' + worker.process.pid + ' died');
  });
} else {
  // Workers can share any TCP connection,in this case its a HTTP server
  app.set('port', process.env.NODE_PORT || 9099 );
	var server = app.listen(app.get('port'), function() {
		console.log('Express server listening on port ' + server.address().port);
	});
}
    这样你只需执行node bin/www,nodejs就会为你当前系统的每一个内核开启一个线程,充分利用当前服务器的硬件资源,

送上 express 4.x中文API地址,供大家参考:http://www.expressjs.com.cn/4x/api.html#res




你可能感兴趣的:(Express,移动端开发笔记)