目录
跳转链接 : NodeJs_01 _ 学习笔记
跳转链接 : NodeJs_02 _ 学习笔记
跳转链接 : NodeJs_04 _ 学习笔记
目标
- 能够编写运行 express 静态服务器
- 能够在 express 中获取 get 和 url 路由参数
- 能够 模块化拆分 请求路由
- 能够 自定义中间件
- 引入第3方中间件为 express 使用
- 使用中间件完成数据验证
- 使用第3方中间件设置好 cookie 和 session
一、Express
1.1、介绍
网址:Express - 基于 Node.js 平台的 web 应用开发框架 - Express 中文文档 | Express 中文网
Express 是基于 Node.js 平台,快速、开放、极简的 Web 开发框架。搭建 web 服务器
Express 的本质:就是一个 npm 上的第三方包,提供了快速创建 Web 服务器的便捷方法。
使用 Express 开发框架可以非常方便、快速的创建 Web 网站的服务器或 API 接口的服务器
Express 它是一个轻量级的 web 服务器框架 , 搭建 web 简单易上手 .
1.2、基本使用
1.2.1、安装
在项目目录中,打开 cmd 命令窗口,执行如下命令
( 1 ) npm init -y
( 2 ) npm i -S express => "express": "^4.17.1"
1.2.2、创建 web 服务
-
引入模块
-
实例化 应用对象
-
监听主机和端口
-
定义路由 get / post / put / delete
// 1. 引入 express 模块 const express = require('express') // 2. 创建 web 服务 , 实例化服务对象 const app = express( ) // 监听 get 请求 // req 请求对象 // res 响应对象 app.get ( '请求 URI' , ( req, res ) => { // 向客户端响应数据 res.send({ id:1, name:'张三' }) }) // 启动服务 app.listen( 8080, ()=>{ } )
// 引入 express 没有对于原有的http模块中的功能减少,而是在原有的功能上增强了
const express = require('express')
// 实例化服务对象
const app = express()
// 定义路由
// 当前以get的方式来进行方式,url路径为 / (根)
app.get('/', (req, res) => {
// res.end('hello')
// res.end('你好世界')
// res.send('你好世界')
// res.send({ id: 1 })
// res.send(1) => 数字不行 , 会报错
// res.send(1 + '')
res.send(req.ip) // 功能增强
})
// 监听端口
app.listen(3000, '0.0.0.0', () => console.log('express start'))
1.2.3、获取 URL 中 get 查询参数
通过 req.query 对象 ,可以访问到客户端通过查询字符串的形式发送到服务器的参数
app.get ( '/?id=1', ( req, res ) => {
console.log( req.query )
})
1.2.4、URL 中 动态参数
通过 req.params 对象,可以访问到 URL 中 动态参数
// 必填的
/xxx/:名称 => req.params.名称
// 可选
/xxx/:名称?
/:id 动态路由参数,: (冒号)它是一个占位符号
// 路由匹配从上向下一直接匹配,【默认】是匹配成功后就停止向下匹配
// 把精确的路由规则写在上面,模糊写在下面
// 动态路由参数
// 根据新闻id查看当前的新闻,传统方案 /news?id=100
// 传统方案:1.id字段暴露在地址栏中 , 有不安全的可能性
// 2.地址栏不优雅,不利用seo,3.参数可以随意添加或减少,一般传统方案用于搜索
// 动态路由
// 1.字段不会暴露,2.有利于seo, 3.参数必须提前在规则中定义好不然不能用
// /:id 动态路由参数,它是一个占位
// /goods/100 => 404
// ? 可选参数
// 路由匹配从上向下一直接匹配,【默认】是匹配成功后就停止向下匹配
// 把精确的路由规则写在上面,模糊写在下面
app.get('/goods/666', (req, res) => { // 精确的路由规则
res.send('商品详情 - 特别处理')
})
app.get('/goods/:id/:goodname?', (req, res) => { // 模糊的路由规则
// 商品详情
let params = req.params
res.send('商品详情 -- ' + params.id)
})
1.2.5、静态资源管理
express 提供了一个非常好用的方法,叫做 express.static( ) ,通过此方法,可以非常方便地 创建一个 静态 web 资源服务器
// 中间件,内置中间件 拥有一个 静态资源服务器
app.use(express.static('路径'))
路由定义 :
get 获取 资源 200
post 添加 资源 201
put / patch 修改 资源 put 全量修改 , patch 增量修改 id name age 201 / 202
delete 删除 返回状态码 204 204状态码,没有响应体 ( 就是所谓的 JSON 数据得不到 , 因为你都删除了 , 所以没必要有响应体 , 去浪费额外的带宽 )
1.3、路由
1.3.1、概述
路由 在生活中如拨打服务电话时,按 数字 几 能处理什么样的处理,它就是类似于 按键 与 服务 之间的 映射 关系 。
在 Express 中,路由 指的就是 客户端 发起的 请求 与 服务器端 处理方法之间的 映射关系 。
服务器端 路由 的切换 , 会引起 浏览器刷新 , 重新加载 , 并且返回对应的 http 状态码
1.3.2、定义路由
express 中的路由分3部份组件,分别是 请求类型、请求 url 和 对应的处理函数。
当一个客户端请求到达服务端之后,先经过路由规则匹配,只有匹配成功之后,才会调用对应的处理函数。在匹配时,会 按照路由的顺序进行匹配,如果 请求类型 和 请求的 URL 同时匹配成功,则 Express 会将这次请求,转交给对应的 函数 进行处理。
app.<get / post / put / delete / all / use>(uri, ( req, res ) => { } )
定义 get 路由 :
// path 模块用于处理文件和目录(文件夹)的路径
const path = require('path')
// fs 模块提供了用于与文件进行交互相关方法
const fs = require('fs')
// fs.promise 它是返回一个 fs 对象, 但是这后面的方法全部返回 promise
const pfs = fs.promises
// 路径要以引入到最终执行的文件位置为主
const filepath = path.resolve('./data/user.json')
/**
* 读取用户数据
* @returns promise
*/
const readFile = async () => {
// 使用fs读取用户的json数据,返回的是一个promise对象,使用async/await进行同步化操作
let str = await pfs.readFile(filepath, 'utf-8')
// 得到的是一个json字符串,转为json对象
return JSON.parse(str)
}
// 导出
module.exports = {
readFile
}
定义 post 路由 :
定义 put 路由 :
// 查看单个用户信息
app.get('/api/users/:uid', async (req, res) => {
// 读取用户数据
let userList = await fs.readFile()
// 获取用户 id
let uid = req.params.uid
// 使用数组方法
let user = userList.find(item => item.id == uid)
res.send({
code: 0,
msg: 'ok',
data: user
})
})
// 修改
app.put('/api/users/:uid', async (req, res) => {
// 修改的数据
let user = {
"name": "李四 --" + Math.random()
}
// 读取用户数据
let userList = await fs.readFile()
// 获取用户 id
let uid = req.params.uid
// 使用数组方法
let index = userList.findIndex(item => item.id == uid)
userList[index] = { ...userList[index], ...user }
// 写入到文件中
fs.writeFile(userList)
res.send({
code: 0,
msg: 'ok',
data: userList[index]
})
})
定义 delete 路由 :
// 删除
app.delete('/api/users/:uid', async (req, res) => {
// 读取用户数据
let userList = await fs.readFile()
// 获取用户 id
let uid = req.params.uid
// 使用数组方法
let index = userList.findIndex(item => item.id == uid)
userList.splice(index, 1)
// 写入到文件中
fs.writeFile(userList)
// 注意 : 204 没有响应体
res.status(204).send('')
})
all 和 use 的 定义 :
// 接受所有的http请求动作 all / use
// all 一般不推荐使用
// all 路径匹配是 精确匹配,只有和参数1中的路径一致才进行匹配
app.all('/api', (req, res) => {
res.send(`Api接口文档---` + req.method)
})
// 模糊匹配,参数1的意思,请求pathname以/api开头的则匹配成功
// use 正是因为这样的特性,所以可以用来 拦截请求(中间件)
app.use('/api', (req, res) => {
res.send(`Api接口文档---` + req.method)
})
// 可以用 use / all 来实现访问页面404处理
// 上面所有的请求如果都没有匹配成功,则 * 全部匹配成功
app.all('*', (req, res) => {
res.send('404')
})
// 参数1不写,表示所有的请求动作和路径我都能匹配成功
app.use((req,res)=>{
res.send('404!')
})
1.3.3、模块化路由
在开发项目时,如果将所有的路由规则都挂载到入口文件中,程序编写和维护都变的更加困难。所以 express 为了 路由 的 模块化管理功能 ,通过 express.Router( ) 方法 创建 路由模块化 处理程序,可以将不同业务需求分开到不同的模块中,从而便于 代码的 维护 和 项目扩展 。
路由模块化处理可以分为以下步骤来完成
- 创建独立 js 空白文件 ( 最后是统一放在一个目录下 )
- 在 js 中使用 express.Router( ) 方法创建 路由模块对象
- 使用 路由对象 完成路由规则的对应的业务编写
- 使用模块化导出 ( module.exports = router )
- 在主入口文件中能过 app.use 方法来注册定义的 路由模块
自动化实现路由导入 :
// 得到路由对象
const router = require('express').Router()
// path 模块用于处理文件和目录(文件夹)的路径
const path = require('path')
// 引入对应的方法, fs 模块提供了用于与文件进行交互相关方法
const fs = require('fs')
// 路由目录
let routerDir = path.resolve('./router/routes')
// 读取当前目录下面的文件列表
let dirs = fs.readdirSync(routerDir, 'utf-8')
// 把目录下面的文件时行路径的映射
dirs = dirs.map(item => './routes/' + item)
// 循环来导入到路由中
dirs.forEach(item => {
router.use('/api', require(item))
})
/* // 用户
router.use('/api', require('./routes/userRouter'))
// 新闻
router.use('/api', require('./routes/newsRouter')) */
module.exports = router
路由模块化 :
1.4、中间件
1.4.1、中间件理解
中间件( 拦截器 )可以理解为 业务流程的中间处理环节 。( 允许我们可以进行切片编程 )
如生活中一般吃炒青菜,大约分为如下几步骤 :
express中当一个请求到达的服务器之后,可以在给客户响应之前连续调用多个中间件,来对本次请求和返回响应数据进行处理。(middleware)
1.4.2、中间件分类
中间件可以分类可分如下几类
- 内置中间件 也就是express本身自带无带npm安装
- 第三方中间件
非 Express 官方内置的,而是由第三方开发出来的中间件,叫做第三方中间件。在项目中可以通过 npm 进行安装第三方中间件并配置,从而提高项目的开发效率。例如 body-parser 此中间件可以很方便帮助我们获取到 post 提交过来的数据。
自定义中间件 开发者自己编写的
1.4.3、自定义中间件
自定义中间件,其本质就是定义一个 处理请求的函数,只是此函数中除了有 request 和 response 参数外还必须包含一个 next 参数,此参数作用让中间件能够让流程向下执行下去直到匹配到的路由中发送响应给客户端。也可以通过给 request 对象添加属性来进行中间件数据的向下传递
function mfn ( req, res, next ) {
// 中间件最后一定要执行此函数,否则程序无法向下执行下去
next( )
}
使用中间件来实现错误的统一处理,即 错误级别中间件
1.4.4、内置中间件
express 也提供了好用的内置中间件,如提供一个 静态资源管理 的中间件,通过此中间件就可以帮助为我们 快速搭建一个静态资源服务器
app.use ( express.static ( '托管目录地址' ) )
1.4.5、第三方中间件
express 搭建的 web 服务器中想要接受表单中的 post 数据可以通过第 3 方中间件帮助解析获取post 数据 body-parser
- 普通 post 接受
- 安装 $ => npm i -S body-parser
- 通过中间件调用 app.use ( body.urlencoded( { extended: false } ) ) ,在匹配的路由中通过 req.body 获数 post 中数据
创建 application/x-www-form-urlencoded 解析
- 在匹配的路由中通过 req.body 获数 post 中数据
- 文件上传
安装 $ => npm i -S multer
使用 :
// var upload = multer({ dest: 'www/uploads/' })
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'www/uploads/')
},
filename: function (req, file, cb) {
cb(null, Date.now() + '-' + file.originalname)
}
})
var upload = multer({ storage: storage })
router.post(路径,upload.single(表单项名称),(req,res)=>{})
router.post(路径,upload.array(表单项名称),(req,res)=>{})
// 获取数据
req.file/files
1.5、cookie 和 session
1.5.1、cookie
HTTP 是一个无状态协议,客户端每次发出请求时候,下一次请求得不到上一次请求的数据,我们如何将上一次请求和下一次请求的数据关联起来呢?如用户登录成功后,跳转到其他页面时候,其他的页面是如何知道该用户已经登录了呢?此时就可以使用到 cookie 中的值来判断用户是否登录,cookie 可以保持用户数据。
cookie它是一个由浏览器和服务器共同协作实现的(cookie是存储于浏览器中)。cookie分为如下几步实现:
1.5.2、session
cookie 操作很方便,但是使用 cookie 安全性不高,cookie 中的所有数据存储在 客户端 浏览器中,数据很容易被伪造;所以一些重要的数据就不能放在 cookie 当中了,并且 cookie 还有一个缺点就是不能存放太多的数据,一般浏览大约在 4 k 左右,为了解决这些问题,session 就产生了,session 中的数据保留在 服务端 的 。
把数据放到 cookie 中是不安全的,我们可以在 cookie 中存放一个 sessionId 值, 该 sessionId 会与服务器端之间会产生映射关系,如果 sessionId 被篡改的话,那么它就不会与服务器端数据之间产生映射,因此安全性就更好,并且 session 的有效期一般比较短,一般都是设置是 20 分钟左右,如果在 20 分钟内客户端与服务端没有产生交互,服务端就会将数据删除 。
express 中操作的 cookie 使用 cookie-seesion 模块 npm i -S cookie-session
const express = require('express');
const session = require('cookie-session');
const app = express();
app.use(session({
name: 'sessionId',
// 给 sessionid 加密的 key, 随便填写s
secret: 'afsfwefwlfjewlfewfef',
//maxAge: 20 * 60 * 1000 // 20分钟
}));
app.get('/, (req, res) => {
if(!req.session['view']){
req.session['view'] = 1;
}else{
req.session['view']++;
}
res.send(`欢迎您第 ${req.session['view']} 次访问!`);
})
app.listen(3000)
图例 :
跳转链接 : NodeJs_01 _ 学习笔记
跳转链接 : NodeJs_02 _ 学习笔记
跳转链接 : NodeJs_04 _ 学习笔记