认识express
express是基于Node.js平台的web开发框架
作用和Node.js内置的http模块类似,是专门用来创建Web服务器的。
本质上Express就是一个npm的第三方包提供了快速创建Web服务器的便捷方法。
中文官网:expressjs.com.cn
express的作用:快速方便的创建 Web网站服务器和 API 接口服务器
express的基本使用
一、下载express包 npm i express@4.17.1
二、创建基本的web服务器
// 导入express
const express = require('express')
// 创建web服务器
const app = express()
// 启动web服务器
app.listen(80,()=>{
console.log('express server running at http://127.0.0.1')
})
监听 GET、POST 请求
1、get请求一般用来请求获取数据、post请求一般作为发送数据到后台,传递数据,创建数据
2、get请求也可以传参到后台,但是传递的参数则显示在地址栏,安全性低,且参数的长度也有限制(2048字符)、 post请求则是将传递的参数放在request body中,不会在地址栏显示,安全性比get请求高,参数没有长度限制
3、get请求刷新浏览器或者回退没有影响、post请求则会重新请求一遍
4、get请求可以被缓存,也会保留在浏览器的历史记录中、post请求不会被缓存,也不好保留在浏览器的历史记录中
5、get请求通常是通过url地址请求、post常见的则是form表单请求
参数一、客户端请求的URL地址 参数二、请求队形的处理函数
req 请求对象 res 响应对象
通过 app.post( )方法监听客户端POST请求。
通过 app.get( )方法可以监听客户端的GET请求。
把内容响应给客户端
通过 res.send( )方法,可以把处理好的内容发送给客户端
// 监听客户的 GET 和 POST 请求,并向客户端响应具体内容
app.get('/user',(req,res)=>{
// 调用res.send方法向客户端响应一个JSON对象
res.send( {name: 'zhangsan',age: 20, gender: '男'})
})
app.post('/user',(req,res)=>{
// 调用res.send向客户端响应一个文本字符串
res.send('请求成功')
})
获取 URL 中携带的查询参数
通过 req.query 对象可以访问到客户端通过查询字符串的形式,发送到服务器的参数
app.get('/',(req,res)=>{
// 通过 req.query 可以获取到客户端发送过来的查询参数
// 默认情况下req.query是空对象
console.log(req.query)
res.send(req.query)
})
获取URL中的动态参数
通过 req.params 对象可以访问到URL中通过 :匹配到的动态参数
// 这里的 :id是一个动态的参数
app.get('/user/:id',(req,res)=>{
// req.params 是动态匹配到的 URL 参数,默认也是一个空对象
console.log(req.params)
res.send(req.params)
})
向 http://127.0.0.1/user/ABCD发送请求后得到响应:
{
"id": "ABCD"
}
express.static 托管静态资源
express.static( )方法可以方便的创建一个静态资源服务器。 通过如下代码可以将public目录下的文件对外开放
app.use(express.static('public'))
注意:存放静态文件的目录名不会出现在URL中
**想要托管多个静态资源目录,则多次调佣express.static( )即可
const express = require('express')
const app = express()
// 调用 express.static( ) 方法快速对外提供静态资源
// 传入文件目录位置
app.use(express.static('./clock'))
app.use(express.static('./files'))
// 启动服务
app.listen(80,()=>{
console.log('express server running at http://127.0.0.1')
})
静态资源访问路径之前挂载前缀
app.use('/abc',express.static('./files'))
:只需要在express.static 前面添加一个前缀即可
学习nodemon工具
该工具可以监听Node.js项目的代码,如果项目的代码发生了修改,该工具会自动重启项目,更方便开发和调试。
安装nodemon npm install -g nodmon
安装nodemon工具后,启动项目的方法:nodemon app.js
Express路由
路由的概念:就是映射关系。在express中路由指的是客户端的请求与服务器处理函数之间的映射关系
express中的路子分三部分组成
- 请求的类型(METHOD):GET或者POST
- 请求的地址PATH
- 处理函数HANDLER
app.METHOD(PATH,HANDLER)
一个简单的路由的例子
app.get('/',function(req,res){
res.send('hello,world')
})
app.post('/',(req,res)=>{
res.send('got a POST request')
})
路由的匹配过程
每一个请求到达服务器都需要先经过路由的匹配,匹配成功只会才会调用对应的处理函数。
会按照路由的顺序进行匹配。
如果请求类型和请求的URL同事匹配Express才会将这次请求转交给对应的function进行处理。
模块化路由
Express不建议将路由直接挂载到app上。为了方便对路由进行模块化的管理,一般将路由抽离为单独的模块。
一、创建路由模块对应的 .js 文件
二、调用 **express.Router( )**创建路由对象
三、向路由对象上挂载具体的路由
四、使用 module.exports向外共享路由对象
五、使用 app.use( )函数注册路由模块
创建路由模块
// 这是一个路由模块
//1、导入express
const express = require('express')
//2、创建路由对象
const router = express.Router()
//3、挂载具体的路由
router.get('/user/list',(req,res)=>{
res.send('get user list')
})
router.post('/user/add',(req,res)=>{
res.send('add new user')
})
//4、向外到处路由对象
module.exports = router
注册路由模块
const express = require('express')
const app = express()
// 导入路由模块
const router = require('./05router')
// 注册路由模块
//app.use(router)
app.use('/api',router)// 统一添加访问前缀。
// 启动服务
app.listen(80,()=>{
console.log('router server running at http://127.0.0.1')
})
app.use( )函数的作用就是用来注册全局中间件的。
express中间件
中间件指的就是业务处理过程中的中间处理环节,一般有输入与输出。
中间件是一个可访问请求对象(req)和响应对象(res)以及(next)的函数
中间件调用流程
当一个请求到达Express的服务器,可能连续调用多个中间件,对该请求进行预处理。
请求=>中间件处理next(中间件处理2 )=>next(中间件处理n)=>next(处理完毕)=>路由响应到客户端
中间件的格式
中间件本质上就是一个Function处理函数:格式如下:
app.get('/',function(req,res,next){
next( )
})
中间件的形参列表中必须包含next形参
next函数的作用
next函数时实现多个中间件连续调用的关键,表示把流转关系转件给下一个中间件或路由。
const express = require('express')
const app = express()
// 定义一个中间件
const mw = (req,res,next)=>{
console.log("这是一个简单的中间件函数")
// 把流转关系转交给下一个中间件或路由
next()
}
app.listen(80,()=>{
console.log("http:127.0.0.1")
})
全局生效的中间件
客户端发起任何请求,到达服务器之后都会被触发的中间件,即为全局生效中间件。
通过 app.use(中间件函数),即可定义一个全局生效中间件
示例代码:
const express = require('express')
const app = express()
// 定义一个中间件
const mw = (req,res,next)=>{
console.log("这是一个简单的中间件函数")
// 把流转关系转交给下一个中间件或路由
next()
}
// 将 mw 注册为全局生效的中间件
app.use(mw)
app.get('/',(req,res)=>{
console.log('调用了 / 路由')
res.send('Home page.')
})
app.get('/user',(req,res)=>{
console.log('调用了 /user 路由')
res.send('uese page.')
})
// 启动服务器
app.listen(80,()=>{
console.log("http:127.0.0.1")
})
全局生效中间件的简化形式
app.use((req,res,next)=>{
console.log('这是一个简单的中间件函数的简写')
next()
})
中间件的作用
由于多个中间件之间可以共享一份req和res,所以我们可以再上有的中间件中统一为 req 或 res 对象添加自定义的属性或方法,宫下游的中间件或路由进行使用。
const express = require('express')
const app = express()
app.use((req,res,next)=>{
// 获取到请求到达服务器的事件
const time = Date.now()
// 为req对象挂载自定义属性,从而把时间共享到后面所有的路由
req.startTime = time
next()
})
const a = app.get('/',(req,res)=>{
res.send('Home page.' + req.startTime)
})
app.get('/user',(req,res)=>{
res.send('uese page.' +req.startTime)
})
// 启动服务器
app.listen(80,()=>{
console.log("http:127.0.0.1")
})
定义多个全局中间件
可以使用 app.use( ) 定义多个全局中间件。客户端请求到达服务器之后,会按照定义的先后顺序依次执行
const express = require('express')
const app = express()
// 定义了第一个全局中间件
app.use((req,res,next)=>{
console.log('调用了第一个全局中间件')
next()
})
// 定义了第二个全局中间件
app.use((req,res,next)=>{
console.log('调用了第二个全局中间件')
next()
})
// 定义一个路由,请求路由后先调用中间件
app.get('/user',(req,res)=>{
console.log('最后进入了路由')
res.send('User page.')
})
app.listen(80,()=>{
console.log("http://127.0.0.1")
})
局部生效的中间件
不使用 app.use( ) 定义的中间件,都叫做局部生效中间件
const express = require('express')
const app = express()
// 定义中间件
const mw1 = (req,res,next)=>{
console.log('调用了局部生效的中间件')
next()
}
// 创建路由
// mw1中间件只会在当前路由中生效
app.get('/',mw1,(req,res)=>{ res.send('Home page.') })
app.get('/user',(req,res)=>{ res.send('User page.') })
app.listen(80,()=>{
console.log("http://127.0.0.1")
})
定义多个局部中间件的方法
app.get('/',mw1,mw2,(req,res)=>{ res.send('Home page.') })
app.get('/',[mw1,mw2],(req,res)=>{ res.send('Home page.') })
Express中间件初体验
中间件的使用注意事项
- 一定要在路由之前注册中间件
- 客户端发送过来的请求可以连续调用多个中间件进行处理
- 执行文中间件的业务代码后一定要调用next( )函数
- 在调用完 next( )之后,尽量不要再写额外代码,防止逻辑混乱
- 连续调用多个中间件,多个中间件之间共享req和res
中间件的分类(五大类)
一、应用级中间件
通过 app.use( ) 或 app.get( ) 或者 app.post( ) 绑定到实例上的中间件
二、路由级中间件
绑定到 express.Router( ) 实例上的中间件。
三、错误级别中间件
作用是捕获项目中可能会发生的异常错误,从而防止项目崩溃、错误级别中间件必须有四个形参: (err,req,res,next)
const express = require('express')
const app = express()
app.get('/',(req,res)=>{
// 认为的制造一个错误
throw new Error('服务器内部发生错误')
res.send('Home page.')
})
// 定义错误级别中间件来捕获整个项目的异常错误从而防止程序崩溃
app.use((err,req,res,next)=>{
console.log('发生了错误'+err.message)
res.send('Error:'+err.message)
})
app.listen(80,()=>{
console.log("http://127.0.0.1")
})
注意:错误级别中间件一定要注册到所有路由之后
四、Expreww内置中间件
Express内置了三个常用中间件
1、express.static:快速托管静态资源的内置中间件:HTML/CSS/图片等。
2、express.json:解析 JSON 格式的请求体数据。4.16.0+node可用
app.use(express.json())
3、express.urlencodes:解析 URL-encoded 格式的请求数据。4.16.0+node可用,该中间件就是基于第三方body-parser 封装出来的。
app.use(express.urlencoded( { extended: false} ) )
示例:
const express = require('express')
const app = express()
// 通过express.json() 全局中间件解析表单中JSON数据
app.use(express.json())
// 通过express.urlencoded() 中间件来解析表单中的 url-encoded 格式的数据。
app.use(express.urlencoded( { extended: false} ))
app.post('/user',(req,res)=>{
// 服务器可以使用 req.body属性接收客户端发送的请求体数据
// 但没有配置表单解析数据的中间件,req.body 默认等于 undefined
console.log(req.body)
res.send('Ok!')
})
app.post('/book',(req,res)=>{
// 通过 req.body 获取 url-encoded格式数据
console.log(req.body)
res.send('Book!')
})
app.listen(80,()=>{
console.log("http://127.0.0.1")
})
五、第三方中间件
非 Express 内置的,第三方开发出来的中间件。可以按需下载,如 body-parser 中间件
1、安装: npm i body-parser 2、导入 3、注册使用
const express = require('express')
const app = express()
// 配置解析表单数据的中间件
const parser = require('body-parser')
// 注册该中间件
app.use(parser.urlencoded( { extended: false }))
app.post('/user',(req,res)=>{
// 未配置解析中间件默认req.body值为undefined
console.log(req.body)
res.send('Ok!')
})
app.listen(80,()=>{
console.log("http:127.0.0.1")
})
案例:尝试自定义一个中间件
const express = require('express')
// 导入 querystringify模块,该内置模块其提供的parse()可以将查询字符串解析成对象格式
const qs = require('querystringify')
const app = express()
app.use((req,res,next)=>{
// 定义存储数据的字符串
let str = ''
// 监听req的dat事件
req.on('data',(chunk)=>{
str += chunk
})
// 监听req的end事件
req.on('end',(chunk)=>{
// 把字符串格式的请求数据解析成对象
req.body = qs.parse(str)
next()
})
})
// 路由
app.post('/user',(req,res)=>{
res.send(req.body)
})
// 启动服务器
app.listen(80,()=>{
console.log("http:127.0.0.1")
})
还可以将自定义中间件封装成模块
const qs = require('querystringify')
const bodyParser = (req,res,next)=>{
// 定义存储数据的字符串
let str = ''
// 监听req的dat事件
req.on('data',(chunk)=>{
str += chunk
})
// 监听req的end事件
req.on('end',(chunk)=>{
// 把字符串格式的请求数据解析成对象
req.body = qs.parse(str)
next()
})
}
module.exports = bodyParser
使用express写接口
创建API路由模块
//创建路由模块
const express = require('express')
const router = express.Router()
//在这里挂载对应的路由
module.exports = router
--------------------------------------
const express = require('express')
const app = express()
// 导入路由模块
const router = require('./19APIrouter')
// 注册路由模块到app
app.use('/api',router)
// 启动服务器
app.listen(80,()=>{
console.log("express server running at http:127.0.0.1")
})
编写 GET 接口与 POST 接口
const express = require('express')
const router = express.Router()
//在这里挂载对应的路由
router.get('/get',(req,res)=>{
// 通过 req.query 获取客户端,通过查询字符串,发送到服务器的数据
const query = req.query
//调用 res.send() 方法向客户端响应处理结果
res.send({
status:0, // 0表示处理成功,1表示处理失败
msg:'GET 请求成功', // 状态描述
data: query // 客户端的数据
})
})
//在这里挂载对应的路由
router.post('/post',(req,res)=>{
// 通过 req.body 获取请求体中包含 url-encoded 格式的数据
const body = req.body
//调用 res.send() 方法向客户端响应处理结果
res.send({
status:0, // 0表示处理成功,1表示处理失败
msg:'POST 请求成功', // 状态描述
data: body // 客户端的数据
})
})
module.exports = router
接口的跨域问题
以上的GET和POST接口存在跨域的问题
解决方案:
一、CORS(主流方案)
二、JSPNP(只支持GET请求)