详细教程:从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}`);
    });
});