文章目录
1、Express 应用使用回调函数的参数: request 和 response 对象来处理请求和响应的数据。
- Request 对象
request 对象表示 HTTP 请求,包含了请求查询字符串,参数,内容,HTTP 头部等属性属性/方法 说明 app 当callback为外部文件时,用req.app访问express的实例 baseUrl 获取路由当前安装的URL路径 body/cookies 获得「请求主体」/ Cookies fresh/stale 判断请求是否还「新鲜」 hostname/ip 获取主机名和IP地址 originalUrl 获取原始请求URL params 获取路由的parameters path 获取请求路径 protocol 获取协议类型 query 获取URL的查询参数串 route 获取当前匹配的路由 subdomains 获取子域名 accepts() 检查可接受的请求的文档类型 acceptsCharsets/acceptsEncodings/acceptsLanguages 返回指定字符集的第一个可接受字符编码 get() 获取指定的HTTP请求头 is() 判断请求头Content-Type的MIME类型 - Response 对象
response 对象表示 HTTP 响应,即在接收到请求时向客户端发送的 HTTP 响应数据属性/方法 说明 app 当callback为外部文件时,用req.app访问express的实例 append() 追加指定HTTP头 set() 在res.append()后将重置之前设置的头 res.cookie(name,value [,option]) 设置Cookie opition domain / expires / httpOnly / maxAge / path / secure / signed clearCookie() 清除Cookie download() 传送指定路径的文件 get() 返回指定的HTTP头 json() 传送JSON响应 jsonp() 传送JSONP响应 location() 只设置响应的Location HTTP头,不设置状态码或者close response redirect() 设置响应的Location HTTP头,并且设置状态码302 render(view,[locals],callback) 渲染一个view,同时向callback传递渲染后的字符串,如果在渲染过程中有错误发生next(err)将会被自动调用。callback将会被传入一个可能发生的错误以及渲染后的页面,这样就不会自动输出了。 send() 传送HTTP响应 sendFile(path [,options] [,fn]) 传送指定路径的文件 -会自动根据文件extension设定Content-Type set() 设置HTTP头,传入object可以一次设置多个头 status() 设置HTTP状态码 type() 设置Content-Type的MIME类型
2、Express路由
路由是指应用程序的终端节点 (URI) 如何响应客户端请求。
在Express中,路由指的是客户端的请求与服务器处理函数之间的映射关系。
Express中的路由分3部分组成,分别是请求的类型、请求的URL地址、处理函数,格式如下:app.method(path, handler)
1.路由方法
// GET method route
app.get('/', function (req, res) {res.send('GET request')
})// POST method route
app.post('/', function (req, res) {res.send('POST request')
})
app.all() 用于在所有 HTTP 请求方法的路径上加载中间件函数。
无论是使用 GET、POST、PUT、DELETE 还是 http 模块中支持的任何其他 HTTP 请求方法,都会对路由 “/secret” 的请求执行以下处理程序。
app.all('/secret', function (req, res, next) {console.log('all')next() // pass control to the next handler
})
2.路由路径
路由路径可以是字符串、字符串模式或正则表达式。
// acd和abcd
app.get('/ab?cd', function (req, res) {res.send('ab?cd')
})
总结:问号前面字符可有可无
app.get('/ab(cd)?e', function (req, res) {res.send('ab(cd)?e')
})
总结:问号前面括号内的字符可有可无
app.get('/ab+cd', function (req, res) {res.send('ab+cd')
})
总结:加号前面字符可无限叠加
app.get('/ab*cd', function (req, res) {res.send('ab*cd')
})
总结:星号前面的字符为开始,后边的字符为结束字符,中间可以任意字符或数字
app.get(/a/, function (req, res) {res.send('/a/')
})
总结:满足正则/a/
的都满足此方法
app.get(/.*fly$/, function (req, res) {res.send('/.*fly$/')
})
总结:满足正则/.*fly$/
以fly字符结束的路由
3.路由处理程序
在没有理由继续当前路由时将控制权传递给后续路由。next(‘route’)
路由处理程序可以采用函数、函数数组或两者的组合形式。
-
单个回调函数可以处理路由。
var express = require('express'); var app = express(); app.get('/abc', function (req, res) {res.send('hello abc'); }) app.listen(8081, function () {console.log("服务启动") })
-
多个回调函数可以处理一个路由(确保指定对象)next();
var express = require('express'); var app = express(); app.get('/abc', function (req, res, next) {console.log(111)next() }, function(req, res) {res.send('hello abc next'); }) app.listen(8081, function () {console.log("服务启动") })
-
回调函数数组可以处理路由。
var express = require('express'); var app = express(); var a0 = function (req, res, next) {console.log('A0')next() } var a1 = function (req, res, next) {console.log('A1')next() } var a2 = function (req, res) {res.send('Hello from A!') } app.get('/abc', [a0, a1, a2]) app.listen(8081, function () {console.log("服务启动") })
-
独立函数和函数数组的组合可以处理路由。
var express = require('express'); var app = express(); var a1 = function (req, res, next) {console.log('A1')next() } var a2 = function (req, res) {res.send('Hello from A!') } app.get('/abc', function (req, res, next) {console.log('A0')next() },[a1, a2]) app.listen(8081, function () {console.log("服务启动") })
3. 模块化路由
为了方便对路由进行模块化的管理,Express不建议将路由直接挂载到app上,而是推荐将路由抽离为单独的模块。
将路由抽离为单独模块的步骤如下:
1.创建路由模块对应的.js文件
2.调用express.Router()函数创建路由对象
3.向路由对象上挂载具体的路由
4.使用module.exports向外共享路由对象
5.使用app.use()函数注册路由模块
- 创建路由模块
// router.js 文件 var express = require('express'); // 1.导入express var router = express.Router(); // 2.创建路由对象router.get('/login/info', (req, res) => { // 3.挂载登录用户信息res.send('Get user list.'); }); router.post('/singUp/add', (req, res) => { // 4.挂载注册用户的路由res.send('Add new user.'); }); module.exports = router; // 5.向外导出路由对象
- 注册路由模块
const express = require('express'); const app = express(); // 1.导入路由模块 const userRouter = require('./router.js'); // 2.使用app.use()注册路由模块 app.use(userRouter); app.listen(8081, () => {console.log('http://127.0.0.1') })
- 为路由模块添加前缀
// 类似于托管静态资源时,为静态资源统一挂载访问前缀一样 // 1.导入路由模块 const userRouter = require('./router.js'); // 2.使用app.use()注册路由模块,并添加统一的范围前缀 /api app.use('/api', userRouter);
4. Express中间件
1.中间件简介
- 中间件简介
中间件是一种特殊的路由处理函数,它可以在请求到达目标处理函数之前,进行一些预处理
操作。Express 支持使用中间件来实现各种功能,例如身份验证、请求日志记录,处理 CORS(跨源资源共享)等。
注意:中间件函数的形参列表中,必须包含next参数,而路由处理函数中只包含req和res。
next函数是实现多个中间件连续调用的关键,它表示把流转关系转交给下一个中间件或路由。
可以使用app.use()连续定义多个全局中间件。客户端请求到达服务器之后,会按照中间件定义的先后顺序依次进行调用
const express = require('express'); const app = express(); // 一个简单的中间件 app.use((req, res, next) => {console.log(`Request received at ${new Date()}`);next(); // 将控制权传递给下一个中间件或路由处理器 }); // 一个路由处理器,用于处理 GET 请求 app.get('/', (req, res) => {res.send('Hello, World!'); });// 启动服务器 app.listen(8081, () => {console.log('Server is running on port 8081'); });
- 局部中间件
不使用app.use()定义的中间件,叫做局部生效的中间件, 中间件只在"当前路由中生效",var express = require('express'); var app = express();var myLogger = function (req, res, next) {console.log('LOGGED')next() } app.get('/', myLogger, function (req, res) {res.send('Hello World!') })
- 中间件的5个使用注意事项
- 监听 req 的 data 事件
在中间件中,需要监听req对象的data事件,来获取客户端发送到服务器的数据。
如果数据量比较大,无法一次性发送完毕,则客户端会把数据切割后,分批发送到服务器。所以data事件可能会触发多次,每一次触发data事件时,获取到数据只是完整数据的一部分,需要手动对接收到的数据进行拼接。// 定义变量,用来储存客户端发送过来的请求体数据 let str = '' // 监听 req 对象的 data 事件(客户端发送过来的新的请求体数据) req.on('data',(data) => {// 打印请求数据console.log(data) })
- 监听 req 的 end 事件
当请求体数据接收完毕之后,会自动触发req的end 事件。
可以在req的end 事件中,拿到并处理完整的请求体数据。// 监听 req 对象的 end 事件(请求体发送完毕后自动触发) req.on('end',() => {// => 打印完整的请求体数据console.log(str)// TODO: 业务逻辑// ....... })
2.中间件分类
- 应用程序级中间件
通过app.use()或app.get()或 app.post(),绑定到app实例上的中间件,叫做应用级别的中间件,var app = express(); var myLogger = function (req, res, next) {console.log('LOGGED')next() } // 应用级别的中间件(全局中间件) app.use((req, res, next) => {req.name = 'router'req.on('end',() => {console.log('end')})next(); }); // 应用级别的中间件(局部中间件) app.get('/', myLogger, (req, res) => {console.log(req.name)res.send('Home page.') }); app.listen(8081, function () {console.log("服务启动") })
- 路由器级中间件
绑定到express.Router()实例上的中间件,叫做路由级别的中间件。它的用法和应用级别中间件没有任何区别。只不过,应用级别中间件是绑定到 app实例上,路由级别中绚件摸定到router 实例上var express = require('express') var app = express() var router = express.Router() router.use(function (req, res, next) {console.log('Time:', Date.now());next() }) app.use('/', router) app.listen(8081, function () {console.log("服务启动") })
- 错误处理中间件
错误处理中间件是专门用来捕获整个项目中发生的异常错误,从而防止项目异常崩溃的问题。
格式:错误级别的中间件的 function 处理函数中,必须有 4 个形参,形参顺序从前到后,分别是(err,req,res,next)。
注意:错误级别的中间件,必须注册在所有路由之后
app.get('/', (req, res) => { // 1.路由throw new Error('服务器内部发生了错误'); // 1.1.抛出一个自定义的错误res.send('Home Page.'); }); app.use((err, req, res, next) => { // 2.错误级别的中间件console.log('发生了错误:' + err.message); // 2.1.在服务器打印错误消息res.send('Erroe!' + err.message); // 2.2.向客户端响应错误相关的内容 });
- 内置中间件
三个内置的中间件分别是
express.static 是快速托管静态资源的内置中间件 例如:HTML文件、图片、CSS样式等(无兼容性)
express.json是拿来解析json格式数据的
express.urlencoded是拿来解析urlencoded格式数据的var express = require('express'); var app = express(); // 注意这是中间件 所以必须配置到路由之前 app.use(express.json()) app.use(express.urlencoded({extended : false})) app.listen(8081, function () {console.log("服务启动") })
- 第三方中间件
非Express官方内置的,而是由第三方开发出来的中间件,叫做第三方中间件。在项目中,大家可以按需下载并配置第三方中间件,从而提高项目的开发效率。
安装所需功能的 Node.js 模块,然后在应用程序级别或路由器级别将其加载到应用程序中。
以cookie-parser为示例:$ npm install cookie-parser
var express = require('express') var app = express() var cookieParser = require('cookie-parser') app.use(cookieParser())
3.自定义中间件
自定义中间件步骤:
- 定义中间件
- 监听req的data事件
- 监听req的end事件
- 使用querystring模块解析请求体数据
- 将解析出来的数据对象挂载为req.body
- 将自定义中间件封装为模块
// myparse.js //1.1 导入内置模块 const qs=require('querystring') //1.2 编写解析函数 function myparse(req,res,next){//2.2 定义一个变量存储客户端字符串let str=''//2.1 对客户端请求数据的监听//注意是对客户端对象进行监听,而不是服务器req.on('data',(chunk)=>{str+=chunk})//2.4 进行发送数据结束的监听req.on('end',()=>{//倘若有响应,说明数据发送结束,我们已经拿到所有数据console.log(str)//4.2 利用内置模块的parser()进行数据解析const body=qs.parse(str)//4.3 进行数据对象的挂载req.body=bodyconsole.log(body)})//2.5 不要忘记需要调用next函数next() } //1.4 通过module.exports暴露 module.exports = myparse
// 使用 var express = require('express'); var app = express(); // 2.1 导入自定义解析模块 const myparse = require('./mybody-parse') app.use(myparse) app.get('/login', function (req, res) {console.log(req.body);res.end(req.body); })