Express介绍
-
express是什么?
express是一个简单,快速的,及简的nodejs web应用开发框架,通过它,可以很轻松的构建各种web应用。
例如:1.接口服务
2.传统的web网站
3.开发工具的集成 -
express本身是极简的,仅仅提供了web的基础能力,但是它通过中间件的方式继承了许许多多的外部插件来处理http请求。
body-parser:解析http请求体
compression:压缩http相应。
cookie-parser:解析cookie数据
cors:处理跨域资源请求
morgan:http请求日志记录。 -
express中间件是一把双刃剑。
1.它让express本身变的更加灵活和简单。
缺点在于虽然有一些中间可以解决几乎所有的问题或需求,但是挑选合适的包有时也是一种挑战。
不对nodejs已有的特性进行二次封装,只是在它之上扩展了web应用所需的基本功能。
3.内部使用的还是http模块。请求对象继承自:。响应对象继承自: -
有很多流行框架基于express。例如:nestjs, sails
-
express特性:
1.简单易学,
2.丰富的基础api支持,以及常见的http辅助程序,例如:重定向,缓存
3.,提供了强大的路由功能
4.灵活的中间件
5.高性能
6.非常稳定。 -
express应用场景:
1.传统的web网站
2.接口服务
3.服务端渲染中间层
4.开发工具 json server, webpack-dev-server
路由基础
查看官方文档:/4x/
路由指的是:确定应用程序如何响应客户端对特定端点的请求,该特定端点是url和特定的http请求方法(get,post等。)
每个路由可以具有一个或多个处理程序函数,这些函数在匹配该路由时执行。路由定义采用一下结构:
时express实例
时小写的http请求方法
时服务器上的路径。
是当路由匹配时执行的功能。
const express = require('express')
const app = express()
app.get('/', (req, res) => {
res.send('get /')
})
app.post('/', (req, res) => {
res.send('post / ')
})
app.post('/user', (req, res) => {
res.send('post /user ')
})
请求和响应
express应用使用路由回调函数的参数,request和response对象来处理请求和响应的数据。
app.get('/', (req, res) => {
req.url // 请求地址
req.method // 请求方式
req.headers // 请求头
res.cookie('foo', 'bar')
})
express不对nodejs已有的特性进行二次抽象,只是在他的接触上扩展了web应用所需的基本功能。
路由:
需求:
实现查询任务列表: get /todos
根据id查询单个任务: get todos/:id
添加任务:post /todos
修改任务: patch /todos
删除任务. delete /todos/:id
const express = require('express')
const fs = require('fs')
const { getDb, saveDb } = require('./db')
const app = express()
// 根据不同的content-type格式解析数据
// 解析表单请求体:application/json
app.use(express.json())
// 解析表单请求体:application/x-www-form-urlencoded
app.use(express.urlencoded())
// 获取列表
app.get('/todos', async(req, res) => {
// ('', 'utf-8', (err, data) => {
// if (err) {
// // json:只能发送json格式的数据
// // 返回给客户端的响应
// return (500).json({error: })
// }
// const db = (data)
// // 返回给客户端
// (200).json()
// })
try {
const db = await getDb()
res.status(200).json(db.todos)
} catch (error) {
return res.status(500).json({error: err.message})
}
})
// 获取详情
app.get('/todos/:id', async(req, res) => {
// ('', 'utf-8', (err, data) => {
// if (err) {
// // json:只能发送json格式的数据
// // 返回给客户端的响应
// return (500).json({error: })
// }
// const db = (data)
// const todo = (todo => == )
// if (!todo) {
// return (404).json({code: '0', data: {}})
// } else {
// return (200).json({ code: '200', data: todo })
// }
// })
try {
const db = await getDb()
const todo = db.todos.find(todo => todo.id == req.params.id)
return res.status(200).json({ code: '200', data: todo })
} catch (error) {
return res.status(404).json({code: '0', data: {}})
}
})
// 添加
app.post('/todos', async(req, res) => {
// 1.获取客户端请求体参数:
// 2.数据验证
// 3.数据验证通过么,把数据存储到中
try {
const todo = req.body
if (!todo.title) {
return res.status(422).json({ code: '0', error: 'title is request' })
} else {
const db = await getDb()
const lastTodo = db.todos[db.todos.listen - 1]
todo.id = lastTodo ? lastTodo.id + 1 : 1
db.todos.push(todo)
await saveDb(db)
// 发送响应
return res.status(200).json({ code: '200', message:'插入成功' })
}
} catch (error) {
return res.status(500).json({ code: '0', error: 'title is request' })
}
})
// 修改
app.patch('/todos/:id', async(req, res) => {
try {
// 1.获取表单数据
// 2。查找要修改的任务项目
const todo = req.body
const db = await getDb()
const ret = db.todos.find(todo => todo.id == req.params.id)
if (!ret) {
return res.status(0).json({ code: '0', error: '数据不存在' })
}
Object.assign(ret, todo)
await saveDb()
return res.status(200).json({ code: 200, data: ret })
} catch (error) {
return res.status(500).json({ code: '0', error: '错误啦' })
}
})
// 删除任务
app.delete('/todos/:id', (req, res) => {
try {
const todoId = Number.parseInt(req.params.id)
const db = await getDb()
const index = db.todos.findIndex(todo => todo.id === todoId)
if (index === -1) {
return res.status(404).end()
}
db.todos.splice(index, 1)
await saveDb()
} catch (error) {
return res.status(500).json({ code: '0', error: error.message })
}
})
app.listen(3000, () => {
console.log('server is strat')
})
const fs = require('fs')
const path = require('path')
const { promisify } = require('util')
const readFile = promisify(fs.readFile)
const writeFile = promisify(fs.writeFile)
const dbPath = path.join(__dirname, './')
exports.getDb = async () => {
const data = await readFile(dbPath, 'utf-8')
return JSON.parse(data)
}
// 向todo中存储数据
exports.saveDb = async (db) => {
const data = JSON.stringify(db)
await writeFile(dbPath, data)
}
{"todos":[{"id":1,"title":"睡觉"},{"id":2,"title":"吃饭"},{"id":3,"title":"code"},{"id":4,"title":"上课"},{"title":"22222","id":1},{"title":"2222222222","id":1},{"title":"2222222222","id":1}]}
中间件
不需要修改原有的代码,增加代码的功能。
// 案例:中间件的配置
app.use(express.json())
// 自己封装的函数
consyt myLogger = (req) => {
console.log(req.method, req.url, Date.now())
}
app.get('/', (req, res) => {
myLogger()
})
中间件的顺序很重要。代码的执行顺序从上到下。
// req:请求对象
// res: 响应对象
// next: 进入下一个中间件
// 当前实现的功能:每次进入一个请求地址,都会输出console里面的内容。
app.use((req, res, next) => {
console.log(req.method, req.url, Date.now())
// 交出执行权,往后继续匹配执行
next()
})
app.get('/', (req, res) => {
})
中间件概念
express中间价和AOP(面向切面编程)是一个意思,都是需要经过一些的步骤,不去修改自己的代码啊,以此来扩展或者处理一些功能。
什么是AOP(面向切面编程)?
将日志记录,性能统计,安全控制,事物处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而该拜年这些行为的时候不影响业务逻辑的代码。
利用aop可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑部分之间的耦合度降低,提供程序的可重用性,同时提高可开发的效率和可维护性。
总结:就是在现有的代码中,程序的生命周期或者横向流程中,加入/减去一个或多个功能,不影响原有的逻辑。
express中间件函数
在express中,中间件就是一个可以访问的请求对象,响应对象和调用next方法的函数。
中间件-分类-应用程序级别中间件
express中的中间件分类:
1.应用程序级别中间件
2.路由级别中间件
3.错误处理中间件
4.内置中间件
5.第三方中间件
- 1.应用程序级别中间件
都是使用
不关心请求路径是什么,哪个请求路径都会指向的。
var express = require('express')
var app = express()
app.use(function (req, res, next) {
console.log(Date.now())
next()
})
限制请求路径的中间件:
// 只有匹配到/user/:id这个路径才会执行的中间件。
app.use('/user/:id', (req, res, next) => {
console.log(Date.now())
next()
})
限定请求路径 + 请求方法
// 会有匹配到get请求,请求路径为/users/:id才会执行函数中的方法
app.get('/users/:id', (req, res, next) => {})
多个处理函数的中间件
app.use('/user/:id', (req, res, next) => {console.log('1')next()}, (req, res, next) => {console.log('2'); next()})
给一个路径定义多个处理中间件
app.get('/user/:id', () =>{}, () =>{},)
中间件也可以在数组中声明使用。
如下:创建了一个带有中间件的子堆栈的数组,该子堆栈处理对user/:id路径的get请求。
function logOriginUrl(req, res, next) {
console.log(req.originalUrl)
next()
}
function logMethod(req, res, next) {
console.log(req.method)
next()
}
var logStuff = [logOriginUrl, logMethod]
app.get('/user/:id', logStuff, function (req, res, next) {
res.send('user info')
})
路由器 - 级别中间件
const express = require('express')
// 1.创建路由实例
路由实例就相当于一个mini express实例
const router = express.router()
app.get
app.post
// 2.配置路由
router.get('/foo', (req, res) => {})
router.post('/about', (req, res) => {})
// 3.导出路由实例
module.exports = router
// 4.将路由挂载继承到express应用当中
在文件当中使用router
const exporess = require('express')
// 1.引入路由
const router = require('./')
app.use('/user/:id', (req, res, next) => {})
// 2.直接挂载路由,路由的访问路径为:/foo
app.use(router)
// 给路由设置访问前缀, 路由的访问路径为:/provider/foo
app.use('/provider',router)
错误处理 - 中间件
以与其他中间件函数相同的方式定义错误处理中间件函数,出了使用4个参数而不是参数之后,错误处理中间件始终带有4个参数,必须提供4个参数已将其标识为错误处理中间件函数。即使不需要使用该next对象,也必须制定它以维护签名。否则,该next对象将被解释为常规中间件。
如果将任何内容传递给next()函数(字符串‘router’除外)express都会将当前请求视为错误,并且将跳过所有剩余的无错处理路由和中间件函数。
app.get('/users', (req, res, next) => {
try{
}catch(err) {
next('err')
}
})
// 在所有的中间件之后挂载处理中间件
// 必须是4个参数,缺一不可。
app.use((err, req, res, next) => {
console.log('err', err)
res.status(500).send('出错啦')
)
next(‘router’)往后匹配当前中间件堆栈中的下一个。
next(err)跳过所有剩余的无错误处理路由和中间件函数。
处理404中间件
通常会在所有的路由之后配置处理404的内容。
请求进来依次从上到下。
app.use('req', 'res', 'next', () => {
res.status(404).send('404 not fond')
})
内置中间件
内置中间件:就是express本身自己存在的。
express有5个内置中间件。
()
()
()
()
()
第三方中间件
常用官方维护的第三方中间件 /en/resources/
express路由
express支持错有http请求方法相对应的方法:get,post。。。
有一种特殊的方法:()
app.all('/serect', (req, res, next) => {})
查询字符串不是路由路径的一部分。
- 此理由路径会将请求匹配到/
app.get('/', () => {})
- 以下是一些基于字符串模式的路由路径。
此路径将与acd和匹配abcd
app.get('/ab?cd', () => {})
- 这条路线的路径将会匹配abcd,abbcd,adddcd等等
app.get('/ab+cd', () => {})
- 这条路线的路径将会匹配abcd,abxcd,abRAndomcd, ab1234cd等等
* : 表示随意的。
app.get('/ab*cd', () => {})
- 这条路线的路径将会匹配/abe, /abcde
app.get('/ab(cd)e', () => {})
- 这条路线的路径将会匹配
带有 “a”的任何内容。
app.get(/a/, () => {})
- 这条路线的路径将会匹配
butterfly和dragonfly
,但不带butterflyman,和dragonflyman等等。
app.get(/.*fly$/, () => {})
路径参数
路由参数被命名为url段,用于捕获url中在其位置处制定的值。捕获的值将其填充到对象中,并将路径指定的route参数的名称作为其各自的键。
app.get('/users/:userId/books/:bookid', function (req, res, next) {})
- 多个回调函数处理一个路由:
app.get('/example/b', (req, res, next) => {next()}, (req, res, next) => {next()})
- 回调函数数组处理路由:
var cb0 = function (req, res, next) {next()}
var cb1 = function (req, res, next) {next()}
var cb2 = function (req, res, next) {next()}
app.get('example/c', [cb0, cb1, cb2])
- 独立功能和功能数组的组合可以处理路由:
var cb0 = function (req, res, next) {next()}
var cb1 = function (req, res, next) {next()}
app.get('example/c', [cb0, cb1], (req, res, next) => {
next()
})
响应方法:
res.download() // 提示要下载
res.end() // 结束响应
res.json() // 发送json格式的数据
res.jsonp() // 发送带有jsonp支持的json响应。
res.redirect() // 重定向请求。
res.render() // 渲染视图模版
res.send() // 发送各种类型的响应。
res.sendFile() // 将文件作为八位字节流发送
res.sendStatus() // 设置响应状态码。
()
定义链式路由处理程序。
app.route('/book')
.get()
.post()
.put()
快速路由器:
该累创建模块化的,可安装的路由。