介绍与安装

Express 是一个基于 Node.js 平台的 web 应用开发框架,提供了一系列强大而灵活的功能,用于创建单页、多页及混合应用。它是目前最流行的 Node.js 框架之一。

安装 Express 及其生成器工具可以使用以下命令:

使用命令

1
sudo npx --package express-generator express

这将会在当前目录下创建一个新的 Express 项目。

常用依赖

在项目中,我们常常会使用 nodemon 来自动重启服务器,便于开发。可以在 package.json 文件中添加以下内容:

1
2
3
4
5
6
7
8
{
"scripts": {
"dev": "nodemon ./bin/www"
},
"devDependencies": {
"nodemon": "^2.0.7"
}
}

发送数据

在 Express 中,发送数据到客户端有多种方式,可以发送文本、JSON、文件等。以下是一些常用的方法及其详细说明:

  1. res.send():

    • 用于发送字符串、Buffer 或对象作为响应体。
    • 自动设置 Content-Type,如果发送的是对象,会设置为 application/json
    • 示例:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      app.get('/', (req, res) => {
      res.send('Hello World!');
      });

      app.get('/buffer', (req, res) => {
      res.send(Buffer.from('Hello Buffer!'));
      });

      app.get('/object', (req, res) => {
      res.send({ message: 'Hello, Object!' });
      });
  2. res.json():

    • 用于发送 JSON 响应,自动设置 Content-Type: application/json
    • 相当于 res.send() 的对象版本,但更明确。
    • 示例:
      1
      2
      3
      app.get('/api/data', (req, res) => {
      res.json({ message: 'Hello, JSON!' });
      });
  3. res.jsonp():

    • 用于发送 JSONP 响应,可以解决跨域问题。
    • 自动设置 Content-Type: application/javascript,并包装 JSON 响应在一个回调函数中。
    • 示例:
      1
      2
      3
      app.get('/api/data', (req, res) => {
      res.jsonp({ message: 'Hello, JSONP!' });
      });
  4. res.sendFile():

    • 用于发送文件作为响应体。
    • 需要提供文件的绝对路径,通常使用 __dirname 来构建路径。
    • 可以通过选项对象设置缓存控制等。
    • 示例:
      1
      2
      3
      4
      5
      6
      7
      app.get('/file', (req, res) => {
      res.sendFile(__dirname + '/file.txt');
      });

      app.get('/file-options', (req, res) => {
      res.sendFile(__dirname + '/file.txt', { cacheControl: false });
      });
  5. res.download():

    • 用于提示下载文件。
    • 需要提供文件路径和可选的下载文件名。
    • 示例:
      1
      2
      3
      4
      5
      6
      7
      app.get('/download', (req, res) => {
      res.download(__dirname + '/file.txt');
      });

      app.get('/download-name', (req, res) => {
      res.download(__dirname + '/file.txt', 'custom_name.txt');
      });
  6. res.redirect():

    • 用于重定向请求到另一个 URL。
    • 可以传递 URL 或者状态码和 URL。
    • 示例:
      1
      2
      3
      4
      5
      6
      7
      app.get('/google', (req, res) => {
      res.redirect('https://www.google.com');
      });

      app.get('/redirect-301', (req, res) => {
      res.redirect(301, 'https://www.google.com');
      });
  7. res.render():

    • 用于渲染视图模板,并将生成的 HTML 发送给客户端。
    • 需要配置模板引擎。
    • 示例(假设使用 Pug 模板引擎):
      1
      2
      3
      4
      5
      app.set('view engine', 'pug');

      app.get('/template', (req, res) => {
      res.render('index', { title: 'Hey', message: 'Hello there!' });
      });
  8. res.end():

    • 结束响应处理流程,通常用于流数据或手动发送响应头后。
    • 示例:
      1
      2
      3
      4
      app.get('/end', (req, res) => {
      res.write('Part of the response');
      res.end('The end of the response');
      });

使用这些方法,您可以灵活地处理和发送不同类型的响应数据,满足各种应用需求。

路由

路由是应用程序的基本组成部分,负责定义应用程序的各个端点(URI)以及如何响应客户端请求。

路由就相当于一个小应用,所以它请求的方法和app是一样的。

定义

需要注意的是路由是从上而下叠加的,所以静态路由一般要注意放在动态路由上面

1
2
3
4
5
6
7
8
9
var express = require('express');
var router = express.Router();

/* GET users listing. */
router.get('/', function(req, res, next) {
res.send('respond with a resource');
});

module.exports = router;

使用

在主应用中使用定义的路由模块:

1
2
3
4
5
6
7
8
9
var express = require('express');
var app = express();
var usersRouter = require('./routes/users');

app.use('/users', usersRouter);

app.listen(3000, function() {
console.log('Server is running on port 3000');
});

路由方法

Express 提供多种路由方法,分别对应于 HTTP 的各种请求方法:getpostputdelete 等。例如:

1
2
3
4
5
6
7
8
9
10
11
router.post('/', function(req, res) {
res.send('Got a POST request');
});

router.put('/user', function(req, res) {
res.send('Got a PUT request at /user');
});

router.delete('/user', function(req, res) {
res.send('Got a DELETE request at /user');
});

同时express提供了route用于更方便的在同一个路径下面,调用不同的路由方法,同时function也可以使用箭头函数来简化=>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
router
.route("/")
.get((req, res) => {
res.send('Got a GET request');
})
.post((req, res) => {
res.send('Got a POST request');
})
.put((req, res) => {
res.send('Got a PUT request');
})
.delete((req, res) => {
res.send('Got a DELETE request');
});

路由参数

你可以在路由中使用参数,通过 req.params 获取:

1
2
3
router.get('/user/:id', function(req, res) {
res.send('user id: ' + req.params.id);
});

中间件

详细请见下面的主标题的内容

路由分组

你可以将相关的路由放在同一个路由器实例中:

1
2
3
4
5
6
7
8
9
10
11
12
var express = require('express');
var router = express.Router();

router.get('/about', function(req, res) {
res.send('About Page');
});

router.get('/contact', function(req, res) {
res.send('Contact Page');
});

module.exports = router;

在主应用中引入:

1
2
3
4
5
6
7
8
9
var express = require('express');
var app = express();
var mainRouter = require('./routes/main');

app.use('/', mainRouter);

app.listen(3000, function() {
console.log('Server is running on port 3000');
});

错误处理

在路由中处理错误的方法:

1
2
3
4
router.use(function(err, req, res, next) {
console.error(err.stack);
res.status(500).send('Something broke!');
});

以上是关于 Express 路由的详细笔记,涵盖了定义、使用、方法、参数、中间件、分组和错误处理等方面。

中间件

中间件是 Express 应用程序中一个重要的概念,用于处理请求、响应以及应用程序之间的一系列操作。

  1. 应用级中间件:

    1
    2
    3
    4
    app.use((req, res, next) => {
    console.log('Time:', Date.now());
    next();
    });
  2. 路由级中间件:

    1
    2
    3
    4
    5
    6
    7
    8
    const router = express.Router();

    router.use((req, res, next) => {
    console.log('Request URL:', req.originalUrl);
    next();
    });

    app.use('/api', router);
  3. 错误处理中间件:

    1
    2
    3
    4
    app.use((err, req, res, next) => {
    console.error(err.stack);
    res.status(500).send('Something broke!');
    });
  4. 第三方中间件(如 body-parser 用于解析请求体):

    1
    2
    const bodyParser = require('body-parser');
    app.use(bodyParser.json());

中间件位于发送到服务器的请求(app.get)和返回给用户的实际响应之间

同时中间件是否执行也取决于位于代码的位置,如果有请求代码位于中间件之前的话,该中间件则不会影响该请求代码

路由中间件是一种执行前后处理操作的方法。使用 next 参数可以传递控制权:

router.param就是一个中间件函数,在我发送请求之前就运行,如果我不使用next()的话将不会执行发送请求的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
router.use(function(req, res, next) {
console.log('Time:', Date.now());
next();
});

router.get('/', function(req, res, next) {
res.send('Home Page');
});

router.param("id", (req, res, next, id) => {
console.log(id);
next();
});
app.get("/", logger, (req, res) => {
res.render("index", { text: "world" });
});

const userRouter = require("./routes/users");

app.use("/users", userRouter);

function logger(req, res, next) {
console.log(req.originalUrl);
next();
}

两种方式使用中间件的方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 1. 直接调用
app.use(logger)
app.get("/", (req, res) => {
res.render("index", { text: "world" });
});

function logger(req, res, next) {
console.log(req.originalUrl);
next();
}

// 2. 在请求方法里面调用
app.get("/", logger, (req, res) => {
res.render("index", { text: "world" });
});

function logger(req, res, next) {
console.log(req.originalUrl);
next();
}


解析表单/JSON数据

在 Express 应用中,解析表单和 JSON 数据是处理客户端请求的关键步骤。Express 提供了中间件来处理这些数据。

解析 URL 编码的表单数据

为了解析传统的表单数据,你可以使用 express.urlencoded 中间件。它会解析 application/x-www-form-urlencoded 编码的数据,这是标准的 HTML 表单提交的格式。

使用 express.urlencoded

直接使用req.body的话,是无法获取的,因为express不允许访问bode的内容,所以我们需要借助中间件

注意这个{ extended: true }不放的话,会有warning

1
2
3
4
5
6
7
8
9
10
11
12
13
var express = require('express');
var app = express();

app.use(express.urlencoded({ extended: true }));

app.post('/submit-form', function(req, res) {
console.log(req.body);
res.send('Form data received');
});

app.listen(3000, function() {
console.log('Server is running on port 3000');
});

参数说明

  • extended: true:使用 qs 库解析 URL 编码数据,支持丰富的对象和数组结构。
  • extended: false:使用 querystring 库解析 URL 编码数据,只支持简单的键值对。

解析 JSON 数据

为了解析 JSON 数据,你可以使用 express.json 中间件。它会解析 application/json 类型的数据,这在使用 API 时非常常见。

使用 express.json

1
2
3
4
5
6
7
8
9
10
11
12
13
var express = require('express');
var app = express();

app.use(express.json());

app.post('/submit-json', function(req, res) {
console.log(req.body);
res.send('JSON data received');
});

app.listen(3000, function() {
console.log('Server is running on port 3000');
});

处理文件上传

如果需要处理文件上传,可以使用 multer 中间件。multer 是一个处理 multipart/form-data 的中间件,通常用于上传文件。

安装 multer

1
npm install multer

使用 multer

1
2
3
4
5
6
7
8
9
10
11
12
13
var express = require('express');
var multer = require('multer');
var upload = multer({ dest: 'uploads/' });
var app = express();

app.post('/upload', upload.single('file'), function(req, res) {
console.log(req.file);
res.send('File uploaded successfully');
});

app.listen(3000, function() {
console.log('Server is running on port 3000');
});

参数说明

  • dest:指定文件上传后的保存路径。
  • upload.single('file'):处理单个文件上传,file 是表单中 <input type="file" name="file">name 属性。

中间件顺序

确保解析中间件的顺序正确,应该在定义路由之前使用中间件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.use('/upload', upload.single('file'));

// Define routes here
app.post('/submit-form', function(req, res) {
// Handle form data
});

app.post('/submit-json', function(req, res) {
// Handle JSON data
});

app.post('/upload', function(req, res) {
// Handle file upload
});

错误处理

处理解析过程中可能出现的错误:

1
2
3
4
app.use(function(err, req, res, next) {
console.error(err.stack);
res.status(500).send('Something went wrong!');
});

以上是关于解析表单和 JSON 数据的详细笔记,包括了解析 URL 编码的表单数据、解析 JSON 数据、处理文件上传、确保中间件顺序以及错误处理等方面。

框架代码解析

目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
myapp
├── app.js
├── package.json
├── bin
│ └── www
├── public
│ ├── images
│ ├── javascripts
│ └── stylesheets
│ └── style.css
├── routes
│ ├── index.js
│ └── users.js
├── views
│ ├── error.jade
│ ├── index.jade
│ └── layout.jade
└── node_modules

index.js

这个render是专门用于渲染html的,然后后面这个{ title: 'Express' }实际上可以传任意对象,它的作用主要是把这个值传递给index.jade

1
2
3
4
5
6
7
8
9
var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
});

module.exports = router;

index.jade

1
2
3
4
5
extends layout

block content
h1= title
p Welcome to #{title}

/bin/www.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#!/usr/bin/env node

/**
* 模块依赖.
*/

var app = require('../app'); // 引入应用程序实例
var debug = require('debug')('ardemobackend:server'); // 引入debug模块并命名为ardemobackend:server
var http = require('http'); // 引入http模块

/**
* 从环境变量获取端口并存储在Express中.
*/

var port = normalizePort(process.env.PORT || '3000'); // 获取环境变量中的端口号或默认使用3000端口
app.set('port', port); // 将端口号设置到应用程序实例中

/**
* 创建HTTP服务器.
*/

var server = http.createServer(app); // 用应用程序实例创建HTTP服务器

/**
* 在所有网络接口上监听提供的端口.
*/

server.listen(port); // 服务器开始监听提供的端口
server.on('error', onError); // 监听服务器错误事件并调用onError函数处理
server.on('listening', onListening); // 监听服务器监听事件并调用onListening函数处理

/**
* 将端口规范化为一个数字,字符串,或false.
*/

function normalizePort(val) {
var port = parseInt(val, 10); // 将端口号字符串转换为整数

if (isNaN(port)) {
// 命名管道
return val; // 如果转换失败,则返回原始值(可能是命名管道)
}

if (port >= 0) {
// 端口号
return port; // 如果是有效端口号,则返回该端口号
}

return false; // 其它情况返回false
}

/**
* HTTP服务器"error"事件的事件监听器.
*/

function onError(error) {
if (error.syscall !== 'listen') {
throw error; // 如果错误不是监听错误,则抛出错误
}

var bind = typeof port === 'string'
? 'Pipe ' + port // 如果端口是字符串类型,则表示为命名管道
: 'Port ' + port; // 如果端口是数字类型,则表示为端口号

// 使用友好的消息处理特定的监听错误
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges'); // 端口需要更高权限
process.exit(1); // 退出进程并返回状态码1
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use'); // 端口已被占用
process.exit(1); // 退出进程并返回状态码1
break;
default:
throw error; // 其它错误抛出
}
}

/**
* HTTP服务器"listening"事件的事件监听器.
*/

function onListening() {
var addr = server.address(); // 获取服务器地址信息
var bind = typeof addr === 'string'
? 'pipe ' + addr // 如果地址是字符串类型,则表示为命名管道
: 'port ' + addr.port; // 如果地址是对象类型,则表示为端口号
debug('Listening on ' + bind); // 输出调试信息,表示服务器正在监听
}

app.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
var createError = require('http-errors'); // 引入http-errors模块用于创建错误
var express = require('express'); // 引入express模块
var path = require('path'); // 引入path模块
var cookieParser = require('cookie-parser'); // 引入cookie-parser模块用于解析Cookie
var logger = require('morgan'); // 引入morgan模块用于日志记录

var indexRouter = require('./routes/index'); // 引入主页路由模块
var usersRouter = require('./routes/users'); // 引入用户路由模块

var app = express(); // 创建Express应用实例

// 视图引擎设置
app.set('views', path.join(__dirname, 'views')); // 设置视图文件夹路径
app.set('view engine', 'jade'); // 设置视图引擎为jade

app.use(logger('dev')); // 使用morgan进行开发环境日志记录
app.use(express.json()); // 解析JSON格式的请求体
app.use(express.urlencoded({ extended: false })); // 解析URL编码的请求体
app.use(cookieParser()); // 解析Cookie
app.use(express.static(path.join(__dirname, 'public'))); // 设置静态文件夹

app.use('/', indexRouter); // 使用主页路由
app.use('/users', usersRouter); // 使用用户路由

// 捕获404错误并转发到错误处理器
app.use(function(req, res, next) {
next(createError(404)); // 创建404错误并传递到下一个中间件
});

// 错误处理器
app.use(function(err, req, res, next) {
// 设置局部变量,仅在开发环境提供错误信息
res.locals.message = err.message; // 设置错误消息
res.locals.error = req.app.get('env') === 'development' ? err : {}; // 如果是开发环境,则提供详细错误信息

// 渲染错误页面
res.status(err.status || 500); // 设置响应状态码
res.render('error'); // 渲染错误视图
});

module.exports = app; // 导出app模块

使用MySQL实现后端

好的,我们可以使用 Sequelize 作为 ORM 框架来实现 MySQL 后端。以下是如何使用 Sequelize 和 Express 来实现用户的增、删、改、查操作的示例。

第一步:安装必要的 npm 包

1
npm install express sequelize mysql2

第二步:设置 Sequelize 连接

在项目根目录下创建一个 config 文件夹,然后在其中创建一个 database.js 文件,用于配置 Sequelize 连接:

1
2
3
4
5
6
7
8
const { Sequelize } = require('sequelize');

const sequelize = new Sequelize('mydatabase', 'root', 'password', {
host: 'localhost',
dialect: 'mysql',
});

module.exports = sequelize;

第三步:定义用户模型

在项目根目录下创建一个 models 文件夹,然后在其中创建一个 user.js 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const { Sequelize, DataTypes } = require('sequelize');
const sequelize = require('../config/database');

const User = sequelize.define('User', {
name: {
type: DataTypes.STRING,
allowNull: false,
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
age: {
type: DataTypes.INTEGER,
allowNull: false,
},
createdAt: {
type: DataTypes.DATE,
defaultValue: Sequelize.NOW,
},
});

module.exports = User;

第四步:同步模型和数据库

app.js 文件中同步模型和数据库:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const express = require('express');
const sequelize = require('./config/database');
const usersRouter = require('./routes/users'); // 引入用户路由模块

const app = express();

app.use(express.json()); // 解析 JSON 请求体

// 同步模型和数据库
sequelize.sync()
.then(() => {
console.log('Database synced');
})
.catch((err) => {
console.error('Error syncing database:', err);
});

app.use('/users', usersRouter);

// 视图引擎设置
// ........

module.exports = app;

第五步:创建用户路由

routes 文件夹中创建 users.js 路由文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
const express = require('express');
const router = express.Router();
const User = require('../models/user');

// @route GET /users
// @desc Get all users
router.get('/', async (req, res) => {
try {
const users = await User.findAll();
res.json(users);
} catch (err) {
res.status(500).json({ message: err.message });
}
});

// @route GET /users/:id
// @desc Get user by ID
router.get('/:id', async (req, res) => {
try {
const user = await User.findByPk(req.params.id);
if (!user) {
return res.status(404).json({ message: 'User not found' });
}
res.json(user);
} catch (err) {
res.status(500).json({ message: err.message });
}
});

// @route POST /users
// @desc Create a new user
router.post('/', async (req, res) => {
try {
const { name, email, age } = req.body;
const newUser = await User.create({ name, email, age });
res.status(201).json(newUser);
} catch (err) {
res.status(400).json({ message: err.message });
}
});

// @route PUT /users/:id
// @desc Update a user
router.put('/:id', async (req, res) => {
try {
const { name, email, age } = req.body;
const user = await User.findByPk(req.params.id);
if (!user) {
return res.status(404).json({ message: 'User not found' });
}
user.name = name || user.name;
user.email = email || user.email;
user.age = age || user.age;
await user.save();
res.json(user);
} catch (err) {
res.status(400).json({ message: err.message });
}
});

// @route DELETE /users/:id
// @desc Delete a user
router.delete('/:id', async (req, res) => {
try {
const user = await User.findByPk(req.params.id);
if (!user) {
return res.status(404).json({ message: 'User not found' });
}
await user.destroy();
res.json({ message: 'User deleted' });
} catch (err) {
res.status(500).json({ message: err.message });
}
});

module.exports = router;

第六步:在 app.js 中使用用户路由

1
2
3
4
5
6
const usersRouter = require('./routes/users'); // 引入用户路由模块

app.use('/users', usersRouter);

// 视图引擎设置
// ........

CRUD 操作文档

1. 创建 (Create)

在 Sequelize 中,创建一个新的记录是通过实例化模型并调用 create() 方法来完成的。

作用:创建一个新的记录并将其保存到数据库中。

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const User = require('../models/user');

const newUser = {
name: 'Jane Smith',
age: 25,
email: 'jane.smith@example.com'
};

User.create(newUser)
.then(savedUser => {
console.log('User saved:', savedUser);
})
.catch(err => {
console.error('Error saving user:', err);
});

2. 读取 (Read)

读取操作涉及查询数据库以检索记录。

作用:从数据库中检索一个或多个记录。

例子

  • 查询所有记录:
1
2
3
4
5
6
7
User.findAll()
.then(users => {
console.log('Users found:', users);
})
.catch(err => {
console.error('Error finding users:', err);
});
  • 查询特定记录:
1
2
3
4
5
6
7
User.findOne({ where: { name: 'Jane Smith' } })
.then(user => {
console.log('User found:', user);
})
.catch(err => {
console.error('Error finding user:', err);
});

3. 更新 (Update)

更新操作用于修改已存在的记录。

作用:在数据库中更新一个记录的字段。

例子

  • 使用 update() 更新一个记录:
1
2
3
4
5
6
7
User.update({ age: 26 }, { where: { name: 'Jane Smith' } })
.then(() => {
console.log('User updated');
})
.catch(err => {
console.error('Error updating user:', err);
});
  • 使用 findByPk()save() 更新一个记录(需要知道记录的 ID):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
User.findByPk(userId)
.then(user => {
if (user) {
user.age = 26;
return user.save();
} else {
console.log('User not found');
}
})
.then(updatedUser => {
console.log('User updated:', updatedUser);
})
.catch(err => {
console.error('Error updating user:', err);
});

4. 删除 (Delete)

删除操作用于从数据库中移除记录。

作用:删除一个或多个数据库中的记录。

例子

  • 删除一个记录:
1
2
3
4
5
6
7
User.destroy({ where: { name: 'Jane Smith' } })
.then(() => {
console.log('User deleted');
})
.catch(err => {
console.error('Error deleting user:', err);
});
  • 删除所有记录:
1
2
3
4
5
6
7
User.destroy({ where: {} })
.then(() => {
console.log('All users deleted');
})
.catch(err => {
console.error('Error deleting users:', err);
});

使用MongoDB实现后端

当然,这里是一个完整的示例,展示如何在MongoDB中使用Mongoose和Express实现用户的增、删、改、查操作。

第一步:在 app.js 中连接数据库并添加相关路由

修改 app.js 文件,添加用户路由,下面的代码为提取过的只关于连接的部分:

在这里设置MongoDB连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var mongoose = require('mongoose');

var usersRouter = require('./routes/users'); // 引入用户路由模块

var app = express();

// 设置MongoDB连接
mongoose.connect('mongodb://localhost:27017/mydatabase', {
useNewUrlParser: true,
useUnifiedTopology: true,
});
const db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', () => {
console.log('Connected to MongoDB');
});

// 视图引擎设置
// .........

module.exports = app;

当然也可以使用环境变量的方式储存配置

首先下载dotenv

1
sudo npm install dotenv

然后新建.env文件

1
DATABASE_URL=mongodb://localhost:27017/expressDemo

最后是更改app.js中的链接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var mongoose = require('mongoose');
require('dotenv').config();

var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users'); // 引入用户路由模块

var app = express();

// 设置MongoDB连接
mongoose.connect(process.env.DATABASE_URL, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
const db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', () => {
console.log('Connected to MongoDB');
});

// 视图引擎设置
// ........

module.exports = app;

第二步:定义用户模式(Schema)

models 文件夹中创建 User.js 模型文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const mongoose = require('mongoose');

const UserSchema = new mongoose.Schema({
name: {
type: String,
required: true,
},
email: {
type: String,
required: true,
unique: true,
},
age: {
type: Number,
required: true,
},
date: {
type: Date,
default: Date.now,
},
});

// 创建模型(Model)
// 模型是从模式编译而来的,用于创建、查询、更新和删除数据。
module.exports = mongoose.model('User', UserSchema);

需要注意的部分是module.exports = mongoose.model('User', UserSchema);并不是mongodb的collectionname,正确的collectionname逻辑如下:模型名称是 User,所以Mongoose会自动使用集合名称 users。这意味着 users.js 路由文件中的所有操作都对应于MongoDB数据库中的 users 集合。

第三步:创建用户路由

routes 文件夹中创建 users.js 路由文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
const express = require('express');
const router = express.Router();
const User = require('../models/User');

// @route GET /users
// @desc Get all users
router.get('/', async (req, res) => {
try {
const users = await User.find();
res.json(users);
} catch (err) {
res.status(500).json({ message: err.message });
}
});

// @route GET /users/:id
// @desc Get user by ID
router.get('/:id', getUser, (req, res) => {
res.json(res.user);
});

// @route POST /users
// @desc Create a new user
router.post('/', async (req, res) => {
const user = new User({
name: req.body.name,
email: req.body.email,
age: req.body.age,
});

try {
const newUser = await user.save();
res.status(201).json(newUser);
} catch (err) {
res.status(400).json({ message: err.message });
}
});

// @route PUT /users/:id
// @desc Update a user
router.put('/:id', getUser, async (req, res) => {
if (req.body.name != null) {
res.user.name = req.body.name;
}
if (req.body.email != null) {
res.user.email = req.body.email;
}
if (req.body.age != null) {
res.user.age = req.body.age;
}

try {
const updatedUser = await res.user.save();
res.json(updatedUser);
} catch (err) {
res.status(400).json({ message: err.message });
}
});

// @route DELETE /users/:id
// @desc Delete a user
router.delete('/:id', getUser, async (req, res) => {
try {
await res.user.remove();
res.json({ message: 'User deleted' });
} catch (err) {
res.status(500).json({ message: err.message });
}
});

// Middleware function to get user by ID
async function getUser(req, res, next) {
let user;
try {
user = await User.findById(req.params.id);
if (user == null) {
return res.status(404).json({ message: 'Cannot find user' });
}
} catch (err) {
return res.status(500).json({ message: err.message });
}

res.user = user;
next();
}

module.exports = router;

CRUD 操作文档

1. 创建 (Create)

在 Mongoose 中,创建一个新的文档是通过实例化模型并调用 save() 方法来完成的。

作用:创建一个新的文档并将其保存到数据库中。

例子

1
2
3
4
5
6
7
8
9
10
11
12
const User = mongoose.model('User', userSchema);

const newUser = new User({
name: 'Jane Smith',
age: 25,
email: 'jane.smith@example.com'
});

newUser.save((err, savedUser) => {
if (err) return handleError(err);
console.log('User saved:', savedUser);
});

2. 读取 (Read)

读取操作涉及查询数据库以检索文档。

作用:从数据库中检索一个或多个文档。

例子

  • 查询所有文档:

    1
    2
    3
    4
    User.find({}, (err, users) => {
    if (err) return handleError(err);
    console.log('Users found:', users);
    });
  • 查询特定文档:

    1
    2
    3
    4
    User.findOne({ name: 'Jane Smith' }, (err, user) => {
    if (err) return handleError(err);
    console.log('User found:', user);
    });

3. 更新 (Update)

更新操作用于修改已存在的文档。

作用:在数据库中更新一个文档的字段。

例子

  • 使用 findOneAndUpdate() 更新一个文档:

    1
    2
    3
    4
    User.findOneAndUpdate({ name: 'Jane Smith' }, { age: 26 }, (err, user) => {
    if (err) return handleError(err);
    console.log('User updated:', user);
    });
  • 使用 findByIdAndUpdate() 更新一个文档(需要知道文档的 ID):

    1
    2
    3
    4
    User.findByIdAndUpdate(userId, { age: 26 }, (err, user) => {
    if (err) return handleError(err);
    console.log('User updated:', user);
    });

4. 删除 (Delete)

删除操作用于从数据库中移除文档。

作用:删除一个或多个数据库中的文档。

例子

  • 删除一个文档:

    1
    2
    3
    4
    User.findOneAndDelete({ name: 'Jane Smith' }, (err, user) => {
    if (err) return handleError(err);
    console.log('User deleted:', user);
    });
  • 删除所有文档:

    1
    2
    3
    4
    User.deleteMany({}, (err) => {
    if (err) return handleError(err);
    console.log('All users deleted');
    });

完成

已经实现了一个完整的用户管理API,支持增、删、改、查操作。例如:

  • GET /users - 获取所有用户
  • GET /users/:id - 获取特定ID的用户
  • POST /users - 创建新用户
  • PUT /users/:id - 更新特定ID的用户
  • DELETE /users/:id - 删除特定ID的用户

基础代码:

app.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');


// var mongoose = require('mongoose');
const mysql = require('mysql2');
require('dotenv').config();

var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users'); // 引入用户路由模块

var app = express();

// 设置MongoDB连接
// mongoose.connect(process.env.DATABASE_URL, {
// useNewUrlParser: true,
// useUnifiedTopology: true,
// });
// const db = mongoose.connection;
// db.on('error', console.error.bind(console, 'connection error:'));
// db.once('open', () => {
// console.log('Connected to MongoDB');
// });

// 设置MySQL连接
const db = mysql.createPool({
host: 'localhost',
port: 3306,
user: 'root',
password: '12345678',
database: 'go'
});

db.getConnection((err, connection) => {
if (err) {
console.error('Error connecting to MySQL:', err);
} else {
console.log('Connected to MySQL');
connection.release();
}
});


// 视图引擎设置
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', indexRouter);
app.use('/users', usersRouter); // 使用用户路由

// 捕获404错误并转发到错误处理器
app.use(function(req, res, next) {
next(createError(404));
});

// 错误处理器
app.use(function(err, req, res, next) {
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};

res.status(err.status || 500);
res.render('error');
});

module.exports = app;

models/index.js

1
2
3
4
5
6
7
8
9
const { Sequelize } = require('sequelize');

const sequelize = new Sequelize('go', 'root', '12345678', {
host: 'localhost',
port: 3306,
dialect: 'mysql',
});

module.exports = sequelize;

models/User.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
const { Sequelize, DataTypes } = require('sequelize');
const sequelize = require('./index');

const User = sequelize.define('User', {
user_id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true
},
user_name: {
type: DataTypes.STRING,
allowNull: false
},
user_psw: {
type: DataTypes.STRING,
allowNull: false
},
user_email: {
type: DataTypes.STRING,
allowNull: false
}
}, {
tableName: 'user', // 指定数据库表名为 'user'
timestamps: false // 如果没有 createdAt 和 updatedAt 字段,可以禁用时间戳
});

module.exports = User;

routes/index.js

1
2
3
4
5
6
7
8
9
10
var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
});


module.exports = router;

routes/users.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
const express = require('express');
const router = express.Router();
const User = require('../models/User');

// @route GET /users
// @desc Get all users
// 获取所有用户
router.get('/', async (req, res) => {
try {
const users = await User.findAll();
res.json(users);
} catch (err) {
res.status(500).json({ message: err.message });
}
});

// @route GET /users/:id
// @desc Get user by ID
// 获取指定ID的用户
router.get('/:id', getUser, (req, res) => {
res.json(res.user);
});

// @route POST /users
// @desc Create a new user
// 创建新用户
router.post('/', async (req, res) => {
const user = new User({
user_name: req.body.user_name,
user_psw: req.body.user_psw,
user_email: req.body.user_email,
});

try {
const newUser = await user.save();
res.status(201).json(newUser);
} catch (err) {
res.status(400).json({ message: err.message });
}
});

// @route PUT /users/:id
// @desc Update a user
// 更新用户
router.put('/:id', getUser, async (req, res) => {
if (req.body.user_name != null) {
res.user.user_name = req.body.user_name;
}
if (req.body.user_psw != null) {
res.user.user_psw = req.body.user_psw;
}
if (req.body.user_email != null) {
res.user.user_email = req.body.user_email;
}

try {
const updatedUser = await res.user.save();
res.json(updatedUser);
} catch (err) {
res.status(400).json({ message: err.message });
}
});

// @route DELETE /users/:id
// @desc Delete a user
// 删除用户
router.delete('/:id', getUser, async (req, res) => {
try {
await res.user.destroy();
res.json({ message: 'User deleted' });
} catch (err) {
res.status(500).json({ message: err.message });
}
});

// Middleware function to get user by ID
// 通过ID获取用户的中间件函数
async function getUser(req, res, next) {
let user;
try {
user = await User.findByPk(req.params.id);
if (user == null) {
return res.status(404).json({ message: 'Cannot find user' });
}
} catch (err) {
return res.status(500).json({ message: err.message });
}

res.user = user;
next();
}

module.exports = router;