通过前面两个章节express(1)基础 和 express(2)中间件的使用的学习,我们掌握了express的一些常见的语法和使用方式。现在我们来探究,express在实际项目中是如何开发的,或者说,如何通过express来搭建一个服务器项目。
在通过npm init -y 和安装好express之后,先在根目录下创建app.js,准备好服务器的基础代码
var express = require("express");
var app = express();
var PORT = process.env.PORT || 3000;
app.get("/", (req, res) => {
res.send("Hello World");
});
app.listen(PORT, function () {
console.log(`server runing at ${PORT}`);
});
|-- config # 配置文件
|-- configDefault.js # 配置文件
|-- controller # 用于解析用户的输入,处理后返回相应的结果
|-- user.js # 所有和user相关接口的通用处理函数或变量
|-- xxx.js # 不同的路由模块,业务放在不同的js文件中
|-- model # 数据持久层 所有对数据的操作,封装到里面
|-- index.js # 组合所有业务的数据模型
|-- user.js # 与user业务相关的数据模型
|-- xxx.js # 其它业务相关的数据模型
|-- middleware # 用于编写非路由的中间件,统一在里面管理
|-- errorHandler.js # 专门处理服务器报错的中间件
|-- router # 把应用中所有的路由配置到里面
|-- index.js # 所有路由的模块最终都会被引入到index.js中
|-- user.js # user业务路由模块
|-- xxx.js # 其它业务路由模块
|-- util # 全局通用变量或者函数的封装
|-- index.js # 全局通用变量或者函数的封装
|-- app.js # 我们服务器的主文件
express.json 和 express.urlencoded这两个中间件用来解析用户请求
cors 用来处理客户端跨域请求
morgan用来打印请求日志
写代码之前,先分别安装cors和morgan这两个第三方中间件
npm install cors
npm install morgan
在之前的介绍中,提到过express.json 和 express.urlencoded是express自带的,不需要额外安装
// app.js
var express = require("express");
var morgan = require("morgan");
var cors = require("cors");
var app = express();
var PORT = process.env.PORT || 3000;
var middlewares = [express.json(), express.urlencoded()];
app.use(middlewares, morgan("dev"), cors());
app.get("/", (req, res) => {
res.send("Hello World");
});
app.listen(PORT, function () {
console.log(`server runing at ${PORT}`);
});
如文件结构所示,在根目录创建一个router文件见,并创建index.js
//router/index.js
var express = require("express");
// 创建路由实例
const router = express.Router();
// 配置路由
router.get("/", function (req, res) {
res.status("200");
res.json({ code: 200, data: "请求成功了" });
});
// 导出路由
module.exports = router;
在app.js中使用
//app.js
var express = require("express");
var morgan = require("morgan");
var cors = require("cors");
var router = require("./router");
var app = express();
var PORT = process.env.PORT || 3000;
var middlewares = [express.json(), express.urlencoded()];
app.use(middlewares, morgan("dev"), cors());
// 我们希望所有的接口都以api开头,符合restful接口开发规范
app.use("/api", router);
app.listen(PORT, function () {
console.log(`server runing at ${PORT}`);
});
router/index.js中配置细化路由
我们不可能将整个项目的所有接口,都放入到router/index.js文件中,所以需要根据功能划分不同的模块,来开发配置路由。
比如与用户相关的接口user
在router文件下创建user.js
//router/user.js
var express = require("express");
// 创建路由实例
const router = express.Router();
// 用户登录
router.post("/user/login", function (req, res) {
res.status("200");
res.json({ code: 200, data: "请求成功了" });
});
// 用户注册
router.post("/users", function (req, res) {
res.status("200");
res.json({ code: 200, data: "请求成功了" });
});
// 获取用户
router.get("/user", function (req, res) {
res.status("200");
res.json({ code: 200, data: "请求成功了" });
});
// 更新用户
router.put("/user", function (req, res) {
res.status("200");
res.json({ code: 200, data: "请求成功了" });
});
// 导出路由
module.exports = router;
在router/index.js中引入
// router/index.js
var express = require("express");
var user = require("./user");
// 创建路由实例
const router = express.Router();
// 配置路由,遵循restful接口开发规范,统一使用json格式返回数据
router.use(function (req, res, next) {
res.set("Content-Type", "application/json");
next();
});
// 用户user相关的路由
router.use(user);
// 导出路由
module.exports = router;
一套路由配置的标准就建立得差不多了。
路由我们配置好了,可每一个接口的代码,处理客户端传递过来的数据,连接数据库或者转发到其它接口,或者是读取文件流等等,最终为本次请求提供响应,这一系列的操作,具体怎么实现?都放在接口router配置的回调函数里?
当然不是,很多时候,我们会将相同的方法,都配置到controller文件夹下,正如前面文件目录所示,在根目录下创建controller文件,并按照模块创建不同的js文件,比如user.js
// controller/user.js
exports.login = function (req, res) {
res.status("200");
res.json({
code: 200,
data: "请求成功了",
path: "/api/user/login",
methods: "post",
});
};
exports.register = function (req, res) {
res.status("200");
res.json({
code: 200,
data: "请求成功了",
path: "/api/users",
methods: "post",
});
};
exports.getUser = function (req, res) {
res.status("200");
res.json({
code: 200,
data: "请求成功了",
path: "/api/user",
methods: "get",
});
};
exports.putUser = function (req, res) {
res.status("200");
res.json({
code: 200,
data: "请求成功了",
path: "/api/user",
methods: "put",
});
};
在router/user.js中引入使用。
// router/user.js
var express = require("express");
var userControl = require("../controller/user");
// 创建路由实例
const router = express.Router();
// 用户登录
router.post("/user/login", userControl.login);
// 用户注册
router.post("/users", userControl.register);
// 获取用户
router.get("/user", userControl.getUser);
// 更新用户
router.put("/user", userControl.putUser);
// 导出路由
module.exports = router;
在express(2)中,介绍了错误处理中间件的使用和注册方式,现在,我们要在真实的项目中启用。
如上的文件路径所示,这是一个中间件,所以放置在middleware文件夹下的errorHandler.js中。
// middleware/errorHandler.js
// err里面必须传入错误状态码和错误的信息
module.exports = function () {
return function (err, req, res, next) {
res.status(err.status).json({
message: err.message,
});
};
};
在全局的app.js中所有路由配置后,配置该中间件
// app.js
var express = require("express");
var morgan = require("morgan");
var cors = require("cors");
var router = require("./router");
var errHandler = require("./middleware/errorHandler");
var app = express();
var PORT = process.env.PORT || 3000;
var middlewares = [express.json(), express.urlencoded()];
app.use(middlewares, morgan("dev"), cors());
// 我们希望所有的接口都以api开头
app.use("/api", router);
app.use(errHandler())
app.listen(PORT, function () {
console.log(`server runing at ${PORT}`);
});
当想要抛出错误时,如何使用,某个router的回调函数举例
还记得如何触发express的错误中间件么?next(params)
params非字符串’route’即可
// controller/user.js
exports.putUser = function (req, res, next) {
try {
// ...
res.status("200");
res.json({
code: 200,
data: "请求成功了",
path: "/api/user",
methods: "put",
});
} catch (error) {
console.log(error)
next({
status: 500,
message: '该用户更新失败'
})
}
};
//app.js
var express = require("express");
var morgan = require("morgan");
var cors = require("cors");
var router = require("./router");
var errHandler = require("./middleware/errorHandler");
var app = express();
var PORT = process.env.PORT || 3000;
var middlewares = [express.json(), express.urlencoded()];
app.use(middlewares, morgan("dev"), cors());
// 我们希望所有的接口都以api开头
app.use("/api", router);
// 404中间件处理,由于之前的所有router都没匹配到,前面的中间件都没有回复响应,我们需要这个中间件做兜底处理。
app.use((req, res) => {
res.status("404").send("404 not found");
});
app.use(errHandler());
app.listen(PORT, function () {
console.log(`server runing at ${PORT}`);
});
绝大多数业务都需要使用到数据库,创建数据模型。在model文件夹下创建index.js,用来链接所有的业务的数据模型,再以user业务为例,创建user.js文件。
以mysql数据库为例,需要安装npm第三方的包 mysql 和mysql2
npm install mysql mysql2
数据库的配置在一个项目中,通常都是固定的,host,user,password,database这些,我们可以放置在 config/configDefault.js文件中。
// config/configDefault.js
module.exports = {
mysqlHost: '127.0.0.1',
mysqlUser: 'dengxi',
mysqlPassword: 'root',
mysqlDatabase: 'express'
}
// model/index.js
var mysql = require("mysql2");
var {
mysqlHost,
mysqlUser,
mysqlPassword,
mysqlDatabase,
} = require("../config/configDefault");
var pool = mysql.createPool({
host: mysqlHost, // host: 数据库服务器的主机名
user: mysqlUser, // user: 连接数据库所需的用户名
password: mysqlPassword, // password: 连接数据库所需的密码
database: mysqlDatabase, // database: 要连接的数据库的名称
waitForConnections: true, // waitForConnections: 如果连接池已满,等待连接变为可用的时间(以毫秒为单位)
connectionLimit: 10, // connectionLimit: 连接池的大小
queueLimit: 0, // queueLimit: 当连接池已满时,排队等待连接变为可用的最大连接数。如果设置为0,则不允许排队
});
// 将一个promise抛出
module.exports = pool;
在utils文件夹下,将连接池的逻辑封装成一个函数,作为常用工具使用
// utils/index.js
var pool = require('../model/index');
// node中使用回调的api大部分都是异步的,这里对操作数据库动作做下封装。
exports.RunSQL = async function (sql) {
return new Promise((resolve, reject) => {
pool.query(sql, (err, result) => {
if (err) reject(err);
resolve(result);
});
});
}
具体业务的使用以注册用户user业务为例
post接口 path=“/api/users” 接收两个参数 username 和 password
在model层处理用户传递过来的数据并校验,然后连接数据库,并处理结果返回
// model/user.js
var utils = require("../utils/index");
/**
* 注册用户,model内部只做数据校验,和数据库连接,并返回结果给controller/user.js
* */
exports.signupUser = async (req) => {
var { username, password } = req.body;
var userNameIsOnlySql = `SELECT * FROM node_user WHERE user_name = '${username}'`;
var insertUserNameSql = `INSERT INTO node_user (user_name) VALUES ('${username}')`;
var result = {};
if (!(username && password)) {
// next({
// status: 401,
// message: "用户名或密码信息不存在",
// });
return {
type: "failed",
status: "401",
data: "",
message: "用户名或密码信息不存在",
};
}
try {
// 执行插入前执行查一下该用户名是否被注册
const queryRepeatUserList = await utils.RunSQL(userNameIsOnlySql);
// 无结果证明没有被注册
if (!queryRepeatUserList.length) {
const sqlResult = await utils.RunSQL(insertUserNameSql);
if (sqlResult) {
result = {
type: "success",
data: JSON.stringify({ user_id: sqlResult.insertId }),
message: "用户注册成功",
};
}
} else {
result = {
type: "failed",
data: "",
status: "500",
message: "用户名重复",
};
}
} catch (error) {
result = {
type: "failed",
data: "",
status: "500",
message: "数据库连接失败",
};
}
return result;
};
在controller内部获取model层的结果,并做出接口响应
// controller/user.js
var { signupUser } = require("../model/user");
exports.register = async function (req, res, next) {
var result = await signupUser(req);
res.set('Content-Type', 'application/json')
if (result.type === "success") {
res.status("200");
res.json({
code: "200",
data: "注册成功",
path: "/api/users",
methods: "post",
});
} else {
// 统一的错误处理中间件
next({
status: result.status,
message: result.message
})
}
};
到现在为止,一整套express服务器项目流程就建立得七七八八了,稍后我会将完整的代码上传到代码仓库。
nodejs commonjs介绍
nodejs fs模块介绍
nodejs path模块介绍
nodejs events模块介绍
nodejs http模块介绍
nodejs net模块介绍
nodejs url模块介绍
nodejs process模块介绍
nodejs buffer模块介绍
nodejs stream 模块介绍
nodejs express(1)模块介绍
nodejs express(2)中间件详解