本文章主要是讲述了 node.js 中 web 开发的基础知识,这里主要讲述如何使用 express 框架的基础使用
1 简单的 http 服务
- 引入 required 模块
使用 require 指令来载入 Node.js 模块。
var http = require('http'); |
- 创建服务器:服务器可以监听客户端的请求,
类似于 Apache 、Nginx 等 HTTP 服务器。具体的细节参加 API
// 使用 http.createServer() 方法创建一个 Web 服务器, 返回一个 Server 实例 |
- 接收请求与响应请求
服务器很容易创建,客户端可以使用浏览器或终端发送 HTTP 请求,服务器接收请求后返回响应数据。
// 注册 request 请求事件 |
- 绑定端口号,启动服务器
server.listen(8888, function () { |
注意 : 当且仅当在第一次调用 server.listen() 或调用 server.close() 期间出现错误时,才能再次调用 server.listen() 方法。 否则,将抛出 ERR_SERVER_ALREADY_LISTEN 错误。参见
此时,若用户访问http://127.0.0.1:8888/则浏览器会一直在响应中,因为服务器没有返回信息。显示
该网页无法正常运作 127.0.0.1 未发送任何数据。
ERR_EMPTY_RESPONSE
因此,可以将步骤三中的代码更改为下面的示例,增加响应。
server.on('request', function (request, response) { |
向请求发送响应头。 状态码是一个 3 位的 HTTP 状态码,如 404。 最后一个参数 headers 是响应头。 可以可选地将用户可读的 statusMessage 作为第二个参数。
const body = 'hello world'; |
此方法只能在消息上调用一次,并且必须在调用 response.end() 之前调用。
如果在调用此方法之前调用了 response.write() 或 response.end(),则将计算隐式或可变的响应头并调用此函数。
当使用 response.setHeader() 设置响应头时,则与传给 response.writeHead() 的任何响应头合并,且 response.writeHead() 的优先。
如果调用此方法并且尚未调用 response.setHeader(),则直接将提供的响应头值写入网络通道而不在内部进行缓存,响应头上的 response.getHeader() 将不会产生预期的结果。 如果需要渐进的响应头填充以及将来可能的检索和修改,则改用 response.setHeader()。
// 返回 content-type = text/plain |
注意,Content-Length 以字节而非字符为单位。 上面的示例可行是因为字符串 ‘hello world’ 仅包含单字节字符。 如果主体包含更高编码的字符,则应使用 Buffer.byteLength() 来判断给定编码中的字节数。 Node.js 不检查 Content-Length 和已传输的主体的长度是否相等。
尝试设置包含无效字符的响应头字段名称或值将导致抛出 TypeError。
另一个完整的 demo 示例如下:
var http = require('http'); |
上述 demo 使用的是
http.createServer([options][, requestlistener]) |
这个接口,其中的 requestListener 是一个自动添加到 ‘request’ 事件的函数。
在上述响应中,如果有中文会出现乱码,可以增加响应头有进行控制,完整的 content-type 列表参见
response.setHeader("Content-Type", "text/html;charset=utf-8"); |
2 express 框架
2.1 安装 Express
cnpm install express --save |
以上命令会将 Express 框架安装在当前目录的 node_modules 目录中, node_modules 目录下会自动创建 express 目录。以下几个重要的模块是需要与 express 框架一起安装的
cnpm install body-parser --save |
这些附带安装的插件的作用如下:
body-parser - node.js 中间件,用于处理 JSON, Raw, Text 和 URL 编码的数据。
cookie-parser - 这就是一个解析 Cookie 的工具。通过 req.cookies 可以取到传过来的 cookie,并把它们转成对象。
multer - node.js 中间件,用于处理 enctype=“multipart/form-data”(设置表单的 MIME 编码)的表单数据。
安装完后,我们可以查看下 express 使用的版本号
$ cnpm list express |
2.2 快速入门
//express_demo.js 文件 |
上述实例中我们引入了 express 模块,并在客户端发起请求后,响应 “Hello World” 字符串。
$ node express_demo.js |
2.3 使用静态资源
项目的结构如上图所示,此时 public 为静态资源目录,无法直接访问,若需要将其当做静态资源访问,需要加上如下代码:
//express_demo.js 文件 |
注意: 若使用路径出现异常时,访问路径里的资源会无法访问
出现问题的代码:
app.use(express.static("/public/")); |
此时会出现如下问题:
完成正确的设置后,可以通过 http://localhost:8081/js/demo.js 访问静态资源问题。
express 官方文档参见 http://www.expressjs.com.cn/starter/static-files.html
注意
上面的配置方式中,放置资源的目录不在访问路径中。
另一种正确的配置方式如下:
app.use("/public/", express.static("./public/")); |
此时,访问静态资源需要带上路径 public,访问路径为:
// public 资源 |
2.4 使用 art-template
安装命令如下:
npm install art-template --sav |
安装完成之后,即可在 node.js 里使用 art-template 了,
在 node 里增加以下代码
// 配置使用 art-template 模板引擎 |
具体的 art-template 用法参见官方文档
注意:
默认的模版文件的目录为 views
如果想修改默认的模板文件夹路径,可以通过如下方法进行设置
app.set('views', __dirname + '/views'); |
上面两行是设置 views 文件夹,即模板文件夹,dirname 是 node.js 里面的全局变量,即取得执行的 js 所在的路径,另外dirname 是目前执行的 js 文件名。
所以,
app.set(‘views’, __dirname + ‘/views’); |
是设置 views 的文件夹。
而
app.set(‘view engine’, ‘jade’); |
是设置 express.js 所使用的 render engine。除了 Jade 之外 express.js 还支持 EJS(embedded javascript)、Haml、CoffeScript 和 jQuerytemplate 等 js 模板
2.5 express 处理 post 请求
- 先安装 body-parser ,安装命令如下:
cnpm install body-parser --save |
- 配置 body-parser
//引入 body-parser |
只要配置了这个属性,在 req 请求对象上就会增加一个属性 body
安装成功之后,就可以使用次插件了,使用的方法如下:
/** |
2.6 node.js 路由设置
假设项目的路由的路径作用如下:
请求方法 | 请求路径 | get 参数 | post 参数 | 备注 |
---|---|---|---|---|
GET | /studens | 渲染首页 | ||
GET | /students/new | 渲染添加学生页面 | ||
POST | /studens/new | name、age、gender、hobbies | 处理添加学生请求 | |
GET | /students/edit | id | 渲染编辑页面 | |
POST | /studens/edit | id、name、age、gender、hobbies | 处理编辑请求 | |
GET | /students/delete | id | 处理删除请求 | |
1.在项目中创建一个名为 router.js 的文件
文件的内容如下:
/** |
- 将 router.js 挂在到服务中
/** |
express 具体的路由使用方法参见 http://www.expressjs.com.cn/4x/api.html#router
3 nodemon 自动重启服务
- 全局安装 nodemon 包,这样新创建的 Node.js 应用都能使用 Nodemon 运行起来了
npm install -g nodemon |
- 安装完成之后,Nodemon 就可以启动 Express 应用了,先关闭当前正在执行的应用程序,然后再执行命令
nodemon index.js |
3.1 nodemon 配置文件
Nodemon 默认会监听当前目录下(也就是执行 nodemon 命令所在的目录)的所有文件,不过有些情况下,虽然项目文件发生了改动,但是不需要 Nodemon 重启应用,那如何让文件不被 Nodemon 监听呢?不需要监听的文件,可以通过设置 Nodemon 的配置文件排除掉,新建文件 server/nodemon.json,添加代码
{ |
Nodemon 配置文件是 JSON 文件,通过设置 ignore 属性值,一个由文件名组成的字符串数组,指定不需要监听的文件
3.2 nodemon 手动重启
有时候可能 Nodemon 还在运行的时候,需要手动重启它,在这种情况下不需要关闭正在运行的 Nodemon 进程然后再重启 Nodemon,只要在 Nodemon 命令运行的终端 窗口中输入 rs 两个字符,然后再按下回车键,就能重启 Nodemon 了
rs |
4 session 处理
在 express 框架中,默认不支持 session,可以使用 express-session 来支持 session
4.1 安装 express-session
npm i express-session |
4.2 配置 express-session
var session = require('express-session') |
express-session 一定要在路由之前配置
express-session 是针对 nodejs express 框架提供的一套 session 扩展,主要参数有 secret,sesave,saveUninitialized,cookie
cookie 主要属性如下
{ path: ‘/’, httpOnly: true, secure: false, maxAge: null }
domain:设置 cookie 可以设置的域名,如果没有设置则 cookie 默认在当前域可以使用
expires:cookie 失效时间,可以设置时间,不建议给固定时间,设置 maxAge 之后自动会生成这个值
//获取当前时间 |
httpOnly:属性禁止客户端 JavaScript 的访问,禁止后不能使用 document.cookie
maxAge:单位毫秒,从设置 cookie 开始多少毫秒失效 ,如果 maxAge 和 expires 都设置了,最后设置的属性生效.
path:路径,默认值为域名的根路径.
sameSite: SameSite-cookies 是一种机制,用于定义 cookie 如何跨域发送。这是谷歌开发的一种安全机制,未来的一种 cookie 跨域授权处理方式,不明白的就不用设置了
(Strict 是最严格的防护,有能力阻止所有 CSRF 攻击。然而,它的用户友好性太差,因为它可能会将所有 GET 请求进行 CSRF 防护处理。
例如:一个用户在 reddit.com 点击了一个链接(GET 请求),这个链接是到 facebook.com 的,而假如 facebook.com 使用了 Samesite-cookies 并且将值设置为了 Strict,那么用户将不能登陆 Facebook.com,因为在 Strict 情况下,浏览器不允许将 cookie 从 A 域发送到B域。
Lax(relax 的缩写?)属性只会在使用危险 HTTP 方法发送跨域 cookie 的时候进行阻止,例如 POST 方式。
例 1:一个用户在 reddit.com 点击了一个链接(GET 请求),这个链接是到 facebook.com 的,而假如 facebook.com 使用了 Samesite-cookies 并且将值设置为了 Lax,那么用户可以正常登录 facebok.com,因为浏览器允许将 cookie 从 A 域发送到 B 域。
例 2:一个用户在 reddit.com 提交了一个表单(POST 请求),这个表单是提交到 facebook.com 的,而假如 facebook.com 使用了 Samesite-cookies 并且将值设置为了 Lax,那么用户将不能正常登陆 Facebook.com,因为浏览器不允许使用 POST 方式将 cookie 从 A 域发送到B域。
)
值 true:sameSite 使用 strict 模式
值 false:不设置 sameSite 属性
值 lax:sameSite 使用 lax 模式
值 strict: sameSite 使用 strict 模式
secure:设置 cookie 的 secure 值,默认是不设置任何值
setSecure(true); 的情况下,只有 https 才传递到服务器端。http 是不会传递的。
genid:设置创建 session id 的自定义函数,默认使用 uid-safe 扩展来生成 id, 自定义 genid 创建函数时一定要保证创建的 id 不要重复。
name :用来设置在 response 中范围 session id 是属性值,reuqest 中可以用默认的 request.session.id 访问。默认值为 connect.sid
proxy:代理,通过设置这个值可以设置 X-Forwarded-Proto 头,
proxy 的值有 true (X-Forwarded-Proto 使用),false (所有头信息忽略,只有 tls/ssl 可以安全连接),undefined(使用 trust proxy 设置) 具体大家研究,因为没有整代码大家继续努力实践
resave:是否允许 session 重新设置,要保证 session 有操作的时候必须设置这个属性为 true
rolling:是否按照原设定的 maxAge 值重设 session 同步到 cookie 中,要保证 session 有操作的时候必须设置这个属性为 true
saveUninitialized:是否设置 session 在存储容器中可以给修改(true 表示无论是否使用 session 都默认分配一个钥匙 )
session 过期 30 分钟,没有人操作时候 session 30 分钟后过期,如果有人操作,每次以当前时间为起点,使用原设定的 maxAge 重设 session 过期时间到 30 分钟只有这种业务场景必须同行设置 resave rolling 为 true.同时 saveUninitialized 要设置为 false 允许修改。
secret:用来注册 session id 到 cookie 中,相当与一个密钥。
store:session 存储的实例子,一般可以用 redis 和 mangodb 来实现
unset:设置 req.session 在什么时候可以设置
值:destory:请求结束时候 session 摧毁,值:keep session 在存储中的值不变,在请求之间的修改将会忽略,而不保存
更多具体的使用参见https://www.npmjs.com/package/express-session
4.3 session 的使用
添加 session 数据
req.session.键=值 |
访问 session 中的数据
req.session.键 |