Express是一个简介灵活的node.js web应用框架,提供了一系列强大特性 帮助创建各种web应用,和丰富的工具。使用Express可以快速搭建一个完整功能的网站
对之前的内置模块http进行了封装,使用更方便
可以设置中间件来响应HTTP请求
定义了路由表用于执行不同的HTTP请求动作
路由:根据不同url路径,调用后台不同的处理函数
中间件:从请求开始到响应结束这个业务流程中的所有处理环节
新建英文目录(目录名不以express开头)
进入目录 初始化(地址栏输入打开cmd:npm init -y)
在此处安装express(npm i express)
新建js文件,编写代码
执行代码: node .\demo.js
- //1. 导入express包
- const express = require("express");
- const path = require("path");
- //2. 实例化对象
- const app = express()
- //3. 处理请求
- app.get("/", function(req,res) {
- //end原生。发字符串,send什么都能发
- //res.sendFile(path.join(__dirname, "../file/news.html"))
- res.send("
Hello
"); - })
- app.get("*", function(req,res) {
- res.send("其它路径!")
- })
- //4. 设置端口
- app.listen(3000, () => {console.log("访问地址:http://localhost:3000");})
1. 修改package.json的script脚本
"dev": "nodemon src/index.js"
2. 使用 npm run dev 执行代码
不需要处理乱码问题
不需要判断请求方式
end:原生http模块方法 express也支持。只能传字符串,不能处理乱码问题
send:express中封装的方法。可以传任何类型,可以处理乱码问题
根据不同url路径,调用后台不同的处理函数
app.请求方式(访问路径, 处理函数)
请求方式:get,post,put,delete
访问路径:也可以正则( * 通配符)
处理函数:匿名函数,匹配未列举的路径
匹配顺序:从上到下 ↓
请求方式和访问路径同时匹配成功,express会将请求转移到对应的处理函数处理
从一个请求跳到另外一个请求,协议/域名/端口 只要有一个不同,即为跨域请求
解决: res.setHeader('Access-Control-Allow-Origin','*') //允许跨域请求
查询所有 http://localhost:3000/users 后台: /users 直接返回数组
查询一个 http://localhost:3000/users?id=1 后台: /users 通过req.query获取参数
查询一个 http://localhost:3000/users/1 后台 /users/:id 通过req.params获取参数
前台
http://localhost:3000/users application/json {"name":"赵六","age":26}
后台
获取请求体数据
设置新增用户的id
把新增的用户添加到数组
前台
http://localhost:3000/users/1 application/json {"name":"赵六","age":26}
后台
获取要修改元素的id
获取请求体数据
查找id相同的元素进行修改
前台
后台
获取要修改元素的id
查找id相同的元素进行删除
- //1. 引入模块
- const express = require("express");
- //2. 创建server服务
- const app = express();
-
- const db = [
- {id:1, name:"张三", age:23},
- {id:2, name:"李四", age:24},
- ]
- //3. 处理请求
- //查全部
- app.get("/users", function(req, res) {
- //解决跨域请求
- res.setHeader("Access-Control-Allow-Origin", "*")
- res.send(db);
- })
- //查一个query
- app.get("/users", function(req, res) {
- //解决跨域请求
- res.setHeader("Access-Control-Allow-Origin", "*")
- //获取id
- db.forEach(item => {
- if (item.id == req.query.id){
- res.send(item);
- }
- });
- })
- //查一个params
- app.get("/users/:id", function(req, res) {
- //解决跨域请求
- res.setHeader("Access-Control-Allow-Origin", "*")
- //获取id
- db.forEach(item => {
- if(item.id == req.params.id){
- res.send(item);
- }
- })
- })
-
- //增
- app.post("/users", function(req, res) {
- //解决跨域请求
- res.setHeader("Access-Control-Allow-Origin", "*")
- let postData = "";
- req.on("data", data => postData += data)
- req.on("end", () => {
- let item = JSON.parse(postData);
- item.id = db.length + 1;
- db.push(item);
- res.send(db)
- })
- })
- //删
- app.delete("/users/:id", function(req,res) {
- //解决跨域请求
- res.setHeader("Access-Control-Allow-Origin", "*")
- db.forEach((item, index) => {
- if (item.id == req.params.id) {
- db.splice(index, 1)
- }
- })
- res.send(db)
- })
- //改
- app.put("/users/:id", function(req, res) {
- //解决跨域请求
- res.setHeader("Access-Control-Allow-Origin", "*")
- let getData = "";
- req.on("data", data => getData += data)
- req.on("end", () => {
- db.forEach(item => {
- let user = JSON.parse(getData); //??
- if (item.id == req.params.id) {
- item.name = user.name;
- item.age = user.age;
- }
- })
- res.send(db)
- })
- })
- //4. 设置端口
- app.listen(3000, () => { console.log(" http://localhost:3000"); })
中间件函数
概念(middieWare)
指业务流程的中间处理环节,本质是一个function处理函数
调用流程
当一个请求到达express服务器之后,可以连续调用多个中间件,对请求进行预处理
分类
按作用范围
局部生效中间件
全局生效中间件
按级别分
express内置中间件
第三方 如:处理跨域请求
应用级中间件
路由级中间件
注意
中间件函数形参列表中必须含next参数,与路由处理函数不同(路由只包含req、res)
next作用:实现多个中间件连续调用,把流转关系移交到下一个中间件 或 处理函数。(接力棒)
局部生效中间件
概念:只在某些访问路径中使用
举例:getPostData中间件,只在新增与修改 的路由中生效,处理请求时要将中间件函数名写在参数中
- // 定义中间件函数m1
- const m1 = function(req,res,next){
- console.log('这是中间件m1');
- next() // 以next结尾
- }
- app.get("/index", m1, m2, function(req, res) {}) //连用中间件
注意
要在路由前注册中间件
客户端发来的请求,可以连续调用多个中间件处理
执行完中间件业务代码后,务必调用next()!
防止代码逻辑混乱,以next()结尾
连续调用多个中间件,共享req和res对象
全局生效中间件
概念:客户端发起的任何请求,到达服务器都会触发的中间件
使用:使用app.use(中间件函数) 来定义全局中间件
内置中间件
概念:由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()创建的)中间件
注意:路由级中间件用处不是很多,了解
产生的原因
1. 路由很多时,若全都卸载app入口文件,文件太大不好维护
2. 项目中有不同类型的请求路径(用户相关,商品相关),都放在一个文件中不好维护
3. 为了便于对路由进行模块化管理,express不建议将路由直接挂在在app上(建议将路由抽成单独模块)
使用步骤
1. 创建路由模块对应的js文件,放在routes文件夹下
2. 调用express.router()创建路由器对象
3. 向路由对象挂载具体路由
4. 使用module.exports向外共享路由对象
5. 在入口文件中使用app.use() 注册路由模块
代码
调用 http://localhost:3000/goods/list
- //导入express包
- const express = require("express");
-
- //实例化express对象
- const app = express()
-
- //导入路由模块
- const goodsRouter = require("../routes/goods");
- const usersRouter = require("../routes/users");
-
- //注册各个路由模块
- app.use("/goods", goodsRouter)
- app.use("/users", usersRouter)
-
- //设置监听端口
- app.listen(3000, () => {
- console.log("运行在: http://localhost:3000");
- })
- //导入express包
- const express = require("express");
-
- //使用express创建路由对象
- const router = express.Router()
-
- //向router挂在具体路由
- router.get("/list", function(req, res) {
- res.send("商品列表页")
- })
- router.get("/add", function(req, res) {
- res.send("商品添加页")
- })
-
- //导出路由对象(对外共享
- module.exports = router
报错解决:
alter user 'root'@'localhost' identified with mysql_native_password by '1234';
- //使用内置中间件处理请求体
- app.use(express.json());
- app.use(express.urlencoded());
- //使用第三方中间件处理跨域问题 --需下载安装
- app.use(cors());
思路
1. 导包、创建连接、连接,三个步骤拷贝进去
2. 在路由里 编写sql语句,执行并返回数据
目的:将数据库操作的代码单独放到一个文件中,便于后期代码维护
做法
1. 将数据库导包、创建连接、连接 代码剪切进一个js文件中
2. 提供查询所有getAll 查询单个getById 增删改exec方法,方法里用promise封装
3. 导出这三个方法
4. 在后台代码中导入js文件,解构这三个方法并使用
模块导出简写:
- //导出对象
- module.exports = {
- getAll: getAll,
- getById: getById,
- exec: exec,
- };
- //属性名和属性值相同,可简写为一个
- module.exports = {
- getAll,
- getById,
- exec,
- };
- //引入:const {getAll, exec}= require("./引入.js")
作用:将同步函数变成异步函数(返回promise对象),就可以使用then() 调用
使用:
- //定义
- async function m1() {
- return 222;
- }
- //调用
- const p = m1()
- p.then((data) => {
- console.log(data); //222
- });
作用:await可以等待后面的 promise对象执行后,拿到返回的结果
注意:
1. await不能单独使用,必须跟async连用,存在于异步函数中
2. await后面必须跟一个promise对象,如果不是promise对象,自动转成promise对象
3. await表达式,返回的是promise执行后的结果
使用:
- //在外面套一个异步函数壳,或将外层函数添加async关键字改为异步
- async function fn() {
- async function m1() {
- return 222;
- }
- //await 等待m1返回的promise对象执行完后,拿到返回的值
- const res = await m1();
- console.log(res);
- }
- fn();
- app.get("/users", async function(req, res) {
- let sql = 'select * from student';
- const data = await exec(sql);//执行SQL语句
- res.send(data);//发给浏览器
- })
- throw "错误信息"
- try{
- //可能会出问题的代码
- } catch (err) {
- //出问题的处理方案
- }
1. 需求分析(系统框架、具体业务分析)
2. 技术方案
数据库设计
设计接口
前台:发送请求的url,需要的参数
后台:返回数据的格式
3. 技术实现(编写代码)
先写后端(express脚手架快速生成)再写前端
1. 全局安装脚手架(项目生成器) npm i express-generator -g
2. 创建项目文件夹,执行命令生成后端目录 express --no-view backEnd
在目录下创建一个`backEnd`的目录, 作为后端项目的目录
`--no-view`: 创建一个数据服务, 不提供页面服务
3. 在后端目录安装相关依赖(cd backEnd,npm 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 mysql、npm i cors)
安装好后会自动添加在package.json
4)执行 npm run start 可以在3000端口看到 express脚手架欢迎页面
5. 导入数据库操作模块
将写好的db文件夹拷贝到backEnd
在routes/users.js中导入模块
- //1. 导入mysql包
- const mysql = require("mysql");
-
- //2. 创建连接,数据库参数可以放到配置文件中(config/index.js)
- var con = mysql.createConnection({
- host: "localhost",
- port: 3306,
- user: "root",
- password: "1234",
- database: "db01",
- timezone: "SYSTEM",
- });
-
- //3. 连接数据库
- con.connect();
-
- //4. 查询所有(获取查询的数据)
- function getAll(sql) {
- return new Promise((resolve, reject) => {
- //执行sql语句
- con.query(sql, (err, data) => {
- //有错,把失败的数据带回去
- if (err) reject(err);
-
- //将获取到的数据带回去
- resolve(data);
- });
- });
- }
-
- //5. 查询一个(获取查询的数据)
- function getById(sql) {
- return new Promise((resolve, reject) => {
- //执行sql语句
- con.query(sql, (err, data) => {
- //有错,把失败的数据带回去
- if (err) reject(err);
-
- //将获取到的数据带回去
- //易报错!
- resolve(data[0] ? data[0] : null);
- });
- });
- }
-
- //6. 管理(增删改)
- function exec(sql) {
- return new Promise((resolve, reject) => {
- //执行sql语句
- con.query(sql, (err, data) => {
- //有错,把失败的数据带回去
- if (err) reject(err);
-
- //将获取到的数据带回去
- resolve(data);
- });
- });
- }
-
- //导出方法
- /* module.exports = {
- getAll:getAll,
- getById:getById,
- exec:exec
- } */
- //属性名和属性值相同,可简写为一个
- module.exports = {
- getAll,
- getById,
- exec,
- };
6. 导入跨域请求模块
在app.js中导入 const cors = require("cors");
在app.js中注册 app.use(cors()) 注册路由之前
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("")
- var express = require('express');
-
- //导入数据库操作模块
- const {getAll, getById, exec} = require("../db");
-
- var router = express.Router();
-
- // 获取所有用户
- router.get('/', async function(req, res, next) {
- let sql = "select * from users";
- res.send(await getAll(sql))
- });
-
- // 获取一个用户
- router.get('/:id', async function(req, res, next) {
- let sql = `select * from users where ${req.params.id} = id`;
- res.send(await getById(sql))
- });
-
- // 添加一个用户
- router.post('/', async function(req, res, next) {
- let {name, age} = req.body;
- let sql = `insert into users (name, age) values("${name}", ${age}) `;
- let data = await exec(sql)
- res.send({
- id: data.insertId,
- name,
- age
- })
- });
-
- // 修改一个用户
- router.put('/:id', async function(req, res, next) {
- let {name, age} = req.body;
- let sql = `update users set name = "${name}", age = ${age} where id = ${req.params.id}`;
- res.send({
- id: req.params.id,
- name,
- age,
- data: await exec(sql),
- })
- });
-
- // 删除一个用户
- router.delete('/:id', async function(req, res, next) {
- let sql = `delete from users where id = ${req.params.id} `;
-
- let data = await exec(sql)
- //设置状态码
- res.status(204).send("")
- });
-
- module.exports = router;
查询(显示所有用户)
1. 新建 fontEnd 文件夹
2. 新建3个html页面
3. 编写list.html页面的骨架
4. 编写基本样式base.css
5. 编写list页面样式style.css
6. 引入jquery,发送ajax请求(已经引入了cors处理跨域请求)
7. 遍历获取到的数据,动态拼接 tr 元素,追加到table下面
- <body>
- <h1>用户列表h1>
- <div class="container">
- <a href="./add.html" class="add-btn">添加a>
- <table class="user-list">
- <tr>
- <th>idth>
- <th>nameth>
- <th>ageth>
- <th>操作th>
- tr>
- table>
- div>
-
- <script>
- $.ajax({
- url: "http://localhost:3000/users",
- type: "GET",
- success: function (data) {
- console.log(data); //js数组
-
- //遍历数组
- data.forEach((item) => {
- // console.log(item);
- const tr = `
-
${item.id} -
${item.name} -
${item.age} -
- ${item.id}" class="edit-btn">修改
- ${item.id})">删除
-
-
`; - $(".user-list").append(tr);
- });
- },
- });
-
- //删除
- function delUser(id) {
- console.log(id);
- $.ajax({
- url: "http://localhost:3000/users/" + id,
- type: "DELETE",
- success: function (res) {
- console.log(res);
- alert("删除成功");
- location.reload();
- },
- });
- }
- script>
- body>
新增
1. 编写add.html页面骨架
2. 在style.css中,添加add.html的样式
3. 监听add按钮的点击,在点击事件中获取用户输入的姓名与年龄进行判断
4. 输入为空,直接返回。否则用ajax发送post请求,提交到后台,修改数据库中数据
5. 修改成功,弹出提示,跳转回list.html页
- <body>
- <h1>添加用户h1>
- <div class="container">
- <ul>
- <li>
- <label for="userName">用户名label>
- <input type="text" name="userName" id="userName" placeholder="请输入用户名" />
- li>
- <li>
- <label for="userAge">年龄label>
- <input type="text" name="userAge" id="userAge" placeholder="请输入年龄" />
- li>
- ul>
- <button class="submit-btn">提交button>
- form>
-
- <script>
- $(".submit-btn").click(function () {
- const name = $("#userName").val().trim()
- const age = $("#userAge").val().trim()
-
- //判断
- if (name == "" || age == "") {
- alert("用户名或年龄不能为空")
- return
- }
-
- //发送异步请求
- $.ajax({
- url: "http://localhost:3000/users",
- type: "POST",
- data: { name, age },
- success: function (res) {
- alert("添加成功")
- location.href = "./list.html";
- },
- });
- });
- script>
- 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页面
- <body>
- <h1>修改用户h1>
- <div class="container">
- <ul>
- <li>
- <label for="userName">用户名label>
- <input type="text" name="userName" id="userName" placeholder="请输入用户名" />
- li>
- <li>
- <label for="userAge">年龄label>
- <input type="text" name="userAge" id="userAge" placeholder="请输入年龄" />
- li>
- ul>
- <button class="submit-btn">修改button>
- form>
-
- <script>
- const id = location.search.replace("?", "").split("=")[1]
-
- $.ajax({
- url: "http://localhost:3000/users/" + id,
- type: "GET",
- success: function (res) {
- console.log(res);
- //回显数据(设置input里的值)
- // alert("查找成功")
- $("#userName").val(res.name)
- $("#userAge").val(res.age)
- },
- });
-
- $(".submit-btn").click(function () {
- const name = $("#userName").val().trim()
- const age = $("#userAge").val().trim()
-
- //判断
- if (name == "" || age == "") {
- alert("用户名或年龄不能为空")
- return
- }
-
- //发送异步请求
- $.ajax({
- url: "http://localhost:3000/users/" + id,
- type: "PUT",
- data: { name, age },
- success: function (res) {
- console.log(res);
- //回显数据(设置input里的值)
- alert("修改成功")
- location.href = "./list.html";
- },
- });
- });
- script>
- body>
删除
1. 在list.html页 为删除a标签 添加点击事件,传入id
2. 弹出确认框
3. 用ajax发送delete请求,删除数据库中数据
4. 删除成功,刷新