Express学习笔记

Express

一、什么是Express?

Express 是基于 Node.js 平台,快速、开放、极简的 Web 开发框架

通俗的理解Express 的作用和 Node.js 内置的 http 模块类似,是专门用来创建 Web 服务器的

Express 的本质: 就是 npm 上的第三方包,提供了快速创建 Web 服务器的便捷方法。

Express 中文官网:http://www.expressjs.com.cn/

为什么有了 http 内置模块还要有 Express ?

http 用起来过于复杂、开发效率低;

Express 是基于 http 进一步封装的,能够提高开发效率

http 内置模块与 Express 的关系?

后者是前者的进一步封装

Express 能做什么

快速创建 Web 网站的服务器或 API 接口的服务器。

二、Express 的基本使用

安装

npm i express@版本号

使用

const express = require('express')

//接收 express 实例
const app = express()
app.listen(端口号, () => {

})

监听 GET 请求

app.get('请求的 url', function(req, res) {
    //可以向客户端发送 JSON 对象{}
    //也可以发送文本内容
    res.send()
})

监听 POST 请求

app.post('请求的 url', function(req, res) {
    //在服务器可以使用 req.body 这个属性来接收客户端发送过来的请求体数据
    //如果不配置解析表单数据的中间件,则 req.body 默认等于 undefine
    res.send(req.body)
})

获取 url 中携带的查询参数

app.get('请求的 url',(req, res) => {
    req.send(req.query)
})

获取 url 中的动态参数

app.get('请求的 url', (req, res) => {
    //params 默认是空对象,里面放着通过 : 动态匹配到的参数值
    req.send(req.params)
})

托管静态资源

通过它,可以非常方便的创建一个静态资源服务器

app.use(express.static('目录'))
挂载路径前缀:
app.use('/',express.static('目录'))

三、Express 的路由

客户端的请求服务器处理函数之间的映射关系

分 3 部分,请求的类型请求的 URL 地址处理函数

//method 为 get 或 post
app.method(path, (req, res) => {

})

按路由顺序匹配

模块化路由

  • 创建路由模块对应的 .js 文件
  • 调用 express.Router() 的函数创建路由对象
const express = require('express')
  • 向路由对象挂载具体的路由
const router = express.Router()

router.get('/目录', (req, res) => {
    res.send('')
})

router.post('/目录', (req, res) => {
    res.send('')
})
  • 使用 moudle.exports 向外分享路由对象
moudle.exports = router
  • 导入路由模块
const userRouter = require('./路由.js')
  • 使用 app.use() 函数注册路由模块
app.use(userRouter)

示例代码:

路由模块 router.js

const express = require('express')

const router = express.Router()

router.get('/abc', (req, res) => {
    res.send('abc')
})


router.post('/def', (req, res) => {
    res.send('def')
})
//导出路由模块
module.exports = router

调用路由模块:

const express = require('express')
//获取 express 实例
const app = express()
//导入路由模块
const router = require('./router')
//注册路由模块
//app.use() 函数的作用,就是用来注册全局中间件
app.use(router)

app.listen(3000,() => {
    console.log('run at http://localhost:3000');
})

四、中间件

什么是中间件?

业务处理环节中的中间处理环节

Express 中间件的调用流程

请求到达 Express 服务器之后,可以连续调用多个中间件,从而对这次请求进行预处理

Express 中间件的格式

本质上是一个函数,格式如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CAsTP6Vr-1689042339329)(C:\Users\mzhj\AppData\Roaming\Typora\typora-user-images\image-20230704211937969.png)]

注意:

中间件函数的形参列表中,必须包含 next 参数。而路由处理函数中只包含 req 和 res

next 函数的作用

next 函数多个中间件连续调用的关键,它表示把流转关系转交给下一个中间件路由

定义中间件函数

const mw = function (req, res, next) {
//业务处理完毕后,必须调用 next() 函数
    next()
}

全局生效的中间件

客户端发起的请求,到达服务器之后都会触发

const express = require('express')
const app = express()

const mw = function (req, res, next) {
//业务处理完毕后,必须调用 next() 函数
    next()
}

//注册全局生效的中间件
app.use(mw)
简化形式
app.use((req, res, next) {
//业务处理完毕后,必须调用 next() 函数
    next()
})

中间件的作用

多个中间件之间,共享 req 和 res 。基于这样的特性,我们可以在上游的中间件中,统一为 req 或 res 对象添加自定义属性方法,共下游的中间件或路由进行使用

局部生效的中间件

不适用 app.use() 定义的中间件,叫做局部生效的中间件,实例代码如下:

const wm1 = (req, res, next) {
    next()
}
// mw1 只会在当前路由生效
app.get('/路径', mw1, (req, res) {
    res.send('')
})

// wm1 不会影响下面这个路由
app.get('/路径', (req, res) {
    res.send('')
})

定义多个局部中间件

//以下两种写法都可
app.get('/路径', mw1, mw2, (req, res) {
    res.send('')
})
app.get('/路径', [mw1, mw2], (req, res) {
    res.send('')
})

注意

  • 中间件不能放在路由后面(请求是从上往下的)
  • 中间件必须调用 next()
  • 调用完 next() 后不能再写额外代码
  • 多个中间件共享 req 和 res

中间件的分类

  • 应用级别的中间件

通过 app.use() 或 app.get() 或 app.post, 绑定到 app 实例上的中间件,叫做应用级别的中间件,示例:

//全局中间件(全局)
app.use((req, res, next) {
    next()
})
//应用级别的中间件(局部)
app.get('/路径', mw1, (req, res) {
    res.send('')
})
  • 路由级别的中间件

绑定到 express.Router() 实例上的中间件,用法与应用级的没区别,示例:

const express = require('express')
const app = express()

const router = express.Router()

//路由级别的中间件
router.use((req, res, next) {
      next()
})

app.use('/', router)
  • 错误级别的中间件

专门捕获项目中发生的错误,从而防止项目异常崩溃的问题

格式:必须有四个形参,从前到后分别是 (err, req, res, next),必须注册在所有路由之后

app.get('/路径', (req, res) {
    throw new Error('错误信息')
    res.send('')
})

app.use((err, req, res, next) => {
    res.send('Error!' + err.message)
})
  • Express 内置的中间件

三个常用中间件

  1. express.static 快速托管静态资源的内置中间件(无兼容性)
  2. express.json() 解析 JSOH 格式的请求体数据(有兼容性,仅在 4.16.0+ 版本可用)
  3. express.urlencoded() 解析 URL-encoded 格式的请求体数据(有兼容性,仅在 4.16.0+ 版本可用)
//配置解析 application/json 格式数据的内置中间件
//客户端发送的请求体数据使用 req.body 接收
app.use(express.json())
//配置解析 application/x-www-form-urlencoded 格式数据的内置中间件
app.use(express.urlencoded({ extended: false }))
  • 第三方的中间件
  1. 运行 npm i 中间件名 安装中间件
  2. 使用 require 导入中间件
  3. 调用 app.use() 注册并使用中间件
const parser = require('body-parser')

parser.use(parser.urlencoded({ extended: false }))

内置的 url.encoded 中间件就是基于第三方 body-parser 的封装

自定义中间件

模拟一个类似 express.urlencoded 的中间件,解析 POST 提交到服务器的表单数据

//定义中间件
app.use((req, res, next) => {next()})
//监听 req 的 data 事件(数据量比较大,客户端就会切割,分批发送,所以会触发多次)
app.use((req, res, next) => {
    let str = ''
    req.on('data', chunk => {
        str += chunk
    })
    req.on('end', () => {
        console.log(str)
    })
    next()
})
//监听 req 的 end 事件(数据都接收完毕了)
app.use((req, res, next) => {
    let str = ''
    req.on('data', chunk => {
        str += chunk
    })
    req.on('end', () => {
        console.log(str)
    })
   
    next()
})
//使用 querystring 模块解析请求体数据
const qs = require('querystring')
app.use((req, res, next) => {
    let str = ''
    req.on('data', chunk => {
        str += chunk
    })
    req.on('end', () => {
        console.log(str)
    })
    const body = qs.parse(str)
    next()
})
//将解析出来的数据对象挂载为 req.body
app.use((req, res, next) => {
    let str = ''
    req.on('data', chunk => {
        str += chunk
    })
    req.on('end', () => {
        const body = qs.parse(str)
        console.log(str)
        req.body = body
        next()
    })
})
//将自定义的中间件封装为模块
function bodyParser(req, res, next){}
module.exports = bodyParser
//导入自定义中间件模块
const body = require('bodyParser')
app.use(body)

五、使用 Express 写接口

5.1 创建基本服务器

const express = require('express')

const app = express()

//接口

app.listen(3000,() => {
    console.log('running  at http://localhost:3000');
})

5.2 创建 API 路由模块

const express = require('express')

const router = express.Router()

//接口

module.exports = router

=====================
//导入并注册路由模块
const router = require('router')

app.use('/api',router)

5.3编写 GET 请求

const express = require('express')

const router = express.Router()

router.get('/get', (req, res) => {
    const query = req.query
    res.send({
        status: 0,//0表示成功,1表示失败
        msg: 'GET 请求成功',
        data: query
    })
})

module.exports = router

5.4编写 POST 接口

router.post('/post', (req, res) => {
    const body = req.body
    res.send({
        status: 0,
        msg: 'POST 请求成功',
        data: body
    })
})

===============
//获取 body 数据必须在导入路由之前,配置解析表单数据的中间件
app.use(express.urlencoded({ extended: false }))

//引入并注册路由模块
const router = require('router')

app.use('/api',router)

六、接口的跨域问题

通过 CORS 解决跨域

什么是 CORS ?

CORS (Cross-Origin Resource Sharing, 跨语资源共享)

由一系列 HTTP 响应头组成,这些 HTTP 响应头决定浏览器是否阻止前端 JS 代码跨域获取资源。(浏览器默认阻止)

CORS 有兼容性,只有支持 XMLHttpRequest Level2 的浏览器,才能正常访问开启了 CORS 的服务端接口

cors 是 Express 的第三方中间件,通过安装和配置,可以很方便的解决跨域问题。

//安装
npm i cors
//导入
const cors = require('cors')
//调用(一定要在路由之前配置 cors)
app.use(cors())
CORS 响应头部 Access-Control-Allow-Origin
// origin 的值指定了允许访问该资源的外域 URL
Access-Control-Allow-Origin: <origin> | *
    
//只允许来自 http://baidu.com 的请求(*代表所有)
res.setHeader('Access-Control-Allow-Origin', 'https://baidu.com')
CORS 响应头部 Access-Control-Allow-Header

默认情况下,CORS 仅支持客户端向服务器发送如下 9 个请求头:

AcceptAccept-LanguageContent-LanguageDPRDownlinkSave-DataViewport-WidthWidthContent-Type(值只限于 text/plainmultipart/form-dataapplication/x-www-form-urlencoded 三者之一)

如果客户端向服务器发送了额外的请求头信息,则需要在服务器端,通过 Access-Control-Allow-Header 对额外的请求头进行声明,否则这次的请求会失败!

res.setHeader('Access-Control-Allow-Header', 'Content-Type, X-Custom-Header')
CORS 响应头部 Access-Control-Allow-Methods

如果客户端希望通过 PUT、DELETE 等方式请求服务器的资源

//只允许 POST GET DELETE HEAD 请求方法
res.setHeader('Access-Control-Allow-Methods', 'POST, GET, DELETE, HEAD')

//允许所有的 HTTP 请求方法
res.setHeader('Access-Control-Allow-Methods', '*')
CORS 请求的分类

根据请求方式和请求头的不同,分为两大类:

  • 简单请求
    1. 请求的方式为 GET,POST,HEAD 三者之一
    2. HTTP 头部信息不能超过以下几种字段:无自定义头部字段AcceptAccept-LanguageContent-LanguageDPRDownlinkSave-DataViewport-WidthWidthContent-Type(值只限于 text/plainmultipart/form-dataapplication/x-www-form-urlencoded 三者之一)
  • 预检请求
    1. 请求方式为 GET、POST、HEAD 之外的请求 Method 类型
    2. 请求头中包含自定义头部字段
    3. 向服务器发送了 application/json 格式的数据

在浏览器与服务器正式通信之前,浏览器会发送 OPTION 请求进行预检,以获知服务器是否允许该实际请求,OPTION 被称为“预检请求”

简单请求与预检请求之间的区别
  • 简单请求的特点:客户端与服务器之间只会发送一次请求。
  • 预检请求的特点:客户端与服务器之间会发送两次请求。

通过 JSONP 解决跨域

浏览器端通过

特点:
  • JSONP 不属于真正的 Ajax 请求,因为它没有使用 XMLHttpRequest 这个对象。
  • JSONP 仅支持 GET 请求,不支持 POST、PUT、DELETE 等
注意事项:

如果已经配置了 CORS 跨域资源共享,防止冲突,必须在配置 CORS 中间件之前声明 JSONP 的接口。否则 JSONP 接口会被处理成开启了的 CORS 的接口,示例:

//优先创建 JSONP 接口
app.get('/api/jsonp', (req, res) => { })

app.use(cors())
//开启了 CORS 的接口
app.get('/api/get', (req, res) => { })
实现 JSONP 接口的步骤
  • 获取客户端发送过来的回调函数的名字
  • 得到要通过 JSONP 形式发送给客户端的数据
  • 根据前两步得到的数据,拼接出一个函数调用的字符串
  • 把上一步拼接得到的字符串,响应给客户端的
app.get('/api/jsonp', (req, res) => {
    const funcName = req.query.callback
    
    const data = { name: 'zs', age: 22}
    
    const scriptStr = `${funcName}(${JSON.stringify(data)})`
    
    res.send(scripStr)
})

S 跨域资源共享,防止冲突,必须在配置 CORS 中间件之前声明 JSONP 的接口。否则 JSONP 接口会被处理成开启了的 CORS 的接口,示例:

//优先创建 JSONP 接口
app.get('/api/jsonp', (req, res) => { })

app.use(cors())
//开启了 CORS 的接口
app.get('/api/get', (req, res) => { })
实现 JSONP 接口的步骤
  • 获取客户端发送过来的回调函数的名字
  • 得到要通过 JSONP 形式发送给客户端的数据
  • 根据前两步得到的数据,拼接出一个函数调用的字符串
  • 把上一步拼接得到的字符串,响应给客户端的
app.get('/api/jsonp', (req, res) => {
    const funcName = req.query.callback
    
    const data = { name: 'zs', age: 22}
    
    const scriptStr = `${funcName}(${JSON.stringify(data)})`
    
    res.send(scripStr)
})
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

前端mz小詹

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值