本篇介绍后台开发的常用技术与概念,以Node.js的Express框架为例,Express是Node.js上最流行的开发框架,十分轻量,提供了一套方便开发的工具方法。当前使用最多的是Express4.0版本。
Express太轻,叫他框架都有点不合适。个人认为框架的特点是我们编写的代码被框架调用,而Express只是提供了一套便捷的API与相关中间件。
学习资料:
- Express教程
- nodejs实践,包教不包会
- 官网:安全最佳实践和性能最佳实践两篇文章不错
知识图谱:nodejs进阶与Express知识图谱,尽量在其他技术上也可以通用。
基本使用
生成项目—express-generator
该工具会自动生成Express工程目录,安装mvc的结构新建一些目录
1 | npm install express-generator -g |
启动服务
1 | var express = require('express'); |
路由设置
本质也是中间件,安装路径分发请求
1 | app.get('/', function (req, res) { |
响应请求
这里的req和res和Nodejs原生的相同,只是在我们添加了一些中间件之后,req中会多一些变量,帮助我们处理请求。可以在不涉及 Express 的情况下调用 req.pipe()
、req.on('data', callback)
和要执行的其他任何函数,如res.end()
等。
Express添加了一些便捷方法
- 请求req中:
req.params
:路由中定义的参数req.query
:问号后面参数req.body
:req.cookies
- 返回res中:
res.status(200)
设置header中的状态码的便捷方法。res.send("xxx");
返回json,text结果,比较常用,自动设置Content-Type,内部包含end。参考res.render();
:渲染视图,返回html,内部包含end。res.sendfile()
:依据文件扩展名设置Content-Type。如直接返回网页。内部包含end。
一般中间件
中间件会被链式调用,处理请求
1 | // 定义中间件 |
错误处理
在其他 app.use()
和路由调用之后,最后定义错误处理中间件
1 | // 与其他中间件不同,它有四个参数,第一个是error |
模板引擎的使用
views
:模板文件所在目录。例如:app.set('views', './views')
view engine
:要使用的模板引擎。例如:app.set('view engine', 'jade')
1 | // 默认这个目录下面 |
jade是一种格式的模板引擎,注意是服务端渲染使用,它的语法与html不同,感觉不如ejs,还是保留原始的html比较好。
核心概念
中间件
这个概念在后台十分广泛,广义上的中间件是任何在系统中通用的组件,比如在分布式系统中的消息队列,缓存系统,配置管理系统,soa治理系统,都可以称之为中间件。这里的中间件也有通用的含义,如对http的结果的预处理(body解析,路由)。
nodejs中的中间件是使用的connect库,深入学习https://github.com/alsotang/node-lessons/tree/master/lesson18
路由
通俗的说就是对请求地址的分发,按照地址路由给某个方法来处理。因此需要一些正则表达式的知识。
多个函数处理一个路由
1 | app.get('/example/b', function (req, res, next) { |
链式处理的便捷方法 app.route(‘/book’)
1 | app.route('/book') |
Express的Router方法可以模块化的处理路由
1 | var express = require('express'); |
模板引擎与render
渲染一个常用的概念,主要用来对前端界面(html)的填值处理。渲染分为服务端渲染和客户端渲染。
- 服务端:有服务器处理模板文件,生成填值后的html文件,返回给客户端。优点是客户端一次请求就是结果,渲染工作在服务端,客户端性能较好。缺点也很明显,这些值只能一次更新的,如果要刷新,必须刷新整个页面。
- 客户端:客户端请求的就是含义模板信息的html文件,使用ajax请求接口返回动态信息(类似app)。然后调用模块引擎库(客户端js工具方法)填值。有点是可以不刷新页面更新值,缺点是必须发起多此请求,需要做好过渡效果处理。
- 两种结合:只有载入时更新的值用服务端渲染,动态更新的值用客户端渲染。
模板引擎是渲染填值的工具,如jade主要用于服务端,ejs两端通用,可以深入学习ejs的使用方法,和它的一些概念如layout模板等。
异步控制方法与回调地狱
- promise
- 事件订阅与发布—eventproxy
- 异步流程控制—async:要求用户传入待执行的函数列表,记为funlist。流程控制库的任务是让这些函数 顺序执行 。
实践
下面的总结有些乱,可以参考知识图谱梳理。
登录
授权登录oAuth2.0(第三方登录)
- 理解什么是:
- oAuth流程,为什么是两步?http://www.tianmaying.com/tutorial/oAuth-login
- 各种oauth授权方式汇总:http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html
- 理解oauth中的token:见jwt
jwt
- 理解token
- token本身只是某些信息的base64+rsa的验签(不防查看,防止修改)
- 为了防止token泄露,传输需要加密(https)
- 区别token和sessionid,token包含信息(可查看,不可修改,思考应该包括什么,不应该包括什么,回放攻击),session就是会话id,信息在内存中,因此占内存。使用范围也不同,一个无状态(只负责授权)一个有状态(授权和关联会话信息)。
思考
- 扩展:这是一个有意思的问题,是web与app跨界的地方,以前web(browser)是server为主的构架(可能是因为js不好用,browser只负责展示),所以用sessionId这种东西。现在App或者h5(RESTful),用更多主动权与业务逻辑,所有token这种客户端为主的技术广泛应用。xxx为主是谁维护状态,谁写业务逻辑。
- 防止xxx泄露:指的是防止传输过程泄露,客户端泄露无能为力。
- 防止token泄露,参考oauth2.0的第二次请求(https传输)
- 防止sessionId泄露,使用https
MemCache Redis
memcache、redis为什么是必须的
由于应用程序实例作为单独的进程运行,因此它们不会共享同一内存空间。也就是说,对象位于应用程序每个实例的本地。因此,无法在应用程序代码中保存状态。然而,可以使用 Redis 之类的内存中数据存储器来存储与会话相关的数据和状态。此警告适用于几乎所有形式的水平扩展(无论是多个进程的集群还是多个物理服务器的集群)
对于负载均衡功能,可能必须确保与特定会话标识关联的请求连接到产生请求的进程。这称为会话亲缘关系或者粘性会话,可通过以上的建议来做到这一点:将 Redis 之类的数据存储器用于会话数据(取决于您的应用程序)。
数据库
mongodb hbase mysql 常见的数据库和应用场景
mongodb:不支持表 join,事务,schema-less(应用场景:log)
mongodb 和 mysql 要我选的话,无关紧要的应用我会选择 mongodb,就当个简单的存 json 数据的数据库来用;如果是线上应用,肯定还是会选择 mysql。毕竟 sql 比较成熟,而且各种常用场景的最佳实践都有先例了。
- 数据库存储方式:行存储与列存储
- 数据库分片:sharding
多进程与进程管理
nodejs是单线程的,一个进程中只有一个主线程,因此无法发挥多核CPU的优势。解决方法是启动多个nodejs实例,即一个程序启动多个相同的进程。这就需要一个中介来分发请求和管理进程的生命周期。常用的管理方式有
- 最原始:使用nodejs的cluster模块写代码自己管理
- 第三方工具
- PM2
- StrongLoop Process Manager 供全面的运行时和部署解决方案的唯一工具?
多进程带来的问题,如何进程间通讯,共享内存变量(如session),这就引入了redis或者memcache这些分布式缓存。
一个典型的场景:登录请求第一次被分配到A实例,缓存了一些值(如sessionId和对应的值,如登录成功),第二次请求被分配到B实例,由于它没有缓存的值,sessionId找不到,又被判定为未登录
测试
- 开发测试—测试驱动开发
- 服务端js mocha,should,istanbul(测试覆盖率)
- 客户端js mocha,chai,phantomjs
- 性能测试 benchmark
- benchmark.js 库
部署
- 线上部署,一些pass云平台部署应用
- 持续集成travis—代码提交后自动运行测试和构建,并生成报告
cookie与session
HTTP 是一个无状态协议,所以客户端每次发出请求时,Server下一次请求无法得知上一次请求所包含的状态数据。两种方案解决这个问题。(重点:Server需要这些信息,来逻辑处理)
- cookies 在请求header中暂存这些信息,传给Client后,原封不动的回传给Server。(以前一直认为cookie是客户端用的)达到记录状态的目的
- 优势:方便,逻辑简单
- 问题:1.值被篡改。2.关键数据来回传送不安全。3.非流量,占网速。4.回放攻击问题。
- session cookie中只传输一个ID,类似于临时用户的概念,代表了与之相关的一组信息。
- 优势:相对安全,不用来回传关键数据,流量少,无回放攻击
- 问题:sessionID泄露,还是会泄露信息。
这两种技术都是在服务端解决http无状态的问题的方案,且主要用在浏览器中(有cookies实现),其思想可以通用,
另外一种是Token授权,主要是客户端的授权。这个技术往往用在由客户端维持状态,服务端仅仅提供无状态的服务的场景。(客户端凭token获取服务)
- sessionId最重要特征:不可修改性!,最担心的问题session泄露(劫持)
- signedCookies:对cookie进行加盐算hash,那么cookies可查看但是防止修改。
安全
- 常见的攻击:
- 攻击的基本思想
- 利用cookies:
- 非法获取:xss可以读取cookies,获取用户信息
- 模拟某个请求浏览器会自动带上cookies(csrf)
- 在外部(恶意)网站模拟被攻击网站
- 虽然浏览器禁止跨域访问,但是还是可以通过其他方法访问
<img/>
的src<scropt>
的src
- 虽然浏览器禁止跨域访问,但是还是可以通过其他方法访问
- 利用cookies:
- 预防
- 保护好cookies
- 使用http-only
- 防止非法代码注入xss
- 对输入校验
- 统一对输入encode,防止嵌入
</script>
- 防止csrf
- 对请求来源进行验证—下发表单时加入hide input包含csrf token,确保以后提交的表单是自己下发的,不是伪造的。
- 中间人攻击:https
- 保护好cookies