Express 快速入门待完结

一、介绍

  • 官方概念:Express 是基于 Node.js 平台,快速、开放、极简的 Web 开发框架。

  • Express 是第三方的 npm 包。

  • http 内置模块复杂,开发效率低。Express 是基于内置的http模块进一步封装,能够极大的提高开发效率。(类似于浏览器中 Web API 和 jQuery 的关系。后者是基于前者进一步封装出来的)

Web 网站服务器:对外提供 Web 网页资源的服务器。
API 接口服务器:对外提供 API 接口的服务器。
使用 Express,可以方便、快速的创建 Web 网站服务器或 API 接口服务器。

二、初识 Express

1、安装

在项目所属目录,安装 npm i express@4.17.1

2、创建基本的Web服务器

// 1. 导入 express
const express = require('express')

// 2. 创建 web 服务器
const app = express()

// 3. 启动 web 服务器
app.listen(80, () => {
   // 端口号是80则路径可省略不写80
   console.log('express server running at http://127.0.0.1')
})

3、监听 GET 和 POST 请求 & 处理参数

(1)监听 GET 请求

通过 app.get() 方法

// req:请求对象
// res:响应对象
app.get('请求的URL', function(req, res) { /* 处理函数 */ })

(2)监听 POST 请求

通过 app.post() 方法

// req:请求对象
// res:响应对象
app.post('请求的URL', function(req, res) { /* 处理函数 */ })

(3)把内容响应给客户端

通过 res.send() 方法,把处理好的内容,发送给客户端

app.get('/user', (req, res) => { 
	res.send({ name: 'liming', sex: '男' })
})

app.post('/user', (req, res) => { 
	res.send('请求成功')
})

然后在 postman 输入 http://127.0.0.1/user 测试即可。

(4)获取 URL 携带的查询参数

通过 req.query 可以获取到客户端通过查询字符串的形式,发送到服务器的参数

app.get('/user', (req, res) => { 
    // 通过 req.query 可以获取到 客户端发来的查询参数
    // 默认 req.query 是个空对象
    // 地址 http://127.0.0.1/?name=liming&age=20
    res.send(req.query.name);  // liming
}

(5)获取 URL 携带的动态参数

通过req.params 对象,可以访问到URL中,通过:匹配到的动态参数:

// 这里的 :id 是个动态参数
app.get("/user/:id", (req, res) => {
  // 通过 req.params 可以获取到 通过 : 匹配到的动态参数
  // 默认 req.params 是个空对象
  // 地址 http://127.0.0.1/user/3
  res.send(req.params.id);  // 3
});

4、静态资源处理

  • express.static()

通过express.static(),可以非常方便地创建一个静态资源服务器。
例:
public 目录下的图片等对外开放访问。

app.use(express.static('public'))

通过以下访问静态资源:
localhost/images/1.jpg
localhost/css/style.css
(注:存放静态文件的目录名(此例的 public)不会出现在URL中)

  • 托管多个静态资源目录:

多次调用 express.static()

app.use(express.static('public'))
app.use(express.static('files'))

根据目录的添加顺序 查找所需文件。若找到相应文件,则不会在下个目录找。

  • 挂载路径前缀
app.use('/public', express.static('public'))

通过带有 public 的前缀地址 访问 public 目录的文件
localhost/public/images/1.jpg
localhost/public/css/style.css

5、使用 nodemon 监听并自动重启项目

修改了node 项目的代码,需要频繁的手动重新启动项目。
为了方便,我们使用 nodemon (https://www.npmjs.com/package/nodemon) 工具,能够监听项目文件的变动,当代码被修改后,nodemon会自动重启项目,方便开发和调试。

  • 安装nodemon
    npm i -g nodemon
  • 使用nodemon
    之前使用 node 文件名 现在使用 nodemon 文件名 来运行 node 项目。

三、Express 的路由

理解:路由就是映射关系。

Express 中,路由指 客户端的请求与服务器处理函数 之间的映射关系。
Express 中的路由,为3部分组成,分别是请求的类型、请求的URL地址、处理函数。

每当一个请求到达服务器后,先经过路由的匹配,只有匹配成功后,才会调用对应的处理函数。
在匹配时,会按照路由定义的先后顺序进行匹配,如果请求类型和请求的URL同时匹配成功,则Express 会将这次请求,转交给对应的 function 函数进行处理。

1、简单写法

格式:
app.方法(路径, 处理函数)

app.get('/user', (req, res) => { 
	res.send({ name: 'liming', sex: '男' })
})

app.post('/user', (req, res) => { 
	res.send('请求成功')
})

当然后期不会用此写法,因为路由多的情况下,会产生很多行代码,且不好维护。

2、模块化路由

为了方便,对路由进行模块化管理。最好不将路由直接挂载在 app 上,而是将 路由抽离为单独的模块。

  • 创建路由模块的 js 文件
    创建 router/user.js
//1. 导入 express
const express = require('express')

// 2. 创建路由对象
const router = express.Router()

// 3. 挂载具体的路由
router.get('/user/info', (req, res) => {
    res.send('男,20岁')
})
router.post('/user/save', (req, res) => {
    res.send('保存成功')
})

// 4. 暴露路由对象
module.exports = router
  • 注册路由模块
const express = require('express')
const app = express()

// 1. 导入路由模块
const userRouter = require('./router/user.js')

// 2. 注册路由模块
app.use(userRouter)


app.listen(80, () => {
    console.log('http://127.0.0.1')
})

注:上述我们用到 app.use(express.static('./public')) 托管静态资源 和 app.use(userRouter) 注册路由模块。所以:app.use() 的作用,是注册全局中间件。(关于中间件,下面会讲到)

  • 为路由模块添加前缀
// 2. 注册路由模块并添加统一访问前缀
app.use('/api', userRouter)

访问路径需加上 /api,如:
localhost/api/user/info


四、Express 中间件

1、中间件的概念

中间件(Middleware), 特指业务流程的中间处理环节。
(就像污水经过三层处理流程,才从起初的污水 变为 处理过的水。)

(1)Express 中间件的调用流程

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

在这里插入图片描述


Express的中间件,本质是一个function处理函数,Express 中间件的格式如下:

app.get('/', function(req, res, next) {
	next();
})

注:中间件函数的形参列表中,必须包含 next 参数。而路由处理函数中只包含reqres

(2)next 函数的作用

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

2、Express 中间件初体验

(1)定义中间件函数

// mw 是中间件缩写
const mw = function (req, res, next) {
    console.log('这是一个简单的中间件函数')
    // 注:在当前中间件的业务处理完毕后,必须调用next()函数,把流转关系转交给下一个中间件或路由
    next()
}

(2)全局生效的中间件

客户端发起的任何请求,到达服务器之后,都会触发的中间件,叫做全局生效的中间件。

通过调用app.use(中间件函数),即可定义一个全局生效的中间件, 如下:

// 定义中间件函数, mw 是中间件缩写
const mw = function (req, res, next) {
    console.log('这是一个简单的中间件函数')
    // 注:在当前中间件的业务处理完毕后,必须调用next()函数,把流转关系转交给下一个中间件或路由
    next()
}

// 将 mw 注册为全局生效的中间件
app.use(mw)

app.get('/user', (req, res) => {
	console.log('调用了 /user 这个路由')
    res.send('get a get')
})

当 url 为 http://localhost/user :先经 mw 中间件,再经 /user 路由
终端输出:

这是一个简单的中间件函数
调用了 /user 这个路由

定义全局中间件的 简化形式:

app.use((req, res, next) => {
    console.log('这是一个简单的中间件函数')
    next()
})

(3)中间件的作用

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

app.use((req, res, next) => {
    // 获取请求到达服务器的时间
    const time = Date.now()
    // 为 req 对象 挂在自定义属性,从而把时间共享给后面所有的路由
    req.startTime = time
    next()
})

app.get('/user', (req, res) => {
    res.send('get a get' + req.startTime)
})

(4)定义多个全局中间件

可以使用 app.use() 连续定义多个全局中间件。客户端请求到达服务器之后,会按照中间件定义的先后顺序依次进行调用
如下:

app.use((req, res, next) => {
    req.aa = "先调用第一个全局中间件"
    next()
})

app.use((req, res, next) => {
    req.b= "后调用第二个全局中间件"
    next()
})

(5)局部生效的中间件

不使用 app.use() 定义的中间件,叫做局部生效的中间件。

// 定义中间件函数
const mw1 = function (req, res, next) {
    console.log('调用了局部中间件')
    next()
}

// mw1中间件 作为app.get参数,只在当前路由/login生效
app.get('/login', mw1, (req, res) => {
    res.send('get a login')
})

app.get('/user', (req, res) => {
    res.send('get a user')
})

当 url 为 http://localhost/login 终端输出:调用了局部中间件
当 url 为 http://localhost/user 终端无输出

(6)定义多个局部中间件

// 按先后顺序依次进行调用局部中间件
// 以下两种写法等价
app.get('/login', mw1, mw2, (req, res) => { res.send('get a login') })
app.get('/login', [mw1, mw2], (req, res) => { res.send('get a login') })

(7)中间件的5个使用注意事项

  • 必须在路由之前注册中间件
  • 客户端发来的请求,可以连续调用多个中间件进行处理
  • 执行完中间件的业务代码后,必须调用 next()
  • 为了防止代码逻辑混乱,调用 next() 后不写额外代码
  • 连续调用多个中间件时,多个中间件之间,共享 req 和 res 对象

3、中间件的分类

Express 官方把常见的中间件用法,分了5大类,分别是:

  • 应用级别的中间件
  • 路由级别的中间件
  • 错误级别的中间件
  • Express 内置的中间件
  • 第三方的中间件

(1)应用级别的中间件

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

(2)路由级别的中间件

绑定到 express.Router() 实例上的中间件,叫做路由级别的中间件。它的用法和应用级别中间件没有任何区别,区别是应用级别中间件是绑定到 app 实例上,路由级别中间件绑定到 router 实例上。

	 var app = express()
	 var router = express.Router()
	 
	 // 路由级别的中间件
	 router.use((req, res, next) {
	 	console.log('???')
	 	next()
	 })
	 	
	 app.use('/', router)

(3)错误级别的中间件

错误级别中间件的作用:专门用来捕获整个项目中发生的异常错误,从而防止项目异常崩溃的问题。
格式: 错误级别中间件的 function 处理函数中,必须有4个形参,形参顺序从前到后,分别是(err, req, res, next)。

app.get('/', function (req, res) {
	// 抛出一个自定义的错误(在这里模拟项目出错)
	throw new Error('服务器内部发生了错误')
	res.send('get a get') // 将不执行
})

// 定义错误级别的中间件,会捕获整个项目中发生的错误,防止项目异常崩溃
app.use(function (err, req, res, next) {
	res.send('错误:' + err.message)  // 客户端接收到:错误:服务器内部发生了错误
})

注:错误级别的中间件,必须注册在所有路由之后。

(4)Express 内置的中间件

自Express 4.16.0版本开始,Express 内置了 3个 常用的中间件,极大的提高了 Express 项目的开发效率和体验:

  • express.static 快速托管静态资源的内置中间件,例如: HTML 文件、图片、CSS 样式等(无兼容性问题)(在前面讲到过)
  • express.json 解析 JSON 格式的请求体数据(有兼容性,仅在4.16.0+版本中可用)
  • express.urlencoded 解析 URL-encoded 格式的请求体数据(有兼容性,仅4.16.0+版本中可用)
// 配置解析 application/json 格式数据的内置中间件
app.use(express.json()
// 配置解析 application/x-www-form-urlencoded 格式数据的内置中间件
app.use(express.urlencoded({ extended: false }))

(5)第三方的中间件

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


学习编程是顺着互联网的发展潮流,是一件好事。新手如何学习编程?其实不难,不过在学习编程之前你得先了解你的目的是什么?这个很重要,因为目的决定你的发展方向、决定你的发展速度。
IT行业是什么工作做什么?IT行业的工作有:产品策划类、页面设计类、前端与移动、开发与测试、营销推广类、数据运营类、运营维护类、游戏相关类等,根据不同的分类下面有细分了不同的岗位。
女生学Java好就业吗?女生适合学Java编程吗?目前有不少女生学习Java开发,但要结合自身的情况,先了解自己适不适合去学习Java,不要盲目的选择不适合自己的Java培训班进行学习。只要肯下功夫钻研,多看、多想、多练
Can’t connect to local MySQL server through socket \'/var/lib/mysql/mysql.sock问题 1.进入mysql路径
oracle基本命令 一、登录操作 1.管理员登录 # 管理员登录 sqlplus / as sysdba 2.普通用户登录
一、背景 因为项目中需要通北京网络,所以需要连vpn,但是服务器有时候会断掉,所以写个shell脚本每五分钟去判断是否连接,于是就有下面的shell脚本。
BETWEEN 操作符选取介于两个值之间的数据范围内的值。这些值可以是数值、文本或者日期。
假如你已经使用过苹果开发者中心上架app,你肯定知道在苹果开发者中心的web界面,无法直接提交ipa文件,而是需要使用第三方工具,将ipa文件上传到构建版本,开...
下面的 SQL 语句指定了两个别名,一个是 name 列的别名,一个是 country 列的别名。**提示:**如果列名称包含空格,要求使用双引号或方括号:
在使用H5混合开发的app打包后,需要将ipa文件上传到appstore进行发布,就需要去苹果开发者中心进行发布。​
+----+--------------+---------------------------+-------+---------+
数组的声明并不是声明一个个单独的变量,比如 number0、number1、...、number99,而是声明一个数组变量,比如 numbers,然后使用 nu...
第一步:到appuploader官网下载辅助工具和iCloud驱动,使用前面创建的AppID登录。
如需删除表中的列,请使用下面的语法(请注意,某些数据库系统不允许这种在数据库表中删除列的方式):
前不久在制作win11pe,制作了一版,1.26GB,太大了,不满意,想再裁剪下,发现这次dism mount正常,commit或discard巨慢,以前都很快...
赛门铁克各个版本概览:https://knowledge.broadcom.com/external/article?legacyId=tech163829
实测Python 3.6.6用pip 21.3.1,再高就报错了,Python 3.10.7用pip 22.3.1是可以的
Broadcom Corporation (博通公司,股票代号AVGO)是全球领先的有线和无线通信半导体公司。其产品实现向家庭、 办公室和移动环境以及在这些环境...
发现个问题,server2016上安装了c4d这些版本,低版本的正常显示窗格,但红色圈出的高版本c4d打开后不显示窗格,
TAT:https://cloud.tencent.com/document/product/1340