前言 Express Fast, unopinionated, minimalist web framework for Node.js(官网 )
使用Express快速创建Web服务器或API接口服务器
安装
创建服务器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 const express = require ("express" );const app = express ();app.get ("/" , function (req, resp ) { resp.send ("ok" ); }); app.post ("/" , function (req, resp ) { resp.send ("ok" ); }); app.listen (80 , function ( ) { console .log ("express server run at http://127.0.0.1" ); });
处理GET请求参数
1 2 3 app.get ("/" , function (req, resp ) { console .log (req.query ); });
处理RESTful风格的GET请求参数
通过req.params
来接收RESTful风格的请求参数
1 2 3 app.get ("/:key1/:key2" , function (req, resp ) { console .log (req.params ); });
如果发送的请求是http://127.0.0.1/value1/value2
,通过req.params
得到的响应如下
1 2 3 4 { "key1": "value1", "key2": "value2" }
处理POST请求体数据
如果不配置解析请求体数据的中间件,默认返回undefined
设置响应头
设置CORS响应头 设置允许跨域 允许指定域的跨域请求 1 resp.setHeader ("Access-Control-Allow-Origin" , "http://example.com" );
允许任何域的跨域请求 1 resp.setHeader ("Access-Control-Allow-Origin" , "*" );
设置允许额外的请求头
默认情况下,为了设置CORS,客户端只能向服务端发送如下9个请求头:Accept
、Accept-Language
、Content-Language
、DPR
、Downlink
、Save-Data
、Viewport-Width
、Width
、Content-Type
如果需要设置其他请求头,需要在Access-Control-Allow-Headers
进行声明
1 resp.setHeader ("Access-Control-Allow-Headers" , "X-Custom-Header" );
设置允许的请求方式 允许客户端发送指定请求
默认情况下,客户端只能向服务端发送GET
、POST
、HEAD
请求
如果需要发送其他请求,需要使用Access-Control-Allow-Methods
来指明允许使用的请求方式
1 resp.setHeader ("Access-Control-Allow-Methods" , "GET, POST, PUT, DELETE" );
允许客户端发送任何请求 1 resp.setHeader ("Access-Control-Allow-Methods" , "*" );
创建静态资源服务器
指定一个目录,将这个目录作为静态资源目录,所有静态资源的请求可以直接被访问
如果先后指定了多个相同层级的相同静态资源,此时先托管的静态资源路径会生效,后托管的静态资源路径不会生效
./public
:静态资源目录名
1 app.use (express.static ("./public" ));
此时访问http://127.0.0.1/index.html
时就会从静态资源中获取了
指定访问路径前缀
/public
:访问路径前缀
1 app.use ("/public" , express.static ("./public" ));
此时访问http://127.0.0.1/public/index.html
时就会从静态资源中获取了
路由
将请求类型和请求路径映射到指定处理函数,这种映射就是Express中的路由
路由有优先级,会从上到下依次匹配,先被匹配成功的路由将会先执行处理函数
挂载到app对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const express = require ("express" );const app = express ();app.get ("/" , function (req, resp ) { resp.send ("ok" ); }); app.post ("/" , function (req, resp ) { resp.send ("ok" ); }); app.listen (80 , function ( ) { console .log ("express server run at http://127.0.0.1" ); });
模块化路由
为了方便模块化管理,不建议直接将路由挂载到app对象,而是将路由抽离为单独的模块
创建路由模块对应的JS文件
调用express.Router()
函数创建路由对象
由路由对象挂载具体路由
使用module.exports
向外共享路由对象
使用app.use()
注册路由模块
router.js
1 2 3 4 5 6 7 8 9 10 11 const express = require ("express" );const router = express.Router ();router.get ("/" , function ( ) { resp.send ("ok" ); }); router.post ("/" , function ( ) { resp.send ("ok" ); }); module .exports = router;
index.js
1 2 3 4 5 6 7 8 9 10 11 const express = require ("express" );const app = express ();const router = require ("./router" );app.use (router); app.listen (80 , function ( ) { console .log ("express server run at http://127.0.0.1" ); });
指定访问路径前缀
/router
:访问路径前缀
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 const express = require ("express" );const app = express ();const func = function (req, resp, next ) { ... next (); } app.use (func); app.get ("/" , function (req, resp ) { resp.send ("ok" ); }); app.listen (80 , function ( ) { console .log ("express server run at http://127.0.0.1" ); });
简化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 const express = require ("express" );const app = express ();app.use (function (req, resp, next ) { ... next (); }); app.get ("/" , function (req, resp ) { resp.send ("ok" ); }); app.listen (80 , function ( ) { console .log ("express server run at http://127.0.0.1" ); });
定义多个全局生效中间件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 const express = require ("express" );const app = express ();const func = function (req, resp, next ) { ... next (); } app.use (func); app.use (func); app.get ("/" , function (req, resp ) { resp.send ("ok" ); }); app.listen (80 , function ( ) { console .log ("express server run at http://127.0.0.1" ); });
局部生效中间件
将中间件函数传递给指定路由,使中间件函数只在指定路由上生效
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 const express = require ("express" );const app = express ();const func = function (req, resp, next ) { ... next (); } app.get ("/" , func, function (req, resp ) { resp.send ("ok" ); }); app.listen (80 , function ( ) { console .log ("express server run at http://127.0.0.1" ); });
定义多个局部生效中间件
直接传递多个中间件函数传递给路由
也可以将多个中间件函数作为数组传递给路由
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 const express = require ("express" );const app = express ();const func = function (req, resp, next ) { ... next (); } app.get ("/" , func, func, function (req, resp ) { resp.send ("ok" ); }); app.get ("/" , [func, func], function (req, resp ) { resp.send ("ok" ); }); app.listen (80 , function ( ) { console .log ("express server run at http://127.0.0.1" ); });
错误级别中间件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 const express = require ("express" );const app = express ();const func = function (err, req, resp, next ) { console .log (err.message ); next (); } app.get ("/" , function (req, resp ) { resp.send ("ok" ); }); app.use (func); app.listen (80 , function ( ) { console .log ("express server run at http://127.0.0.1" ); });
Express内置中间件 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 (模块名);
通过Express编写后端接口
router.js
1 2 3 4 5 6 7 8 9 10 11 12 const express = require ("express" );const router = express.Router ();router.get ("/子路径" , function ( ) { resp.send ({ "code" : 200 , "msg" : "ok" , "data" : req.query }); }); module .exports = router;
index.js
1 2 3 4 5 6 7 8 9 const express = require ("express" );const app = express ();const router = require ("./router.js" );app.use ("/父路径" , router); app.listen (80 , function ( ) { console .log ("express server run at http://127.0.0.1" ); });
请求的分类 简单请求
请求方式为GET
、POST
、HEAD
之一的请求
请求头内只包含Accept
、Accept-Language
、Content-Language
、DPR
、Downlink
、Save-Data
、Viewport-Width
、Width
、Content-Type
之一的信息
预检请求
请求方式为GET
、POST
、HEAD
之外的请求
请求头内包含除了Accept
、Accept-Language
、Content-Language
、DPR
、Downlink
、Save-Data
、Viewport-Width
、Width
、Content-Type
之外的信息
客户端向服务断发送了application/json
格式的数据
预检请求与简单请求的区别:在浏览器与服务器正式通信之前,浏览器会先发送OPTION
请求进行预检,以获知服务端是否允许该实际请求,所以这一次OPTION
请求被成为预检请求,服务端成功响应预检请求后,客户端才会发送真正的请求,并且携带真实数据
JSONP
浏览器通过<script></script>
标签的src
属性向服务端请求数据,得到的是一个函数调用,这种请求方式就是JSONP
JSONP不属于真正的AJAX请求,因为没有使用XMLHttpRequest
对象
JSONP仅支持GET请求方式
编写JSONP接口
1 2 3 4 5 6 7 8 9 10 app.get ("/jsonp" , function (req, resp ) { const funcName = req.query .callback ; let data = {"key" , "value" }; const scriptStr = `${funcName} (${JSON .stringify(data)} )` ; resp.send (scriptStr); });
完成 参考文献 哔哩哔哩——黑马程序员