• Express笔记


     1. 简介

    1.1 概念

    Express是一个简介灵活的node.js web应用框架,提供了一系列强大特性 帮助创建各种web应用,和丰富的工具。使用Express可以快速搭建一个完整功能的网站

    1.2 本质

    对之前的内置模块http进行了封装,使用更方便

    1.3 核心

    可以设置中间件来响应HTTP请求

    定义了路由表用于执行不同的HTTP请求动作

    1.4* 核心概念

    路由:根据不同url路径,调用后台不同的处理函数

    中间件:从请求开始到响应结束这个业务流程中的所有处理环节

    2. 安装使用

    2.1 express安装

    新建英文目录(目录名不以express开头)

    进入目录 初始化(地址栏输入打开cmd:npm init -y)

    在此处安装express(npm i express)

    2.2 使用

    新建js文件,编写代码

    执行代码: node .\demo.js

    1. //1. 导入express包
    2. const express = require("express");
    3. const path = require("path");
    4. //2. 实例化对象
    5. const app = express()
    6. //3. 处理请求
    7. app.get("/", function(req,res) {
    8. //end原生。发字符串,send什么都能发
    9. //res.sendFile(path.join(__dirname, "../file/news.html"))
    10. res.send("

      Hello

      "
      );
    11. })
    12. app.get("*", function(req,res) {
    13. res.send("其它路径!")
    14. })
    15. //4. 设置端口
    16. app.listen(3000, () => {console.log("访问地址:http://localhost:3000");})

    2.3 express集成nodemon

    1. 修改package.json的script脚本

            "dev": "nodemon src/index.js"

    2. 使用 npm run dev 执行代码

    2.4 区别(对比http创建web服务)

    不需要处理乱码问题

    不需要判断请求方式

    2.5 res.send()与res.end()

    end:原生http模块方法 express也支持。只能传字符串,不能处理乱码问题

    send:express中封装的方法。可以传任何类型,可以处理乱码问题

    3. Express路由

    3.1 概念

    根据不同url路径,调用后台不同的处理函数

    3.2 语法

    app.请求方式(访问路径, 处理函数)

            请求方式:get,post,put,delete

            访问路径:也可以正则( * 通配符)

            处理函数:匿名函数,匹配未列举的路径

    3.3 注意事项

    匹配顺序:从上到下 ↓

    请求方式和访问路径同时匹配成功,express会将请求转移到对应的处理函数处理

    4. 跨域请求问题

    从一个请求跳到另外一个请求,协议/域名/端口 只要有一个不同,即为跨域请求

    解决: res.setHeader('Access-Control-Allow-Origin','*') //允许跨域请求

    5. Express增删改查

    5.1 查询 app.get()

    查询所有 http://localhost:3000/users            后台: /users 直接返回数组

    查询一个 http://localhost:3000/users?id=1   后台: /users 通过req.query获取参数

    查询一个 http://localhost:3000/users/1         后台 /users/:id 通过req.params获取参数

    5.2 新增 app.post()

    前台

    http://localhost:3000/users application/json {"name":"赵六","age":26}

    后台

            获取请求体数据

            设置新增用户的id

            把新增的用户添加到数组

    5.3 修改 app.put()

    前台

    http://localhost:3000/users/1 application/json {"name":"赵六","age":26}

    后台

            获取要修改元素的id

            获取请求体数据

            查找id相同的元素进行修改

    5.4 删除 app.delete()

    前台

    http://localhost:3000/users/1

    后台

            获取要修改元素的id

            查找id相同的元素进行删除

    🟢 代码

    1. //1. 引入模块
    2. const express = require("express");
    3. //2. 创建server服务
    4. const app = express();
    5. const db = [
    6. {id:1, name:"张三", age:23},
    7. {id:2, name:"李四", age:24},
    8. ]
    9. //3. 处理请求
    10. //查全部
    11. app.get("/users", function(req, res) {
    12. //解决跨域请求
    13. res.setHeader("Access-Control-Allow-Origin", "*")
    14. res.send(db);
    15. })
    16. //查一个query
    17. app.get("/users", function(req, res) {
    18. //解决跨域请求
    19. res.setHeader("Access-Control-Allow-Origin", "*")
    20. //获取id
    21. db.forEach(item => {
    22. if (item.id == req.query.id){
    23. res.send(item);
    24. }
    25. });
    26. })
    27. //查一个params
    28. app.get("/users/:id", function(req, res) {
    29. //解决跨域请求
    30. res.setHeader("Access-Control-Allow-Origin", "*")
    31. //获取id
    32. db.forEach(item => {
    33. if(item.id == req.params.id){
    34. res.send(item);
    35. }
    36. })
    37. })
    38. //增
    39. app.post("/users", function(req, res) {
    40. //解决跨域请求
    41. res.setHeader("Access-Control-Allow-Origin", "*")
    42. let postData = "";
    43. req.on("data", data => postData += data)
    44. req.on("end", () => {
    45. let item = JSON.parse(postData);
    46. item.id = db.length + 1;
    47. db.push(item);
    48. res.send(db)
    49. })
    50. })
    51. //删
    52. app.delete("/users/:id", function(req,res) {
    53. //解决跨域请求
    54. res.setHeader("Access-Control-Allow-Origin", "*")
    55. db.forEach((item, index) => {
    56. if (item.id == req.params.id) {
    57. db.splice(index, 1)
    58. }
    59. })
    60. res.send(db)
    61. })
    62. //改
    63. app.put("/users/:id", function(req, res) {
    64. //解决跨域请求
    65. res.setHeader("Access-Control-Allow-Origin", "*")
    66. let getData = "";
    67. req.on("data", data => getData += data)
    68. req.on("end", () => {
    69. db.forEach(item => {
    70. let user = JSON.parse(getData); //??
    71. if (item.id == req.params.id) {
    72. item.name = user.name;
    73. item.age = user.age;
    74. }
    75. })
    76. res.send(db)
    77. })
    78. })
    79. //4. 设置端口
    80. app.listen(3000, () => { console.log(" http://localhost:3000"); })

    6. 中间件

    6.1 抽取及中间件简化

    中间件函数

    概念(middieWare)

            指业务流程的中间处理环节,本质是一个function处理函数

    调用流程

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

    分类

            按作用范围

                    局部生效中间件

                    全局生效中间件

            按级别分

                    express内置中间件

                    第三方 如:处理跨域请求

                            应用级中间件

                            路由级中间件

    注意

            中间件函数形参列表中必须含next参数,与路由处理函数不同(路由只包含req、res)

            next作用:实现多个中间件连续调用,把流转关系移交到下一个中间件 或 处理函数。(接力棒)

    6.2 中间件的范围划分

    局部生效中间件

    概念:只在某些访问路径中使用

    举例:getPostData中间件,只在新增与修改 的路由中生效,处理请求时要将中间件函数名写在参数中

    1. // 定义中间件函数m1
    2. const m1 = function(req,res,next){
    3. console.log('这是中间件m1');
    4. next() // 以next结尾
    5. }
    6. app.get("/index", m1, m2, function(req, res) {}) //连用中间件

    注意

            要在路由前注册中间件

            客户端发来的请求,可以连续调用多个中间件处理

            执行完中间件业务代码后,务必调用next()!

            防止代码逻辑混乱,以next()结尾

            连续调用多个中间件,共享req和res对象

    全局生效中间件

            概念:客户端发起的任何请求,到达服务器都会触发的中间件

            使用:使用app.use(中间件函数) 来定义全局中间件

    6.3 🟡中间件的级别划分

    内置中间件

    概念:由express官方提供,常见3个

    分类:

            express.json 解析json格式的请求体数据

            express.urlencoded 解析url-encode格式的请求体数据

            express.static 快速托管静态资源,可以帮助加载html文件、css样式、图片

    使用:

            app.use(express.json())

            app.use(express.urlencoded())

            app.use(express.static(path.join(__dirname,"../../file/")))

            引入内置中间件:注册托管静态资源的中间件 将在file文件夹下的静态进行托管

            效果: http://localhost:3000/new.html 可以直接加载

            index.html不必输入文件名 http://localhost:3000

    第三方中间件

            概念:第三方个人/团队开发,使用前需下载

            分类:cors() 处理跨域请求

    使用

            下载:npm i cors

            导包:const cors = require("cors")

            引入:app.use(cors())

    原理:每个响应中添加响应头

    应用级中间件

            概念:通过app.use() app.get() app.post()绑定到app实例上的

            注意:应用及中间件,可以全局可以局部,但都要绑定到app实例上

    路由级中间件

            概念:绑定到路由对象(express.router()创建的)中间件

            注意:路由级中间件用处不是很多,了解

    6.4 🟢模块化路由

    产生的原因

            1. 路由很多时,若全都卸载app入口文件,文件太大不好维护

            2. 项目中有不同类型的请求路径(用户相关,商品相关),都放在一个文件中不好维护

            3. 为了便于对路由进行模块化管理,express不建议将路由直接挂在在app上(建议将路由抽成单独模块)

    使用步骤

            1. 创建路由模块对应的js文件,放在routes文件夹下

            2. 调用express.router()创建路由器对象

            3. 向路由对象挂载具体路由

            4. 使用module.exports向外共享路由对象

            5. 在入口文件中使用app.use() 注册路由模块

    代码

            调用 http://localhost:3000/goods/list

    1. //导入express包
    2. const express = require("express");
    3. //实例化express对象
    4. const app = express()
    5. //导入路由模块
    6. const goodsRouter = require("../routes/goods");
    7. const usersRouter = require("../routes/users");
    8. //注册各个路由模块
    9. app.use("/goods", goodsRouter)
    10. app.use("/users", usersRouter)
    11. //设置监听端口
    12. app.listen(3000, () => {
    13. console.log("运行在: http://localhost:3000");
    14. })
    15. //导入express包
    16. const express = require("express");
    17. //使用express创建路由对象
    18. const router = express.Router()
    19. //向router挂在具体路由
    20. router.get("/list", function(req, res) {
    21. res.send("商品列表页")
    22. })
    23. router.get("/add", function(req, res) {
    24. res.send("商品添加页")
    25. })
    26. //导出路由对象(对外共享
    27. module.exports = router

    7. 数据库操作

    7.1 node连接数据库

    报错解决:

    alter user 'root'@'localhost' identified with mysql_native_password by '1234';

    Error: ER_NOT_SUPPORTED_AUTH_MODE: Client does not support authentication protocol requested by serv_畱䶒的博客-CSDN博客

    7.2 增删改查(数组实现,优化)

    1. //使用内置中间件处理请求体
    2. app.use(express.json());
    3. app.use(express.urlencoded());
    4. //使用第三方中间件处理跨域问题 --需下载安装
    5. app.use(cors());

    7.3 增删改查(数据库实现)

    思路

            1. 导包、创建连接、连接,三个步骤拷贝进去

            2. 在路由里 编写sql语句,执行并返回数据

    7.4 增删改查(提取数据库操作)

    目的:将数据库操作的代码单独放到一个文件中,便于后期代码维护

    做法

            1. 将数据库导包、创建连接、连接 代码剪切进一个js文件中

            2. 提供查询所有getAll 查询单个getById 增删改exec方法,方法里用promise封装

            3. 导出这三个方法

            4. 在后台代码中导入js文件,解构这三个方法并使用

    模块导出简写:

    1. //导出对象
    2. module.exports = {
    3. getAll: getAll,
    4. getById: getById,
    5. exec: exec,
    6. };
    7. //属性名和属性值相同,可简写为一个
    8. module.exports = {
    9. getAll,
    10. getById,
    11. exec,
    12. };
    13. //引入:const {getAll, exec}= require("./引入.js")

    8. ES7 语法糖(async_await)

    8.1 async 异步

    作用:将同步函数变成异步函数(返回promise对象),就可以使用then() 调用

    使用:

    1. //定义
    2. async function m1() {
    3. return 222;
    4. }
    5. //调用
    6. const p = m1()
    7. p.then((data) => {
    8. console.log(data); //222
    9. });

    8.2 await 等待

    作用:await可以等待后面的 promise对象执行后,拿到返回的结果

    注意:

            1. await不能单独使用,必须跟async连用,存在于异步函数中

            2. await后面必须跟一个promise对象,如果不是promise对象,自动转成promise对象

            3. await表达式,返回的是promise执行后的结果

    使用:

    1. //在外面套一个异步函数壳,或将外层函数添加async关键字改为异步
    2. async function fn() {
    3. async function m1() {
    4. return 222;
    5. }
    6. //await 等待m1返回的promise对象执行完后,拿到返回的值
    7. const res = await m1();
    8. console.log(res);
    9. }
    10. fn();

    8.3 增删改查await改写

    1. app.get("/users", async function(req, res) {
    2. let sql = 'select * from student';
    3. const data = await exec(sql);//执行SQL语句
    4. res.send(data);//发给浏览器
    5. })

    9. 错误处理机制

    1. throw "错误信息"
    2. try{
    3. //可能会出问题的代码
    4. } catch (err) {
    5. //出问题的处理方案
    6. }

    10. 🟢用户管理系统搭建

    10.1 思路

    1. 需求分析(系统框架、具体业务分析)

    2. 技术方案

            数据库设计

            设计接口

                    前台:发送请求的url,需要的参数

                    后台:返回数据的格式

    3. 技术实现(编写代码)

            先写后端(express脚手架快速生成)再写前端

    10.2 用户管理系统搭建

    1. 全局安装脚手架(项目生成器) npm i express-generator -g

    2. 创建项目文件夹,执行命令生成后端目录  express --no-view backEnd

            在目录下创建一个`backEnd`的目录, 作为后端项目的目录

            `--no-view`: 创建一个数据服务, 不提供页面服务

    3. 在后端目录安装相关依赖(cd backEndnpm i

            进入backEnd目录, 执行命令, 根据`package.json`中的依赖项, 安装项目所有的依赖

    4. 启动项目

            1)npm i nodemon -D

                    将nodemon作为开发时的依赖安装(会在package.json中, 生成devDependencies)

            2)修改启动脚本: package.json>"scripts">"start": "nodemon ./bin/www"

            3)安装mysql和cors包(npm i mysqlnpm i cors

                    安装好后会自动添加在package.json

            4)执行 npm run start 可以在3000端口看到 express脚手架欢迎页面

            5. 导入数据库操作模块

                    将写好的db文件夹拷贝到backEnd

                    在routes/users.js中导入模块

    1. //1. 导入mysql包
    2. const mysql = require("mysql");
    3. //2. 创建连接,数据库参数可以放到配置文件中(config/index.js)
    4. var con = mysql.createConnection({
    5. host: "localhost",
    6. port: 3306,
    7. user: "root",
    8. password: "1234",
    9. database: "db01",
    10. timezone: "SYSTEM",
    11. });
    12. //3. 连接数据库
    13. con.connect();
    14. //4. 查询所有(获取查询的数据)
    15. function getAll(sql) {
    16. return new Promise((resolve, reject) => {
    17. //执行sql语句
    18. con.query(sql, (err, data) => {
    19. //有错,把失败的数据带回去
    20. if (err) reject(err);
    21. //将获取到的数据带回去
    22. resolve(data);
    23. });
    24. });
    25. }
    26. //5. 查询一个(获取查询的数据)
    27. function getById(sql) {
    28. return new Promise((resolve, reject) => {
    29. //执行sql语句
    30. con.query(sql, (err, data) => {
    31. //有错,把失败的数据带回去
    32. if (err) reject(err);
    33. //将获取到的数据带回去
    34. //易报错!
    35. resolve(data[0] ? data[0] : null);
    36. });
    37. });
    38. }
    39. //6. 管理(增删改)
    40. function exec(sql) {
    41. return new Promise((resolve, reject) => {
    42. //执行sql语句
    43. con.query(sql, (err, data) => {
    44. //有错,把失败的数据带回去
    45. if (err) reject(err);
    46. //将获取到的数据带回去
    47. resolve(data);
    48. });
    49. });
    50. }
    51. //导出方法
    52. /* module.exports = {
    53. getAll:getAll,
    54. getById:getById,
    55. exec:exec
    56. } */
    57. //属性名和属性值相同,可简写为一个
    58. module.exports = {
    59. getAll,
    60. getById,
    61. exec,
    62. };

    6. 导入跨域请求模块

            在app.js中导入 const cors = require("cors");

            在app.js中注册 app.use(cors()) 注册路由之前

    10.3 后端编写流程

    1. 在用户【路由模块】users.js下编写代码

    2. 编写获取所有用户,使用get请求,调用getAll()方法,返回所有用户的数组(data)

    3. 编写获取一个用户,使用get请求,调用getById()方法,返回指定id的用户(data)

    4. 编写新增一个用户,使用post请求,调用exec()方法,返回新增的用户(data.insertId)

    5. 编写修改一个用户,使用put请求,调用exec()方法,返回新增的用户(无需data)

    6. 编写删除一个用户,使用delete请求,调用exec()方法,设置状态码,返回空字符串(无需data)

            es.status(204).send("")

    1. var express = require('express');
    2. //导入数据库操作模块
    3. const {getAll, getById, exec} = require("../db");
    4. var router = express.Router();
    5. // 获取所有用户
    6. router.get('/', async function(req, res, next) {
    7. let sql = "select * from users";
    8. res.send(await getAll(sql))
    9. });
    10. // 获取一个用户
    11. router.get('/:id', async function(req, res, next) {
    12. let sql = `select * from users where ${req.params.id} = id`;
    13. res.send(await getById(sql))
    14. });
    15. // 添加一个用户
    16. router.post('/', async function(req, res, next) {
    17. let {name, age} = req.body;
    18. let sql = `insert into users (name, age) values("${name}", ${age}) `;
    19. let data = await exec(sql)
    20. res.send({
    21. id: data.insertId,
    22. name,
    23. age
    24. })
    25. });
    26. // 修改一个用户
    27. router.put('/:id', async function(req, res, next) {
    28. let {name, age} = req.body;
    29. let sql = `update users set name = "${name}", age = ${age} where id = ${req.params.id}`;
    30. res.send({
    31. id: req.params.id,
    32. name,
    33. age,
    34. data: await exec(sql),
    35. })
    36. });
    37. // 删除一个用户
    38. router.delete('/:id', async function(req, res, next) {
    39. let sql = `delete from users where id = ${req.params.id} `;
    40. let data = await exec(sql)
    41. //设置状态码
    42. res.status(204).send("")
    43. });
    44. module.exports = router;

    10.4 前端编写流程

    查询(显示所有用户)

    1. 新建 fontEnd 文件夹

    2. 新建3个html页面

    3. 编写list.html页面的骨架

    4. 编写基本样式base.css

    5. 编写list页面样式style.css

    6. 引入jquery,发送ajax请求(已经引入了cors处理跨域请求)

    7. 遍历获取到的数据,动态拼接 tr 元素,追加到table下面

    1. <body>
    2. <h1>用户列表h1>
    3. <div class="container">
    4. <a href="./add.html" class="add-btn">添加a>
    5. <table class="user-list">
    6. <tr>
    7. <th>idth>
    8. <th>nameth>
    9. <th>ageth>
    10. <th>操作th>
    11. tr>
    12. table>
    13. div>
    14. <script>
    15. $.ajax({
    16. url: "http://localhost:3000/users",
    17. type: "GET",
    18. success: function (data) {
    19. console.log(data); //js数组
    20. //遍历数组
    21. data.forEach((item) => {
    22. // console.log(item);
    23. const tr = `
    24. ${item.id}
    25. ${item.name}
    26. ${item.age}
    27. `;
    28. $(".user-list").append(tr);
    29. });
    30. },
    31. });
    32. //删除
    33. function delUser(id) {
    34. console.log(id);
    35. $.ajax({
    36. url: "http://localhost:3000/users/" + id,
    37. type: "DELETE",
    38. success: function (res) {
    39. console.log(res);
    40. alert("删除成功");
    41. location.reload();
    42. },
    43. });
    44. }
    45. script>
    46. body>

    新增

    1. 编写add.html页面骨架

    2. 在style.css中,添加add.html的样式

    3. 监听add按钮的点击,在点击事件中获取用户输入的姓名与年龄进行判断

    4. 输入为空,直接返回。否则用ajax发送post请求,提交到后台,修改数据库中数据

    5. 修改成功,弹出提示,跳转回list.html页

    1. <body>
    2. <h1>添加用户h1>
    3. <div class="container">
    4. <ul>
    5. <li>
    6. <label for="userName">用户名label>
    7. <input type="text" name="userName" id="userName" placeholder="请输入用户名" />
    8. li>
    9. <li>
    10. <label for="userAge">年龄label>
    11. <input type="text" name="userAge" id="userAge" placeholder="请输入年龄" />
    12. li>
    13. ul>
    14. <button class="submit-btn">提交button>
    15. form>
    16. <script>
    17. $(".submit-btn").click(function () {
    18. const name = $("#userName").val().trim()
    19. const age = $("#userAge").val().trim()
    20. //判断
    21. if (name == "" || age == "") {
    22. alert("用户名或年龄不能为空")
    23. return
    24. }
    25. //发送异步请求
    26. $.ajax({
    27. url: "http://localhost:3000/users",
    28. type: "POST",
    29. data: { name, age },
    30. success: function (res) {
    31. alert("添加成功")
    32. location.href = "./list.html";
    33. },
    34. });
    35. });
    36. script>
    37. body>

    修改

    1. 编写edit.html骨架

    2. 在list.html中为 修改a标签 的href拼接id属性

       `href="./edit.html?id=${item.id}"`

             动态绑定

    3. 在edit.html页获取路径携带的id值

        window.location.search.replace('?','').split('=')[1]

    4. 用ajax发送get请求,把根据id查找到的值回显到表单中

       $('#age').val(res.age)

    5. 为按钮添加点击事件,获取用户输入的名字年龄,为空直接返回,否则用ajax发送put请求,提交到后台 修改数据库中数据

    6. 修改成功,弹出提示,跳转到list.html页面

    1. <body>
    2. <h1>修改用户h1>
    3. <div class="container">
    4. <ul>
    5. <li>
    6. <label for="userName">用户名label>
    7. <input type="text" name="userName" id="userName" placeholder="请输入用户名" />
    8. li>
    9. <li>
    10. <label for="userAge">年龄label>
    11. <input type="text" name="userAge" id="userAge" placeholder="请输入年龄" />
    12. li>
    13. ul>
    14. <button class="submit-btn">修改button>
    15. form>
    16. <script>
    17. const id = location.search.replace("?", "").split("=")[1]
    18. $.ajax({
    19. url: "http://localhost:3000/users/" + id,
    20. type: "GET",
    21. success: function (res) {
    22. console.log(res);
    23. //回显数据(设置input里的值)
    24. // alert("查找成功")
    25. $("#userName").val(res.name)
    26. $("#userAge").val(res.age)
    27. },
    28. });
    29. $(".submit-btn").click(function () {
    30. const name = $("#userName").val().trim()
    31. const age = $("#userAge").val().trim()
    32. //判断
    33. if (name == "" || age == "") {
    34. alert("用户名或年龄不能为空")
    35. return
    36. }
    37. //发送异步请求
    38. $.ajax({
    39. url: "http://localhost:3000/users/" + id,
    40. type: "PUT",
    41. data: { name, age },
    42. success: function (res) {
    43. console.log(res);
    44. //回显数据(设置input里的值)
    45. alert("修改成功")
    46. location.href = "./list.html";
    47. },
    48. });
    49. });
    50. script>
    51. body>

    删除

    1. 在list.html页 为删除a标签 添加点击事件,传入id

    2. 弹出确认框

    3. 用ajax发送delete请求,删除数据库中数据

    4. 删除成功,刷新

  • 相关阅读:
    设计模式之责任链模式
    【手撕AHB-APB Bridge】~ AMBA总线 之 APB
    配电房智能化改造
    一份vue面试知识点梳理清单
    python小项目之利用pygame实现代码雨动画效果(附源码 可供学习)
    【羊了个羊】什么!第二关难如上青天,能不能简单版??
    Spark之Container killed on request.Exit code is 137
    LearnOpenGL 及 ShaderToy 的 CMake 构建框架
    技术分享 | app自动化测试(Android)–显式等待机制
    leetcode 94.二叉树的中序遍历(非递归和递归遍历)
  • 原文地址:https://blog.csdn.net/weixin_43895819/article/details/126909109