通过 ObjectId 查找
本课属于 Coddy Node.js 学习旅程的“使用 MongoDB 的数据库”单元。第 7 课,共 29 课。
要查找一本书,服务器会根据 _id 查找,但 URL 中的 id 是文本。
MongoDB 创建的 _id 是一个ObjectId:一个 12 字节的值,以 24 个十六进制字符表示,包括数字 0 到 9 和字母 a 到 f。JSON 会将它显示为字符串,但它在数据库中并不是字符串,所以使用纯文本的筛选条件不会匹配到任何内容。先将文本转换为 ObjectId。ObjectId.isValid 用来判断文本是否可以转换为 ObjectId,因为 new ObjectId 遇到其他内容时会抛出异常。
import { ObjectId } from 'mongodb';
const text = '66f1c2a9e4b0a1b2c3d4e5f6';
await orders.findOne({ _id: new ObjectId(text) }); // 订单,或 null
await orders.findOne({ _id: text }); // null:text 不是 ObjectId
ObjectId.isValid('42'); // falsefindOne 会返回第一个匹配的文档;如果没有匹配项,则返回 null。
挑战
简单这一步会打开课程准备好的书架 API 项目,而不是你在插入文档课中的项目文件(包括 server.js)。你之前的成果仍保留在原来的课中。编辑 server.js 前,先阅读提供的文件。这份项目副本包含书架列表和插入路由。
客户端想要一本书,并根据它的 _id 来选择。
- 在
server.js中,添加一个GET /books/:id路由。 - URL 中的 id 是文本。有些文本不可能是 ObjectId,ObjectId 由 24 个字符组成,只能包含数字和字母 a 到 f。对于这类文本,使用
sendError返回状态码 400 和错误,例如{ "error": "Not a valid id" }。要在创建 ObjectId 之前进行检查,因为用不正确的文本创建 ObjectId 会抛出错误。 - 否则,根据
_id查找这本书。找到后,返回状态码 200 和这本书:{ "_id": "6720a1f4c2b9e81d3a5f0c11", "title": "Dune", "author": "Frank Herbert", "year": 1965, "isbn": "9780441013593", "genre": "scifi" }。 - 如果没有任何书的
_id与之匹配,就返回状态码 404 和错误,例如{ "error": "No such book" }。试试看:发送GET /books,并复制 Dune 的_id:也就是"_id":后面的 24 个字符。每次点击“运行”,书籍都会获得新的 id,所以每次运行后都要重新复制。发送GET /books/,后面接上你复制的 id:你会得到状态码 200 和 Dune。发送GET /books/42:你会得到状态码 400。发送GET /books/000000000000000000000000:这是有效的 id,但没有任何书使用它,所以你会得到状态码 404。
亲手试试
import express from 'express';
import { MongoClient } from 'mongodb';
const client = new MongoClient('mongodb://localhost:27017');
const db = client.db('shelf');
const books = db.collection('books');
await books.insertMany([
{ title: 'Dune', author: 'Frank Herbert', year: 1965, isbn: '9780441013593', genre: 'scifi' },
{ title: 'Kindred', author: 'Octavia E. Butler', year: 1979, isbn: '9780807083697', genre: 'scifi' },
{ title: 'Emma', author: 'Jane Austen', year: 1815, isbn: '9780141439587', genre: 'classic' },
]);
const app = express();
app.use(express.json());
// 所有错误都返回 { error: message }。
function sendError(res, status, message) {
res.status(status).json({ error: message });
}
app.get('/', (req, res) => {
res.send('Bookshelf API');
});
app.get('/books', async (req, res) => {
const list = await books.find().toArray();
res.json(list);
});
app.post('/books', async (req, res) => {
const book = req.body;
await books.insertOne(book);
res.status(201).json(book);
});
app.listen(3000, () => {
console.log('Bookshelf is running on port 3000');
});
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
使用 MongoDB 的数据库中的所有课
自己练习: Express 在线编辑器