前后端都用js语言的好处:
- 无须切换语言环境
- 数据(JSON)可以很好地实现跨前后端直接使用
- 一些业务(如模板渲染)可以很自由地轻量地选择是在前端还是在后端进行
基础功能
以官方经典的Hello World为例:
1 | var http = require('http') |
在具体的业务中,我们可能有如下这些需求:
- 请求方法的判断
- URL的路径解析
- URL中查询字符串解析
- Cookie的解析
- Basic认证
- 表单数据的解析
- 任意格式文件的上传处理
请求方法
HTTP_Parser在解析请求报文的时候,将报文头抽取出来,设置为req.method
路径解析
HTTP_Parser解析为req.url
查询字符串
Cookie
请求报文Cookie的格式类似如下:
1 | Cookie: foo=bar; baz=val |
设置Cookie的报文如下所示:
1 | Set-Cookie: name=value; Path=/; Expires=Sun, 23-Arp-23 09:01:35 GMT; Domain=.domain.com |
- path
- Expires/Max-Age
- HttpOnly
- Secure
node设置cookie:
1 | res.setHeader('Set-Cookie', 'foo=bar; Path=/; Expires=Sun, 23-Arp-23 09:01:35 GMT; Domain=.domain.com') |
Session
- 基于Cookie实现用户和数据的映射
- 通过查询字符串来实现
session与安全
设置Cookie时,将sid通过私钥加密进行签名,把这个签名值作为新的sid:
1 | function sign (val, secret) { |
验证:
1 | function unsign (val, secret) { |
一种更好的方案是将客户端的某些独有信息与口令作为原值,然后前面,这样攻击者一旦不在原始的客户端上进行访问,就会导致签名失败。这些独有信息包括用户ip和用户代理(User Agent)
缓存
- If-Modified-Since/Last-Modified
当服务器返回Last-Modified
时,下次浏览器请求会自动带上If-Modified-Since
1 | var handle = function (req, res) { |
- If-None-Match/ETag
1 | var getHash = function (str) { |
- Cache-Control/Expires
略
Basic认证
在Basic认证中,他会将用户和密码部分组合:username + ":" + password
。然后进行Base64编码:
1 | var encode = function (username, password) { |
如果首次访问该页面,URL地址中也没有携带认证内容,服务器会返回401,浏览器会弹出一个登陆的窗口
1 | var checkUser = function (user, pass) { |
数据上传
表单数据
默认的表单提交,请求头中的Content-Type
字段为application/x-www-form-urlencoded
,由于它的报文体跟查询字符串相同:foo=bar&baz=val
,因此解析起来比较容易:
1 | var hasBody = function(req) { |
其他格式
JSON文件
1 | var mime = function (req) { |
XML文件
1 | var xml2js = require('xml2js') |
附件上传
1 | ------WebKitFormBoundaryuNvMBwzIYUQBKoHY |
1 | var handle = function (req, res) { |
数据上传与安全
上传大小限制
1 | var bytes = 1024 |
CSRF
关于CSRF请查看CSRF亲测
路由解析
文件路径型
略
MVC
路由映射
1.手工映射
1 | var http = require('http') |
正则匹配
1 | var use = function (path, action) { |
参数解析
1 | var pathRegexp = function (path, strict) { |
2.自然映射
1 | var http = require('http') |
RESTful
通过URL设计资源,通过请求方法定义资源的操作,通过Accept决定资源的表现形式
1 | var routes = {'all': []} |
中间件
页面渲染
内容响应
MIME
浏览器通过不同的Content-Type
的值来决定采用不同的渲染方式,这个值我们简称为MIME(Multipurpose Internet Mail Extensions)值。
附件下载
1 | Content-Disposition: attachment; filename="filename.ext" |
a标签:
1 | // 以w3logo为文件名下载 |
响应JSON
1 | res.json = function (json) { |
响应跳转
1 | res.redirect = function (url) { |
模板
- 模板语言
- 包含模板语言的模板文件
- 拥有动态数据的数据对象
- 模板引擎
模板引擎
1 | var render = function (str, data) { |
上面代码,相同的模板每次渲染都要重新编译:
1 | console.log(render(tpl, {username: 'ayou'})) |
我们通常会采用模板预编译的方式:
1 | var compile = function (str) { |
with的使用
上面的模板引擎非常弱,只能替换变量,<%= "jackson tian"%>
就无法支持了。使用with
可以方便的实现:
1 | var compile = function (str) { |
模板安全
为了提高安全性,大多数模板都提供了转义的功能:
1 | var escape = function (html) { |
输出:
1 | var tpl = ""; |
模板逻辑
譬如下面的代码:
1 | <% if (user) { %> |
编译完后应该是:
1 | function (obj, escape) { |
实现方法如下:
1 | var compile = function (str) { |
集成文件系统
1 | var cache = {} |
子模板
1 | var preCompile = function (str) { |
布局视图
略