前言
博客系统是一个非常常见的应用程序,很多人想要自己搭建一个自己的博客系统,但是一开头就被各种技术坑了。在本文中,我们将介绍如何用 Fastify 来搭建一个博客系统,使得阅读和管理博客变得更加简单易用。本文对前端初学者友好,同时也有一些深度内容供有经验的开发者参考。
准备工作
在搭建博客系统之前,我们需要准备一些基础的东西。首先,需要安装 Node.js 和 npm。如果你的电脑上还没有 Node.js,请到 Node.js 官网 下载并安装。
其次,我们需要一个基础的博客系统框架。在本文中,我们将使用 Fastify 来搭建博客系统。以下是安装 Fastify 和相关插件的命令:
npm install fastify fastify-static fastify-multipart
在我们开始编写代码之前,我们还需要规划一下博客系统的基础架构和功能。我们将设计一个包含以下功能的博客系统:
- 首页显示最新的 10 篇博文
- 阅读博文,包括评论功能
- 写博文
- 登录和注册
- 管理员可以删除博文和评论
编写基础路由
首先,我们需要编写基础的路由代码。路由是将 URL 和请求方法映射到应用程序中的逻辑的过程。在我们的博客系统中,我们将使用以下路由:
/
显示最新的 10 篇博文/:id
显示一个特定的博文/login
显示登录页面/register
显示注册页面/logout
退出登录/write
显示写博文页面/api/posts
获取所有博文列表/api/posts/:id
获取一个特定的博文/api/posts/new
新建博文/api/posts/:id/delete
删除一个特定的博文/api/posts/:id/comments
获取一个特定博文的评论列表/api/posts/:id/comments/new
在一个特定博文下发表一条评论/api/posts/:id/comments/:commentId/delete
删除一个特定博文下的一条评论
现在我们可以开始编写代码了。以下是路由的代码:
-- -------------------- ---- ------- ----- ------- - -------------------- ------- ---- --- ----- ---- - ---------------- -- -- -------------- ----------- ------------------------------------------- - ----- -------------------- ---------- ------- ---- --- -- -- ----------------- ------------ ----------------------------------------------- -- ------- -- --- ---------------- ----- --------- ------ -- - ------------------------ ----------------- --- -- --------- ------------------- ----- --------- ------ -- - ------------------------ ---------------- ----------------------- --- -- ---- --------------------- ----- --------- ------ -- - ------------------------ ----------------- --- -- ---- ------------------------ ----- --------- ------ -- - ------------------------ ----------------- --- -- ---- ---------------------- ----- --------- ------ -- - -------------------- --- -- ----- --------------------- ----- --------- ------ -- - ------------------------ ------------------ --- -------------------- ----- -- - -- ----- - ----------------------- ---------------- - ------------------------ --------- -- ----------------------------------- ---
在上面的代码中,我们使用 fastify.get
方法定义路由。这里的 async
以及使用 await
是为了处理异步操作。
我们还注册了两个插件:fastify-static
用于返回静态文件(我们将在后面的部分中使用它)和 fastify-multipart
用于接收上传的文件。
除了 /
之外的所有路由都在显示页面,因此我们都以 reply.send
方法来返回文本响应。 /
的响应将在之后的代码中使用模板引擎生成。
编写模板引擎
模板引擎可以让我们将数据和 HTML 组合在一起,生成响应页面。在本文中,我们将使用 EJS 作为我们的模板引擎。以下是如何使用 EJS 编写模板引擎的代码:
fastify.register(require('point-of-view'), { engine: { ejs: require('ejs') }, includeViewExtension: true, templates: path.join(__dirname, 'views') });
我们使用 fastify.register
方法注册了一个 point-of-view
插件,这个插件用于支持模板引擎。我们将模板和模板引擎的文件放在了 views
目录下,并将其传递给了 templates
选项。
现在我们需要编写我们的模板。以下是我们的基本结构:
-- -------------------- ---- ------- --------- ----- ------ ------ ----------------- ------- ------ ---- ------------ ----------- -- -- ------ - -- -- --------------------- -- ----------------------- -- - ---- - -- -- -------------------- -- ----------------------- -- - -- ------ ---- ------------- --- ------- -- ------ ---- ------------ --------- ---- ------ ------ ------- -------
在这个模板中,我们显示了一个网站标题,一个导航栏和一个页脚。 <%=
用于显示变量, <%
用于显示 JavaScript 代码。
现在我们将使用这个模板来生成首页:
-- -------------------- ---- ------- -- ------- -- --- ---------------- ----- --------- ------ -- - ------------------------ ----- ----- - - -- ----- ------- -- -- ----- ---- - - -- ----- ------ -- -- ----- ------- - - ---- -- --- ------ ---- -- ------ - -- ---- -- --------- -------- ------- ---------- ------ ----- -- - -- ----- -- ----- --------- - --- - ---- ----- ---- - ----- -------------------------- - ----- -------- --------- --- ----------------- ---
在这个代码中,我们使用 reply.view
方法来生成 HTML 响应。它接受两个参数:模板的文件名和传递给模板的数据。在这里,我们使用硬编码的数据,但是我们将在下面的部分中添加更多的功能。
数据库和模型
现在我们需要将这个博客系统连接到一个数据库中。我们将使用 MongoDB 作为我们的数据库。以下是连接数据库的代码:
-- -------------------- ---- ------- ----- -------- - -------------------- ----- -- - -------------------- -------------------------------------------- - ---------------- ----- ------------------- ----- --- --------------- -- -- - -------------------- ------- ---
这里我们使用了 Mongoose 来连接 MongoDB 数据库。我们也注册了一个 open
事件监听器,这个监听器会在数据库连接成功后打印信息。
现在我们需要编写一个模型来管理我们的博客和评论。以下是博客和评论的模型代码:
-- -------------------- ---- ------- ----- -------- - -------------------- ----- - ------ - - --------- ----- ------------- - --- -------- ----- ------- ------- - ----- ---------------------- ---- ------- -- ---------- - ----- ----- -------- -------- -- --- ----- ---------- - --- -------- ------ ------- -------- ------- ------- - ----- ---------------------- ---- ------- -- ---------- - ----- ----- -------- -------- -- --------- - ----- ----- -------- -------- -- --------- ---------------- --- ----- ---- - ---------------------- ------------ ----- ------- - ------------------------- --------------- -------------- - - ----- ------- --
在这个代码中,我们使用了 Mongoose 的 Schema
来定义数据结构。我们为文章和评论定义了自己的模型。文章包含标题、内容、作者和评论,评论包含文本、作者和时间戳。我们可以使用这些模型来读取和写入数据。
现在我们需要使用这些模型来实现我们的 API。首先,让我们添加 “创建一篇新文章” 的路由:
-- -------------------- ---- ------- ----- - ---- - - -------------------- -- ---- ------------------------------ ----- --------- ------ -- - ----- - ------ ------- - - ------------- ----- ---- - --- ------ ------ ------- --- ----- ------------ ------------ ------- ----- ---- --- ---
在这个代码中,我们从请求体中读取了标题和内容,并在数据库中创建了一个新的文章。它的响应有 status: 'ok'
和新创建的 post
对象。
接下来,我们添加 “获取所有博客文章” 的路由:
// 获取所有博文列表 fastify.get('/api/posts', async (request, reply) => { const posts = await Post.find(); reply.send({ status: 'ok', posts }); });
这里我们通过 Post.find()
方法从数据库中获取所有的博文。
现在我们要更新首页路由,使其从数据库中获取最新的 10 篇博文:
-- -------------------- ---- ------- -- ------- -- --- ---------------- ----- --------- ------ -- - ------------------------ ----- ----- - ----- ------------------ ---------- ------ ------------- ----- ---- - - -- ----- ------ -- -- ----- ------- - - ---- -- --- ------ ---- -- ------ - -- ---- -- ---------- ------- ------- ---------- ------ ----- -- - -- ----- -- ----- --------- - --- - ---- ----- ---- - ----- -------------------------- - ----- -------- --------- --- ----------------- ---
在这里,我们使用 Post.find()
方法获取所有博文,然后使用 sort
方法根据创建时间降序排序,接着使用 limit
方法限制只显示最新的 10 个博文。最后,我们将博文传递到 HTML 模板中。
接下来,我们添加 “获取一个特定的博文” 的路由:
// 获取特定博文 fastify.get('/api/posts/:id', async (request, reply) => { const post = await Post.findById(request.params.id); reply.send({ status: 'ok', post }); });
这个路由将根据请求参数返回一个特定的博文。现在我们要更新博客文章页面,这样它可以从数据库中获取数据并显示评论:
-- -------------------- ---- ------- -- --------- ------------------- ----- --------- ------ -- - ------------------------ ----- - -- - - --------------- ----- ---- - ----- ---------------------------------------------- ----- ---- - - -- ----- ------ -- -- --- ------------ - --- --- ------ ------- -- -------------- - ------------ -- - ---- ---------------- ---------------------- -- ------------------------------------------------- ------ -- - ----- ------- - - ---------------------- ---------------------- --------------- -- ----- --------- - -------------- - ---- ----- ---- - ----- -------------------------- - ----- -------- --------- --- ----------------- ---
在这里,我们使用 populate('comments.author')
函数获取文章的评论列表和评论作者的详细信息,并将其传递到 HTML 模板中。
现在我们要添加 “在一个特定的博客下发布评论” 的路由:
-- -------------------- ---- ------- -- ---- ------------------------------------------- ----- --------- ------ -- - ----- - -- - - --------------- ----- - ---- - - ------------- ----- ------ - ---------------- -- --------- ----- ------- - - ----- ------ -- ----- ---- - ----- ------------------ ---------------------------- ----- ------------ ------------ ------- ----- ------- --- ---
这个路由将在一个特定的博客文章下发布新的评论。我们还需要更新 /api/posts/:id
路由来返回评论:
// 获取特定博文 fastify.get('/api/posts/:id', async (request, reply) => { const { id } = request.params; const post = await Post.findById(id).populate('comments.author'); reply.send({ status: 'ok', post }); });
现在我们要添加 “删除一个特定的博文” 的路由:
// 删除一个特定的博文 fastify.delete('/api/posts/:id/delete', async (request, reply) => { const { id } = request.params; await Post.findByIdAndDelete(id); reply.send({ status: 'ok' }); });
我们还需要保护路由,只有管理员才能删除博文。
登录和授权
现在我们需要添加登录和授权功能。我们将使用 Passport.js 来处理登录和授权。以下是安装 Passport.js 的命令:
npm install passport passport-local
接下来,我们需要定义一个本地策略来验证凭据:
-- -------------------- ---- ------- ----- -------- - -------------------- ----- ------------- - ----------------------------------- ---------------- ------------------- ---------- --------- ----- -- - ----- ---- - - -- ----- ------- -- -- -- ------- - ------ ---------- ------ - -------- ---------- --- - -- ------------------------------ --------------- - ------ ---------- ------ - -------- ---------- --- - ------ ---------- ------ ----
在这里,我们定义了一个本地策略来验证用户凭据。具体来说,我们获取了 username
和 password
,从数据库中获取了用户信息,并将其与提供的凭据进行比较。如果验证成功,我们将调用 done(null, user)
,这将在后续的路由中提供认证信息。
现在我们需要序列化和反序列化用户信息:
passport.serializeUser((user, done) => { done(null, user.id); }); passport.deserializeUser((id, done) => { const user = { /* todo: 从数据库中获取 */ }; done(null, user); });
这个过程定义了在储存和提取用户信息时用于序列化和反序列化用户的唯一 ID。
在最后,我们添加登录和注册路由:
-- -------------------- ---- ------- ----- ------ - -------------------- ----- ---------- - --- -- ----- -------------------------------------------- -------------------------------------------- - ------- -- ------ ---- ------- ------ -- -- ------------ ------- - ------- ----- -- --- ----------------------------------- -------------------------------- ------------- --------- ------------------------------ - ---------------- ---- ---------------- --------- ------------- ----- -- -- ------------------------- ----- --------- ------ -- - ----- - --------- -------- - - ------------- ----- ---- - - --------- --------- ------------------------- ----------- -- -- ----- --------- -- ------------------- ----- -- - -- ----- ----- ---- -------------------- --- --- ---------------------- ----- --------- ------ -- - ----------------- -------------------- ---
在这里,我们注册了 fastify-cookie
和 fastify-session
插件,这两个插件分别用于读取和保存客户端的 Cookie 和 Redis 中的 Session。我们使用 passport.authenticate
方法来验证用户,如果验证通过,则将用户身份信息存储在会话中,否则将会看到一个错误信息。我们还添加了注册路由,并在登录后使用 request.login
方法来将用户信息保存在会话中。
现在我们的博客系统基本上已经完成了。除了上面提到的保护路由外,你还可以自己添加其他功能,比如文章更新等等,这不在本文讨论范围之内。
结论
在本文中,我们介绍了如何用 Fastify、MongoDB 和 Passport.js 来搭建一个博客系统。我们从安装必要的工具开始,之后编写了基础路由和模板引擎代码,然后重点讲解了数据库和模型,接着我们添加了登录和授权功能。最后我们介绍了如何保护路由和添加更多功能。希望这篇文章对你有所帮助,也欢迎读者在评论区留言。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/676fa01ae9a7045d0d74f453