Express RESTful API — 图书管理 CRUD
目标
15 分钟搭建完整的 RESTful API,包含 GET/POST/PUT/DELETE 操作,数据存内存(毕设可替换为数据库)。
完整代码
const express = require('express');
const app = express();
app.use(express.json());
let books = [
{ id: 1, title: 'Node.js 实战', author: 'Mike Cantelon', year: 2021 },
{ id: 2, title: 'JavaScript 高级程序设计', author: 'Matt Frisbie', year: 2023 },
];
let nextId = 3;
app.use((req, res, next) => {
console.log(`[${new Date().toLocaleTimeString()}] ${req.method} ${req.url}`);
next();
});
app.get('/books', (req, res) => {
const { q } = req.query;
if (q) {
const filtered = books.filter(b =>
b.title.includes(q) || b.author.includes(q)
);
return res.json({ count: filtered.length, data: filtered });
}
res.json({ count: books.length, data: books });
});
app.get('/books/:id', (req, res) => {
const book = books.find(b => b.id === parseInt(req.params.id));
if (!book) return res.status(404).json({ error: '图书不存在' });
res.json(book);
});
app.post('/books', (req, res) => {
const { title, author, year } = req.body;
if (!title || !author) {
return res.status(400).json({ error: '标题和作者为必填' });
}
const book = { id: nextId++, title, author, year: year || new Date().getFullYear() };
books.push(book);
res.status(201).json(book);
});
app.put('/books/:id', (req, res) => {
const book = books.find(b => b.id === parseInt(req.params.id));
if (!book) return res.status(404).json({ error: '图书不存在' });
Object.assign(book, req.body);
res.json(book);
});
app.delete('/books/:id', (req, res) => {
const index = books.findIndex(b => b.id === parseInt(req.params.id));
if (index === -1) return res.status(404).json({ error: '图书不存在' });
books.splice(index, 1);
res.json({ message: '已删除' });
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`📚 图书 API: http://localhost:${PORT}/books`));
运行与测试
npm install express
node app.js
curl http:
curl http:
curl -X POST http:
-H "Content-Type: application/json" \
-d '{"title":"Go语言编程","author":"许式伟"}'
curl -X PUT http:
-H "Content-Type: application/json" \
-d '{"year":2024}'
curl -X DELETE http:
curl "http://localhost:3000/books?q=Node"
预期输出
{"count":2,"data":[{"id":1,"title":"Node.js 实战",...},{"id":2,...}]}
{"id":3,"title":"Go语言编程","author":"许式伟","year":2024}
Express 文件上传 + 静态文件服务
目标
使用 Multer 处理单文件/多文件上传,同时提供静态文件访问,是毕设中「头像上传」「文件提交」的标配方案。
完整代码
const express = require('express');
const multer = require('multer');
const path = require('path');
const crypto = require('crypto');
const app = express();
const storage = multer.diskStorage({
destination: (req, file, cb) => cb(null, 'uploads/'),
filename: (req, file, cb) => {
const ext = path.extname(file.originalname);
const name = crypto.randomBytes(16).toString('hex');
cb(null, `${name}${ext}`);
},
});
const fileFilter = (req, file, cb) => {
const allowed = ['image/jpeg', 'image/png', 'image/gif', 'application/pdf'];
if (allowed.includes(file.mimetype)) {
cb(null, true);
} else {
cb(new Error('仅支持 JPG/PNG/GIF/PDF 文件'), false);
}
};
const upload = multer({ storage, fileFilter, limits: { fileSize: 5 * 1024 * 1024 } });
app.use('/uploads', express.static('uploads'));
app.use(express.static('public'));
app.post('/api/upload/avatar', upload.single('avatar'), (req, res) => {
if (!req.file) return res.status(400).json({ error: '请选择文件' });
res.json({
message: '上传成功',
url: `/uploads/${req.file.filename}`,
originalName: req.file.originalname,
size: req.file.size,
});
});
app.post('/api/upload/attachments', upload.array('files', 10), (req, res) => {
if (!req.files || req.files.length === 0) {
return res.status(400).json({ error: '请选择文件' });
}
const uploaded = req.files.map(f => ({
url: `/uploads/${f.filename}`,
name: f.originalname,
size: f.size,
}));
res.json({ message: `成功上传 ${uploaded.length} 个文件`, files: uploaded });
});
app.use((err, req, res, next) => {
if (err instanceof multer.MulterError) {
if (err.code === 'LIMIT_FILE_SIZE') {
return res.status(400).json({ error: '文件大小不能超过 5MB' });
}
return res.status(400).json({ error: err.message });
}
res.status(500).json({ error: err.message });
});
app.listen(3000, () => console.log('📁 http://localhost:3000'));
配套 HTML 测试页面
<!-- public/index.html -->
<h2>头像上传</h2>
<form id="avatarForm">
<input type="file" name="avatar" accept="image/*">
<button type="submit">上传头像</button>
</form>
<img id="preview" src="" style="max-width:200px;display:none;">
<h2>附件上传(多选)</h2>
<form id="filesForm">
<input type="file" name="files" multiple>
<button type="submit">上传附件</button>
</form>
<div id="fileList"></div>
<script>
document.getElementById('avatarForm').addEventListener('submit', async (e) => {
e.preventDefault();
const fd = new FormData(e.target);
const res = await fetch('/api/upload/avatar', { method: 'POST', body: fd });
const data = await res.json();
document.getElementById('preview').src = data.url;
document.getElementById('preview').style.display = 'block';
});
document.getElementById('filesForm').addEventListener('submit', async (e) => {
e.preventDefault();
const fd = new FormData(e.target);
const res = await fetch('/api/upload/attachments', { method: 'POST', body: fd });
const data = await res.json();
document.getElementById('fileList').innerHTML = data.files
.map(f => `<p><a href="${f.url}">${f.name}</a> (${(f.size/1024).toFixed(1)}KB)</p>`)
.join('');
});
</script>
运行
mkdir uploads public
npm install express multer
node app.js