116181545032456974
前言 Fast, unopinionated, minimalist web framework for Node.js(官网 )
安装
创建Web服务器
80:指定端口号
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 const express = require ("express" );const port = 80 ;const app = express ();app.get ("/" , function (req, resp ) { resp.send ("" ); }); app.listen (port, "0.0.0.0" , function ( ) { console .log (`http://127.0.0.1:${port} ` ); });
请求 获取请求参数 query request 1 GET http://127.0.0.1:80/?id=1
1 2 3 4 app.get ("/" , function (req, resp ) { const query = req.query ; console .log (query["id" ]); });
path request 1 GET http://127.0.0.1:80/1
1 2 3 4 app.get ("/:id" , function (req, resp ) { const path = req.params ; console .log (path["id" ]); });
body request 1 2 3 4 5 6 POST http://127.0.0.1:80/ Content-Type : application/json{ id: "1" }
1 2 3 4 app.post ("/" , function (req, resp ) { const body = req.body ; console .log (body.id ); });
响应 设置响应头 1 resp.setHeader ("Content-Type" , "application/json" );
创建静态资源服务器
指定一个目录,将这个目录作为静态资源目录,所有静态资源的请求可以直接被访问
如果先后指定了多个相同层级的相同静态资源,此时先托管的静态资源路径会生效,后托管的静态资源路径不会生效
./public:静态资源目录名
1 app.use (express.static ("./public" ));
指定访问路径前缀 1 app.use ("/public" , express.static ("./public" ));
路由
将请求类型和请求路径映射到指定处理函数,这种映射就是Express中的路由
路由有优先级,会从上到下依次匹配,先被匹配成功的路由将会先执行处理函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 const express = require ("express" );const port = 80 ;const app = express ();const router = express.Router ();router.get ("/" , function (req, resp ) { resp.send ("" ); }); app.use (router); app.listen (port, "0.0.0.0" , function ( ) { console .log (`http://127.0.0.1:${port} ` ); });
指定访问路径前缀 1 app.use ("/router" , router);
中间件(Middleware)
中间件指业务流程的中间处理环节
Express中的中间件本质上就是一个特殊的处理函数
中间件处理函数的形参必须包含next,同时函数体中结尾必须包含next()函数
中间件处理函数在做完处理后,会交给下一个中间件或路由进行进一步处理
除了错误级别的中间件,其他的中间件一定要在路由之前配置
中间件的分类
应用级别的中间件:绑定到app实例上的中间件
路由级别的中间件:绑定到express.Router()实例上的中间件
错误级别的中间件:错误级别的中间件是一种特殊的全局中间件,专门用来捕获整个项目中发生的异常错误,从而防止项目异常崩溃的问题
错误级别的中间件函数必须有4个形参,形参顺序从前到后分别是err、req、resp、next
全局生效中间件
当客户端发起任何请求,到达服务器时,都会触发全局生效中间件
通过app.use()可以设置一个中间件处理函数作为全局生效中间件
多个中间件之间共用一份req和resp对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 const express = require ("express" );const port = 80 ;const app = express ();const fn = function (req, resp, next ) { ... next (); } app.use (fn); app.get ("/" , function (req, resp ) { resp.send ("" ); }); app.listen (port, "0.0.0.0" , function ( ) { console .log (`http://127.0.0.1:${port} ` ); });
定义多个全局生效中间件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 const express = require ("express" );const port = 80 ;const app = express ();const fn = function (req, resp, next ) { ... next (); } app.use (fn); app.use (fn); app.get ("/" , function (req, resp ) { resp.send ("" ); }); app.listen (port, "0.0.0.0" , function ( ) { console .log (`http://127.0.0.1:${port} ` ); });
局部生效中间件
将中间件函数传递给指定路由,使中间件函数只在指定路由上生效
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 const express = require ("express" );const port = 80 ;const app = express ();const fn = function (req, resp, next ) { ... next (); } app.get ("/" , fn, function (req, resp ) { resp.send ("" ); }); app.listen (port, "0.0.0.0" , function ( ) { console .log (`http://127.0.0.1:${port} ` ); });
定义多个局部生效中间件
直接传递多个中间件函数传递给路由
也可以将多个中间件函数作为数组传递给路由
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 const express = require ("express" );const port = 80 ;const app = express ();const fn = function (req, resp, next ) { ... next (); } app.get ("/" , fn, fn, function (req, resp ) { resp.send ("" ); }); app.get ("/" , [fn, fn], function (req, resp ) { resp.send ("" ); }); app.listen (port, "0.0.0.0" , function ( ) { console .log (`http://127.0.0.1:${port} ` ); });
错误级别中间件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 const express = require ("express" );const port = 80 ;const app = express ();const fn = function (err, req, resp, next ) { console .log (err.message ); next (); } app.get ("/" , function (req, resp ) { resp.send ("" ); }); app.use (fn); app.listen (port, "0.0.0.0" , function ( ) { console .log (`http://127.0.0.1:${port} ` ); });
Express内置中间件 express.static
1 app.use (express.static ());
express.json
解析JSON格式的请求体数据
仅在4.16.0及以后的版本可用
1 app.use (express.json ());
express.urlencoded
解析url-encoded格式的请求体数据
仅在4.16.0及以后的版本可用
1 app.use (express.urlencoded ({ extended : false }));
第三方中间件 body-parser
express内置中间件就是根据body-parser进一步封装的
安装
引入 1 const parser = require ("body-parser" );
注册并使用 1 app.use (parser.urlencoded ({ extended : false }));
cors
安装
引入 1 const cors = require ("cors" );
注册并使用
express-session进行身份认证
通过express-session进行身份认证
在不跨域的情况下推荐使用session进行身份认证
安装 1 npm install express-session
引入 1 const session = require ("express-session" );
注册
secret:随意指定一个字符串作为Session的加密密钥
1 2 3 4 5 app.use (session ({ secret : "" , resave : false , saveUninitialized : true }));
通过req.session对象中存取数据
通常在登录成功后将用户登录状态和用户信息存储在session中
销毁req.session对象中存储的数据
通常在退出登录后,将当前请求的用户对应的session清除
jsonwebtoken和express-jwt
使用jsonwebtoken模块将JSON字符串生成JWT加密字符串Token
使用express-jwt模块将JWT加密字符串Token还原成JSON对象
安装 1 npm install jsonwebtoken express-jwt
引入 1 2 const jwt = require ("jsonwebtoken" );const expressJWT = require ("express-jwt" );
定义一个用于加解密的密钥
加密
<user>:需要存储的用户信息secretKey:指定密钥expiresIn: "":指定有效时间
s:秒m:分钟h:小时
1 2 3 4 5 app.post ("/login" , function (req, resp ) { resp.send ({ token : jwt.sign (<user>, secretKey, {expiresIn : "30s" }) }); });
解密 注册中间件
1 app.use (expressJWT ({secret : secretKey}));
通过unless()指定不需要鉴权的请求路径,可以使用通配符
1 app.use (expressJWT ({secret : secretKey}).unless ({path : [/^\/dist/ ]}));
获取解密后的对象
中间件在得到加密的Token后,会自动解密,并将解密后的对象挂载在req.user对象上
1 2 3 app.post ("/login" , function (req, resp ) { console .log (req.user ); });
异常处理
如果Token是无效或者过期的,会报错:UnauthorizedError,通过全局异常处理函数可以捕获异常
1 2 3 4 5 6 7 8 app.use (function (err, req, resp, next ) { if (err.name === "UnauthorizedError" ) { return resp.send ({ code : "401" , msg : "鉴权失败" }); } });
自定义中间件
示例:通过自定义中间件实现express.urlencoded中间件的功能
定义一个自定义中间件
监听data事件
在中间件中,需要坚挺req对象的data事件,来获取客户端发送到服务端的数据
如果数据量比较大,无法一次性发送完毕,则客户端会把数据切割后,分批发送到服务端,所以data事件可能会触发多次,每次触发data事件️,获取到的数据都只是完整数据的一部分,需要手动对接收到的数据进行拼接
监听end事件
使用Nodejs的内置模块querystring将请求体字符串转换成JS对象
如果请求体字符串中包含url编码的中文字符,也会自动解码
将解析后的数据挂载为req.body
next()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 const querystring = require ("querystring" );app.use (function (req, resp, next ) { let data = "" ; req.on ("data" , function (chunk ) { data += chunk; }); req.on ("end" , function ( ) { console .log (data); data = querystring.parse (data); req.body = data; next (); }); });
将自定义中间件封装为模块
模块名.js 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const querystring = require ("querystring" );const 模块名 = function (req, resp, next ) { let data = "" ; req.on ("data" , function (chunk ) { data += chunk; }); req.on ("end" , function ( ) { console .log (data); data = querystring.parse (data); req.body = data; next (); }); } module .exports = 模块名;
index.js 1 2 const 模块名 = require ("./模块名.js" );app.use (模块名);
完成 参考文献 哔哩哔哩——黑马程序员