详细教程:从0到1使用NodeJS编写后端接口的实战案例demo
一、项目简介
本项目利用Node.js与Express框架搭建一个后端API,实现用户管理、商品管理等基础功能。数据存储使用MySQL数据库。
二、开发环境准备
确保Node.js、MySQL已安装,然后通过npm安装必要的库:
npm install express mysql body-parser multer nodemon silly-datetime
三、后端代码实现
1. 入口文件 — index.js
设置基本的服务器和中间件配置。
const express = require('express');
const bodyParser = require('body-parser');
const apiRouter = require('./routes/myApi');
const app = express();
app.use(bodyParser.json());
app.use('/api', apiRouter);
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
2. 数据库连接配置文件 — db.js
配置MySQL连接。
const mysql = require('mysql');
const connection = mysql.createConnection({
host: 'localhost',
user: 'root', // 修改为你的用户名
password: 'password', // 修改为你的密码
database: 'myDatabase' // 确保数据库已创建
});
connection.connect(error => {
if (error) throw error;
console.log('Successfully connected to the database.');
});
module.exports = connection;
3. API路由配置文件 — myApi.js
路由文件连接各个控制器。
const express = require('express');
const userController = require('../controllers/userController');
const productController = require('../controllers/productController');
const router = express.Router();
router.post('/register', userController.register);
router.post('/login', userController.login);
router.get('/products', productController.list);
module.exports = router;
四、各模块功能的后端代码
1. 用户注册和登录退出
使用基础的SQL命令插入数据和验证用户。
// controllers/userController.js
const db = require('../db');
exports.register = function(req, res) {
const { username, password } = req.body;
db.query('INSERT INTO users (username, password) VALUES (?, ?)', [username, password], (error, results) => {
if (error) {
res.status(500).send({ message: "Error while registering", error });
} else {
res.status(201).send({ message: "Registered successfully" });
}
});
};
exports.login = function(req, res) {
const { username, password } = req.body;
db.query('SELECT * FROM users WHERE username = ? AND password = ?', [username, password], (error, results) => {
if (error) {
res.status(500).send({ message: "Error while logging in", error });
} else if (results.length > 0) {
res.status(200).send({ message: "Logged in successfully" });
} else {
res.status(404).send({ message: "User not found" });
}
});
};
2. 修改用户信息
提供用户信息更新接口,例如更新电子邮件。
// 更新用户信息
exports.updateProfile = function(req, res) {
const { userId, email } = req.body;
db.query('UPDATE users SET email = ? WHERE id = ?', [email, userId], (error, results) => {
if (error) {
res.status(500).send({ message: "Error updating user profile", error });
} else {
res.status(200).send({ message: "Profile updated successfully" });
}
});
};
3. 用户头像上传和更新
使用multer
处理文件上传。
const multer = require('multer');
const db = require('../db');
const storage = multer.diskStorage({
destination: (req, file, cb) => cb(null, './uploads/'),
filename: (req, file, cb) => cb(null, file.fieldname + '-' + Date.now() + path.extname(file.originalname))
});
const upload = multer({ storage: storage }).single('avatar');
exports.updateAvatar = function(req, res) {
upload(req, res, (error) => {
if (error) {
res.status(500).send({ message: "Error uploading file", error });
} else {
const userId = req.body.userId;
const avatarPath = req.file.path;
db.query('UPDATE users SET avatar = ? WHERE id = ?', [avatarPath, userId], (error, results) => {
if (error) {
res.status(500).send({ message: "Error updating avatar", error });
} else {
res.status(200).send({ message: "Avatar updated successfully" });
}
});
}
});
};
4. 用户密码的修改
允许用户更新其密码。
exports.changePassword = function(req, res) {
const { userId, oldPassword, newPassword } = req.body;
db.query('SELECT password FROM users WHERE id = ?', [userId], (error, results) => {
if (error) {
res.status(500).send({ message: "Database error", error });
return;
}
if (results[0].password !== oldPassword) {
res.status(401).send({ message: "Old password does not match" });
return;
}
db.query('UPDATE users SET password = ? WHERE id = ?', [newPassword, userId], (error, results) => {
if (error) {
res.status(500).send({ message: "Error updating password", error });
} else {
res.status(200).send({ message: "Password updated successfully" });
}
});
});
};
5. 商品列表的查看和搜索
实现商品列表查询,包括基本搜索功能。
exports.list = function(req, res) {
const { search } = req.query;
let query = 'SELECT * FROM products';
if (search) {
query += ' WHERE name LIKE ?';
search = `%${search}%`;
}
db.query(query, [search], (error, results) => {
if (error) {
res.status(500).send({ message: "Error retrieving products", error });
} else {
res.status(200).send(results);
}
});
};
6. 商品详情查看
提供商品详情页面。
exports.details = function(req, res) {
const { productId } = req.params;
db.query('SELECT * FROM products WHERE id = ?', [productId], (error, results) => {
if (error) {
res.status(500).send({ message: "Error fetching product details", error });
} else if (results.length > 0) {
res.status(200).send(results[0]);
} else {
res.status(404).send({ message: "Product not found" });
}
});
};
7. 添加商品和商品图片上传
允许用户添加商品信息和上传商品图片。
const uploadProductImage = multer({ storage: storage }).single('productImage');
exports.addProduct = function(req, res) {
uploadProductImage(req, res, function(error) {
if (error) {
res.status(500).send({ message: "Error uploading product image", error });
return;
}
const { name, description, price } = req.body;
const imageUrl = req.file.path;
db.query('INSERT INTO products (name, description, price, imageUrl) VALUES (?, ?, ?, ?)', [name, description, price, imageUrl], (error, results) => {
if (error) {
res.status(500).send({ message: "Error adding new product", error });
} else {
res.status(201).send({ message: "Product added successfully" });
}
});
});
};
8. 商品评论的查看和添加
实现查看和添加评论的功能。
exports.addComment = function(req, res) {
const { productId, userId, comment } = req.body;
db.query('INSERT INTO comments (productId, userId, comment) VALUES (?, ?, ?)', [productId, userId, comment], (error, results) => {
if (error) {
res.status(500).send({ message: "Error adding comment", error });
} else {
res.status(201).send({ message: "Comment added successfully" });
}
});
};
9. 购物车列表
查看当前用户的购物车列表。
exports.cartList = function(req, res) {
const { userId } = req.body;
db.query('SELECT p.*, c.quantity FROM cart c JOIN products p ON c.productId = p.id WHERE c.userId = ?', [userId], (error, results) => {
if (error) {
res.status(500).send({ message: "Error retrieving cart items", error });
} else {
res.status(200).send(results);
}
});
};
10. 添加、删除购物车
允许
用户添加或删除购物车中的商品。
exports.addToCart = function(req, res) {
const { userId, productId, quantity } = req.body;
db.query('INSERT INTO cart (userId, productId, quantity) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE quantity = quantity + ?', [userId, productId, quantity, quantity], (error, results) => {
if (error) {
res.status(500).send({ message: "Error adding to cart", error });
} else {
res.status(201).send({ message: "Added to cart successfully" });
}
});
};
exports.removeFromCart = function(req, res) {
const { userId, productId } = req.body;
db.query('DELETE FROM cart WHERE userId = ? AND productId = ?', [userId, productId], (error, results) => {
if (error) {
res.status(500).send({ message: "Error removing from cart", error });
} else {
res.status(200).send({ message: "Removed from cart successfully" });
}
});
};
11. 用户商品收藏列表
提供查看用户收藏的商品列表的功能。
exports.favoritesList = function(req, res) {
const { userId } = req.body;
db.query('SELECT p.* FROM favorites f JOIN products p ON f.productId = p.id WHERE f.userId = ?', [userId], (error, results) => {
if (error) {
res.status(500).send({ message: "Error retrieving favorite products", error });
} else {
res.status(200).send(results);
}
});
};
12. 用户商品的收藏和取消
允许用户添加或取消收藏商品。
exports.addFavorite = function(req, res) {
const { userId, productId } = req.body;
db.query('INSERT INTO favorites (userId, productId) VALUES (?, ?)', [userId, productId], (error, results) => {
if (error) {
res.status(500).send({ message: "Error adding to favorites", error });
} else {
res.status(201).send({ message: "Added to favorites successfully" });
}
});
};
exports.removeFavorite = function(req, res) {
const { userId, productId } = req.body;
db.query('DELETE FROM favorites WHERE userId = ? AND productId = ?', [userId, productId], (error, results) => {
if (error) {
res.status(500).send({ message: "Error removing from favorites", error });
} else {
res.status(200).send({ message: "Removed from favorites successfully" });
}
});
};
13. 用户的添加商品列表
查询用户自己添加的商品列表。
exports.userAddedProducts = function(req, res) {
const { userId } = req.body;
db.query('SELECT * FROM products WHERE userId = ?', [userId], (error, results) => {
if (error) {
res.status(500).send({ message: "Error retrieving user's products", error });
} else {
res.status(200).send(results);
}
});
};
14. 用户的添加商品的删除
允许用户删除他们自己添加的商品。
exports.deleteProduct = function(req, res) {
const { userId, productId } = req.body;
db.query('DELETE FROM products WHERE id = ? AND userId = ?', [productId, userId], (error, results) => {
if (error) {
res.status(500).send({ message: "Error deleting product", error });
} else {
res.status(200).send({ message: "Product deleted successfully" });
}
});
};
五、前端代码
提供前端代码的基础配置。
1. 配置文件(主要是后端接口请求地址要配置代理)
适用于前端项目(如React或Vue)进行跨域请求的配置。
// 在React项目的package.json中添加,用于代理API请求
"proxy": "http://localhost:3000"
六、运行项目
详细描述如何运行前后端代码。
1. 前端代码运行命令(参考vue项目)
npm run serve
2. 后端代码启动命令
利用Nodemon运行Node.js应用,实现热更新。
nodemon index.js
七、静态文件
在Express应用中处理静态文件是非常直接的。你可以设置一个或多个目录作为静态资源的容器,Express将自动为这些文件提供服务。
配置静态文件目录
可以通过Express的express.static
中间件来设定静态资源文件夹,通常这是非常有用的,例如存放图片、JavaScript文件或CSS文件。
const express = require('express');
const app = express();
app.use('/static', express.static('public'));
app.listen(3000, () => {
console.log('Server started on port 3000');
});
在上面的例子中,所有public
目录下的文件现在都可以通过/static
这个路径访问。
八、文件上传
文件上传通常需要处理各种类型的数据,特别是大型文件或敏感数据。以下是如何使用Node.js和multer
库来设置文件上传的示例。
基本文件上传设置
我们之前已经看到了如何设置multer
来处理文件上传。这里我们更深入地讨论如何整合它到Express应用中。
单文件上传
这是一个简单的例子,展示如何设置一个API端点来接受单个文件上传。
const express = require('express');
const multer = require('multer');
const app = express();
const storage = multer.diskStorage({
destination: function(req, file, cb) {
cb(null, './uploads');
},
filename: function(req, file, cb) {
cb(null, file.fieldname + '-' + Date.now());
}
});
const upload = multer({ storage: storage });
app.post('/upload', upload.single('file'), (req, res) => {
res.send('File uploaded successfully.');
});
app.listen(3000, () => {
console.log('App running on port 3000');
});
多文件上传
对于需要上传多个文件的情况,你可以使用upload.array()
方法。
app.post('/uploads', upload.array('files', 5), (req, res) => {
res.send(`${req.files.length} files uploaded successfully.`);
});
九、multer
multer
是一个Node.js中间件,用于处理multipart/form-data
,主要用于上传文件。下面详细说明如何使用multer
及其配置选项。
1. 安装
首先确保已安装multer
。
npm install multer
2. 使用
配置multer
以处理文件上传。以下是基础配置:
const multer = require('multer');
const storage = multer.diskStorage({
destination: function(req, file, cb) {
cb(null, './uploads/');
},
filename: function(req, file, cb) {
cb(null, `${file.fieldname}-${Date.now()}${path.extname(file.originalname)}`);
}
});
const upload = multer({ storage: storage });
3. 文件信息
在上传的回调函数中,可以访问req.file
(单个文件)或req.files
(多文件上传)来获取上传文件的详细信息,如下所示:
app.post('/upload', upload.single('file'), (req, res) => {
console.log(req.file); // 输出文件详细信息
res.send('File uploaded successfully');
});
4. Multer(options)
可以在multer
配置中设置多个选项,例如限制文件大小、设置文件数量等。
.single(fieldname)
接受一个以fieldname
命名的文件。这些文件的信息保存在req.file
。
app.post('/profile', upload.single('avatar'), function (req, res) {
res.send('Avatar uploaded!');
});
.array(fieldname[, maxCount])
接受一个以fieldname
命名的文件数组,可选参数maxCount
设置最大文件数量。
app.post('/gallery', upload.array('photos', 4), function (req, res) {
res.send('Photos uploaded!');
});
.fields(fields)
接受指定字段名的混合文件。
app.post('/post', upload.fields([{ name: 'avatar', maxCount: 1 }, { name: 'gallery', maxCount: 8 }]), function (req, res) {
res.send('Files uploaded!');
});
.none()
拒绝文件,只接受文本字段。
app.post('/data', upload.none(), function (req, res) {
res.send('Only data submitted!');
});
.any()
接受所有文件。
app.post('/upload', upload.any(), function (req, res) {
res.send('Any file accepted!');
});
5. Storage
如上所示,可以配置磁盘存储或内存存储。
6. limits
限制上传的文件大小、数量等。
const upload = multer({
storage: storage,
limits: { fileSize: 1000000 } // 文件大小限制1MB
});
7. fileFilter
允许过滤上传的文件类型,例如只接受图片文件:
const fileFilter = (req, file, cb) => {
if (file.mimetype.startsWith('image/')) {
cb(null, true);
} else {
cb(new Error('Not an image! Please upload only images.'), false);
}
};
const upload = multer({ storage: storage, fileFilter: fileFilter });
8. 错误处理机制
在multer
中处理错误通常涉及使用错误回调函数:
app.post('/upload', function (req, res) {
upload(req, res, function (err) {
if (err instanceof multer.MulterError) {
return res.status(500).json(err);
} else if (err) {
return res.status(500).json(err);
}
res.send(`Upload successful: ${req.file.path}`);
});
});