Express 入门指南(详细)
一、安装
-
安装node.js,准备node环境
-
创建新文件夹,运行
npm init -y
创建package.json -
运行
yarn add nodemon -D
(自动重启工具) -
在package.json的scripts中增加
"dev":"nodemon app.js",
-
运行
yarn add express
安装express -
项目根目录新建 app.js
const express = require("express");
const app = express();
app.get("/", (req, res) => {
res.send("Hello World");
});
app.listen(4000, () => {
console.log("server is running");
});
运行 npm run dev
,访问localhost:4000可以看到hello world
二、路由
- 项目根目录新建controllers文件夹,专门存放路由回调
新建controllers/loginController.js,登录逻辑
exports.register = function (req, res) {
res.send("Got a POST request");
}
新建controllers/userController.js,用户逻辑
exports.list = function (req, res) {
res.json({
//发送json数据类型
list: [
{
name: "12",
id: 1,
},
{
name: "1233",
id: 2,
},
],
});
}
exports.deleteUser = function (req, res) {
res.send("Got a DELETE request at /user"); //发送各种类型的响应
}
- 项目根目录新建routes文件夹,专门存放路由
新建routes/login.js,登录路由
const express = require("express");
const loginController = require('../controllers/loginController');
const router = express.Router(); //模块化路由
router.post("/register", loginController.register);
module.exports = router;
新建routes/user.js,用户路由
const express = require("express");
const userController = require("../controllers/userController");
const router = express.Router();
router.get("/list", userController.list);
router.delete("/user", userController.deleteUser);
module.exports = router;
- 新建routes/index.js,路由出口
const express = require("express");
const userRouter = require("./user");
const loginRouter = require("./login");
const router = express.Router();
router.use("/api", userRouter); // 注入用户路由模块
router.use("/api", loginRouter); // 注入登录路由模块
module.exports = router;
- 在app.js中引入
const express = require("express");
const routes = require("./routes"); //新增
const app = express();
app.use("/", routes); //新增
//删除
// app.get("/", (req, res) => {
// res.send("Hello World");
// });
app.listen(4000, () => {
console.log("server is running");
});
通过浏览器或postman 访问 http://localhost:4000/api/list 可以看到接口成功被调用
将controllers逻辑与routes路由拆分开,目的是为了结构更加清晰,并且可以复用controller
最终目录结构:
三、托管静态文件
新建public/images文件夹,存放一些图片,例如react.png
在app.js中增加
var path = require('path');
app.use(express.static(path.join(__dirname, 'public')));
访问 http://localhost:4000/images/react.png 可以看到图片显示成功
四、中间件
中间件是个函数,有三个参数 req,res,next
若非结束响应,中间件必须调用 next() 将控制传递给下一个中间件函数(否则请求将成为悬挂请求)
1. 自定义全局中间件
//在app.js中新增
var myLogger = function (req, res, next) {
console.log("LOGGED");
next();
};
app.use(myLogger);
//加在 app.use("/", routes); 之前,在路由之前注册
访问:http://localhost:4000/api/list 可以看到终端打印了日志 LOGGED
2. 路由中间件
//在routes/login.js中新增
function login_middleware(req, res, next) {
console.log("中间件1");
next(); //传递给下一步
}
function login_params(req, res, next) {
let { name, password } = req.query;
if (!name || !password) {
//发送消息,结束响应,不需要再调用next
res.json({
message: "参数校验失败",
});
} else {
next();
}
}
router.post("/login", [login_middleware, login_params], (req, res, next) => {
res.send("登录成功");
});
在postman中使用post访问:http://localhost:4000/api/login?name=1&password=2 ,请求成功,并可在终端中看到打印信息
这里为了方便演示,将回调函数和路由都放到了一起,实际工作中,可做上述的controllers与routes拆分处理
接下来介绍第三方中间件 body-parser
五、body-parser
上面的post请求参数是放在url上的,实际场景通常都在body上,而express直接通过req.body只能获取到undefined,因此需要 body-parser 第三方库
-
运行
yarn add body-parser
-
修改app.js
const bodyParser = require('body-parser');
// 解析application/x-www-form-urlencoded数据格式
app.use(bodyParser.urlencoded({extended: true}));
// 解析json数据格式
app.use(bodyParser.json());
//解析 text/plain 数据格式
app.use(bodyParser.text());
//加在 app.use("/", routes); 之前,在路由之前注册
提示:body-parser目前不支持解析contentType: multipart/form-data 的格式类型
- 修改routes/login.js
router.post("/login", (req, res, next) => {
console.log("req", req.body);
res.send("登录成功");
});
使用postman通过post请求x-www-form-urlencoded数据格式
使用postman通过post请求json数据格式
注意:选完raw后,一定要将右侧的Text换成JSON
在实际开发中,前端通过URLSearchParams发送x-www-form-urlencoded数据格式
const params = new URLSearchParams();
params.append('param1', 'value1');
params.append('param2', 'value2');
六、解决跨域 cors
-
安装
yarn add cors
-
修改app.js
//新增
const cors = require('cors');
app.use(cors());
//加在 app.use("/", routes); 之前,在路由之前注册
- 最终app.js文件
const express = require("express");
const bodyParser = require("body-parser"); // 引入body-parser模块
var path = require("path");
const cors = require("cors");
const routes = require("./routes");
const app = express();
app.use(express.static(path.join(__dirname, "public")));
// 解析form表单提交的数据application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json()); // 解析json数据格式
app.use(bodyParser.text()); //解析 text/plain 数据格式
app.use(cors()); // 注入cors模块解决跨域
app.use("/", routes);
app.listen(4000, () => {
console.log("server is running port");
});
七、文件上传 multer
-
运行
yarn add multer
-
运行
yarn add uuid
-
新建util/upload.js
const fs = require("fs");
const path = require("path");
const multer = require("multer");
const uuid = require("uuid");
const memoryDest = path.join(__dirname, "../public/images");
const storage = multer.diskStorage({
// 文件存储位置
destination: (req, file, cb) => {
// 校验文件夹是否存在,如果不存在则创建一个
const isExists = fs.existsSync(memoryDest);
if (!isExists) {
fs.mkdirSync(memoryDest);
}
cb(null, memoryDest);
},
filename: (req, file, cb) => {
// 生成唯一文件名
const uid = uuid.v1();
// 获取文件扩展名
let ext = path.extname(file.originalname);
cb(null, uid + ext);
},
});
// 过滤文件
function fileFilter(req, file, callback) {
if (!file) {
callback(null, false);
} else {
callback(null, true);
}
}
const upload = multer({
storage: storage,
fileFilter: fileFilter,
limits: {
fileSize: 20 * 1024 * 1024,
},
}).single("file"); //上传的fieldname必须为file
module.exports = upload;
- 修改routes/user.js
const upload = require("../util/upload");
router.post("/upload", upload, (req, res, next) => {
// 存储后的文件信息在 req.file 中,此时文件已经存储到本地了。
console.log(req.file);
res.send("success");
});
- 前端代码
注意:name="file"必传,与上面single("file")对应
//ant组件
<Upload
name="file"
listType="picture-card"
className="avatar-uploader"
showUploadList={false}
action='http://127.0.0.1:4000/api/upload'
>
//Element组件
<el-upload
name="file"
class="avatar-uploader"
action='http://127.0.0.1:4000/api/upload'
:show-file-list="false"
></Upload>
八、连接数据库
前提准备:
- 本地数据库安装
该教程网上的资源比较多,可自行百度,如果有遇到麻烦的,可在评论区留言,我再更一篇安装本地数据库的教程
-
运行本地数据库并登录
-
修改本地数据库加密方式
在cmd中运行
ALTER USER 'root'@'localhost' IDENTIFIED BY 'password' PASSWORD EXPIRE NEVER;
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '123456';
FLUSH PRIVILEGES;
- 使用Navicat for MySQL新建连接
- 新建数据库
- 新建一个user表
id设为主键,并自动递增
填一些数据
express部分:
-
安装mysql:
yarn add mysql
-
新建db/mysql.js 创建数据库连接池
const mysql = require("mysql");
const pool = mysql.createPool({
connectionLimit: 10, //最大连接数,默认为10
host: "localhost", // 数据库服务器地址
port: 3306, //数据库端口
user: "root", // 数据库的用户名
password: "123456", // 数据库密码
database: "manage", // 数据库名
});
class Mysql {
constructor() {}
query(sql) {
return new Promise((resolve, reject) => {
pool.getConnection(function (err, connection) {
if (err) {
reject(err);
throw err; // not connected!
}
connection.query(sql, function (error, results, fields) {
if (error) {
reject(err);
throw error;
}
connection.release(); //只是释放链接,在缓冲池,没有被销毁
resolve(results);
});
});
});
}
}
module.exports = new Mysql();
- 新建service/userService.js 存放sql语句,实现语句的复用
//新增
exports.userAll = `SELECT * FROM user;`
- 修改controllers/userController.js
const mysql = require("../db/mysql");
const userService = require("../service/userService");
exports.getuser = function (req, res) {
mysql.query(userService.userAll).then((data) => {
let jsonData = JSON.parse(JSON.stringify(data));
res.json({
data: jsonData,
});
});
};
- 修改routes/user.js
//新增
router.get("/user/json", userController.getuser);
使用浏览器或postman访问 http://localhost:4000/api/user/json 可以获取到数据
九、express-jwt、jsonwebtoken
jsonwebtoken是用来生成token给客户端的,express-jwt是用来验证token的
-
安装:yarn add jsonwebtoken yarn add express-jwt
-
修改routes/login.js 生成token
const jwt = require("jsonwebtoken");
router.post("/login", (req, res, next) => {
let { username } = req.body;
// 登录成功,签发一个token并返回给前端
const token = jwt.sign(
// payload:签发的 token 里面要包含的一些数据
{ username},
// 私钥
"caowj",
// 设置过期时间
{ expiresIn: 60 * 60 * 24 } //1 day
);
res.json({
msg: "登录成功",
data: { token },
});
});
- 新建util/user-jwt.js 验证token
var { expressjwt: jwt } = require("express-jwt");
// 验证token是否过期
const jwtAuth = jwt({
secret: "caowj", //密匙
algorithms: ["HS256"], //签名算法
}).unless({ path: ["/api/login", "/api/register"] }); // unless 设置jwt认证白名单
module.exports = jwtAuth;
- 修改routes/index.js 注入验证模块,并对错误进行处理
//注入验证模块
const jwtAuth = require("../util/user-jwt");
router.use(jwtAuth);
router.use("/api", userRouter); // 注入用户路由模块
router.use("/api", loginRouter); // 注入登录路由模块
// 自定义统一异常处理中间件,需要放在代码最后
router.use((err, req, res, next) => {
// 自定义用户认证失败的错误返回
console.log("err===", err);
if (err && err.name === "UnauthorizedError") {
const { status = 401, message } = err;
// 抛出401异常
res.status(status).json({
code: status,
msg: "token失效,请重新登录",
data: null,
});
} else {
const { output } = err || {};
// 错误码和错误信息
const errCode = (output && output.statusCode) || 500;
const errMsg =
(output && output.payload && output.payload.error) || err.message;
res.status(errCode).json({
code: errCode,
msg: errMsg,
});
}
});
使用postman访问/api/login获取到token,这样在请求别的接口时,需要在请求头上将token加到authorization上
//前端传的对应值以Bearer开头然后空一格,接近着是token值
authorization:Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6I
该设计方式与express-jwt源码有关,感兴趣的可以去阅读
十、结尾
本篇文章如果有不对或不详细的地方,欢迎在评论区留言,后续会继续更新这篇文章
码字不易,点赞支持!!!
最后,祝大家春节快乐~
欢迎关注:之后文章会首发在云在前端公众号,未经许可禁止转载!
转载自:https://juejin.cn/post/7189164529430265912