上篇《博客系统知多少:揭秘那些不为人知的学问(一)》介绍了博客的历史、我的博客故事及博客的受众来源。本篇精彩继续,介绍博客基本功能设计要点。
目录
由于文章篇幅较长,本文将分为 4 篇推送,目录如下:
- “博客”的前世今生
- 我的博客故事
- 谁是博客的受众?
- 博客基本功能设计要点
- 4.1 文章(Post)
- 4.2 评论(Comment)
- 4.3 分类(Category)
- 4.4 标签(Tag)
- 4.5 归档(Archive)
- 4.6 页面(Page)
- 4.7 订阅
- 4.8 版本控制
- 4.9 主题及个性化
- 4.10 用户及权限
- 4.11 插件
- 4.12 图片及附件的处理
- 4.13 脏词过滤及评论审查
- 4.14 静态化
- 4.15 通知系统
- 博客协议或标准
- 5.1 RSS
- 5.2 ATOM
- 5.3 OPML
- 5.4 APML
- 5.5 FOAF
- 5.6 BlogML
- 5.7 Open Search
- 5.8 Pingback
- 5.9 Trackback
- 5.10 MetaWeblog
- 5.11 RSD
- 5.12 阅读器视图
- 设计博客系统有哪些知识点
- 6.1 时区真的全用 UTC?
- 6.2 HTML 还是 Markdown
- 6.3 MVC 还是 SPA
- 6.4 安全
- 结束语
01 文章 (Post)
我们每天可能都会阅读或长或短的 3-5 篇文章。文章是博客系统的核心业务,因此博客文章的内容和质量非常重要。
那么,文章这个业务类型如何起名?数据库的表名和代码的变量名,类型名称用 article 吗?好像上学时候只学过文章叫做 article。其实博客类型的文章,正确的表达是 post,英文单词里 post 和 article 的区别在于,post 只是随心所欲写的文章,而 article 指的是论文那样的经过精心雕琢,旁征博引,并且有可能在学术期刊上发表的文章。因此设计博客系统的时候,尽量避免使用 article 这个单词来命名代码。更具体来讲,post 中可以出现不严谨、口语化的表达方式,例如本文就算是个 post。而 article 讲究言语上的规范,连 “让我们” , “我们来看看” 这种字眼都不能出现。
文章需要具备标题、Slug、创建时间、发布时间、修改时间、摘要和内容等要素,也会包含所属分类、标签、阅读量和点赞量等次要信息。其中 Slug 是博客的特色,它指的是一篇文章的 URL。例如我的文章:《Try the New Azure .NET SDK》,它的 URL 为 https://edi.wang/post/2019/12/5/try-the-new-azure-net-sdk,其中try-the-new-azure-net-sdk即为该文章的 Slug。Slug 讲究的是“人类可读”,一般情况下均为博客标题对应的英文表达,用中划线分割英文单词,Slug 也对博客的 SEO 起到了关键作用。如果你的博客文章用的是数据库 ID、文章标题的 HTML Encoding 等做 URL,请更换为 Slug。特别是遇到中文文章,如果标题被 URL Encoding 了,那么对于 SEO 和链接分享,都是灾难。一个 Slug 一旦定下,尽量不要改动,虽然大部分博客系统都支持修改 Slug,但是对于被搜索引擎收入的文章,改了 Slug 就会导致 404。比较完备的博客系统(如 WordPress)支持采用 301 重定向方式告诉搜索引擎原文地址已变化。
摘要有两个作用,一是用于在列表视图中显示文章信息预览,二是用于 SEO,放在 description 这个 meta 标签中,可以帮助搜索引擎精准定位收录的内容。对于中文内容,需要注意是否输出的 HTML 源代码被 Encoding 过,ASP.NET Core 默认的 Encoding 会对 SEO 造成灾难(我的博客系统因为面向英语用户,不考虑中文支持,所以并不解决这个问题)。
摘要可以自动抓取文章前几百字,也可以像微信公众号那样要求用户手工填写。我的博客采用的是自动取文章前 400 字。结合 SEO 的关系,我的文章通常开头段落就是概要,这样可以让用户在搜索引擎预览页面就能看到准确内容,而不是页面上无关紧要的 UI 元素。
文章的状态通常包括:草稿、发布、回收。用户仅能看到已发布的文章,管理员可在后台更改文章状态。
02 评论(Comment)
评论是博客中作者和读者互动的主要方式。有些博客要求读者登录后才能发表评论,而有些可以允许游客评论(比如我的博客及 WordPress)。登录的好处在于可以识别你的读者,并有效防止垃广告评论。但要求登录也会给用户造成操作上多了一个步骤,嫌麻烦的用户就不会进行评论。
我的博客及 WordPress 默认都设计为需要管理员在后台审核评论后,才能放出显示。这也能有效避免垃圾广告、骚扰信息甚至是一些恶意的煽动性言论。对于提供 Email 地址的用户,管理员也能够在后台回复用户的评论,并由博客系统向用户发送 Email 通知。
对于技术博客,评论可考虑开放 markdown 格式。这是一种在程序员之间格外受欢迎的语法,在 GitHub 得到了广泛应用。
评论需要采用验证码或其他人机验证技术,以防止机器人发广告。但根据经验,验证码并不能 100%阻止垃圾信息,因为现代化的垃圾信息还真的是人组团发的,有专门的公司、团队、微信群等,国外也有。于是,你可能需要考虑关键词过滤,购买三方过滤接口等。
评论也得记得做字数限制,不然也有可能会造成部分用户“灌水”、刷屏的现象。
如果不想自己写功能,还可以整合三方的评论服务,即博客系统本身不实现评论功能,通过三方服务加载外部 JS,在文章阅读页面“注入”一个评论区,通常这要求文章的 URL 不变(WordPress 里叫做永久性 URL)。
03 分类(Category)
像建文件夹一样将文章根据内容进行区分,即为分类。文章分类后,可以帮助读者快速检索同种类的文章。
例如写.NET、PHP、JS 的文章都属于 “开发技术(Development)”这个分类。而技术圈新闻和职场经验分享等文章,则属于 “工作” 分类,分类的划分完全由用户控制。分类可以多对多。例如写一篇文章介绍了用 ASP.NET Core 开发 Angular 应用的文章,可以同时属于 “.NET 技术” 及 “前端开发” 分类。
分类需要一个标题、一个简介,以及一个路由名称。例如我博客,微软云 Azure 的分类,标题为 Microsoft Azure,简介为 The Best Cloud,路由名称为 azure。标题需要同时显示在标题栏,以便于 SEO。简介是对于标题的补充说明,便于用户查看。设计路由名称的原因请参考下一段介绍的标签的设计。
分类的另一个功能就是产生 OPML 及 RSS/Atom 订阅源,这个将在第五章介绍博客协议中讲解。
04 标签(Tag)
一篇文章所提到的话题,即为文章的标签。和分类一样,标签也是多对多关系。标签可以作为检索文章的依据,类似关键词,快速查找相关内容的文章。
标签需要考虑到标签含义重复的情况,例如:VS 和 Visual Studio 是一个意思,VSCode、VSC 和 Visual Studio Code 也是一个意思。那么用户选择标签的时候,最好使用智能提示推荐用户使用已有标签。
对于博客系统设计者来说,还要考虑标签的 URL。如果 URL 用的是标签本身的内容,会导致很多问题。当标签名称为整个英文单词,例如 Excel,并不会发生问题,因为 URL 通常是 https://yourblog/tags/excel。但是如果标签内容为 .NET Core、C#、Robots.txt,事情就变的有意思起来。https://yourblog/tags/robots.txt 到底是在请求 tags 下的 robots.txt 文件还是在请求标签?自己作为博客系统设计者,当然可以从程序上限制所有 tags 接受的路由参数都为标签,好像是解决了问题,但 SEO 和扫描工具可不这么认为,他们有大量 by convention 的规则会认为是请求文件。
对于需要 URL Encoding 的标签内容,更会导致 URL 缺乏可读性,从而影响 SEO。千万不要自作聪明地以为现代的搜索引擎可以处理好 URL Encoding,一个 URL 是否干净对 SEO 的影响很大。特别是当标签是中文内容的时候,如果全 encoding 了,URL 就会非常冗长,甚至影响到 SEO,也影响到博主分享链接。因此,我的博客系统为了处理标签 URL,给每个标签都设计了规范化名称(normalized name),由系统根据标签内容自动产生,如 .NET Core 经过 normalize,会变成 dotnet-core,最终产生的 URL 即 https://edi.wang/tags/list/dotnet-core。
对于用户来说,最容易犯错之一的就是把标签用成搜索关键词。例如用户写了一篇关于 Visual Studio Code 的文章,那么标签可能会同时打 VSCode、VSC 和 Visual Studio Code,但其实只要选择一个标签即可。打太多同样含义的标签会导致读者无法完整检索到所有相关文章,对搜索引擎来说,也是如此。所以如何用好标签,是博客设计者和用户需要共同关注的要点。
标签云(Tag Cloud)是博客中用来列出最热门标签的功能。通常使用更大号字、更明显的颜色来标识出对应文章较多的标签。标签云可以作为博客博主的个性化属性,一眼就能看出博主热衷于什么话题(比如 Windows Phone?0.0)。
05 归档(Archive)
以时间(年、月、日)整理的博客文章即为归档,它和分类的区别在于归档只以时间为标准来划分文章。Archive 的 SEO 相对于文章、分类、标签来说,并不那么关键。所以除了 URL 可以按年月划分以外,并没有额外的讲究。
例如:https://edi.wang/archive/2019/9 表示 2019 年 9 月的文章。https://edi.wang/archive/2019 则表示 2019 年所有的文章。归档功能主要用于给读者按时间查询,看看博主某个时间都在干什么。设计这样的功能可以提高读者对博主的兴趣,也是个人对外形象的一种展示。
06 页面(Page)
页面是博客的可选功能之一,事实上,它更接近于 CMS 的功能。有些内容并不适合以文章的形式发布,比如“关于”页面。这样的页面通常与发布时候的时间无关,内容也经常更新,排版设计也非常自由,不单纯是文字。
页面通常不需要评论、标签和分类等属性,但可以有发布和编辑时间。和文章一样,页面也需要注意 Slug。
在我的博客系统中,页面也选择是否隐藏侧边栏,用户也可以完全编写页面的 HTML 及 CSS 代码,并把页面添加为导航菜单。WordPress 对于页面的处理更加完备,接近于 CMS 系统。
07 订阅(Subscription)
读者订阅博客的主要方式有 Feed(RSS/ATOM)及 Newsletter。Feed 方式本质上是被动订阅,需要客户端软件发起请求给服务器,检查是否有新文章发表,才能显示到客户端里。Newsletter 一般采用 Email 形式主动发送给订阅用户,但这要求博客系统的编写者实现 Email 订阅功能,也要求管理员维护 Email 服务。订阅一般只推送近期发表的新文章,例如前 10、20 篇,而不会每次都推送全部文章导致客户端爆炸。
订阅一般可按文章分类提供,以便于只对某些分类感兴趣的读者阅读。有些博客系统也提供文章评论的订阅源,以便读者观摩吐槽大会。
关于 RSS 及 ATOM 的详细介绍请看 5.1、5.2 章节。
08 版本控制
更接近 CMS 的博客系统通常提供版本控制功能,允许用户回滚文章或页面的历史版本。设计版本控制的时候,不能只考虑往前回滚,得还能再滚得回来。通常,用户每次编辑一篇已经写好的文章,就会产生一个新版本,类似于 git 对于一个文件的 commit。博客的版本控制也类似于代码版本控制,你可以选择保存一篇文章的完整内容作为历史版本,也可以选择每次只保存变化量信息(delta)。保存完整内容不容易后续花费大量时间精力 ,但是会占用较多存储空间。保存内容变化量节省数据库空间,但实现代码容易占用大量精力。
09 主题及个性化
好用的博客系统通常支持主题,毕竟个性化是博客本身应有的特点之一。WordPress 积累了大量的主题库,也允许自制主题。但是我的博客只支持更改主题色,还有很大上升空间。
10 用户及权限
博客系统分为个人、团队及博客平台。个人博客系统一般为单用户(例如我的博客),不需要设计权限、注册等功能。多用户博客则需要实现不同的角色和权限,比如博客管理员、审核专员、撰稿人、评论管理员等等。无论是单用户还是多用户博客,集成一套成熟的三方 RBAC 方案可能是最高效的选择,多数三方方案也都支持 SSO,例如我博客支持的 Azure AD。
11 插件
插件功能可以在不更改博客代码的情况下,按需拓展博客的功能。WordPress 以及 BlogEngine 都支持插件,但 Moonglade 还不行。
12 图片及附件的处理
图片格式
在 2020 年,图片格式已经非常自由,一般的博客 JPG 居多,程序员的博客 PNG 居多(毕竟都是屏幕截图),像微信公众号那样采用 WEBP 格式现在同样可取,只要读者的设备兼容即可。一般 BMP 格式由于体积大会导致网络传输慢,所以不推荐。同样道理,GIF 也要注意限制尺寸。
博客系统输出图片时,需要采用正确的 Mime Type,以保证客户端的兼容性。一般直接输出静态文件本身不需要博客编写者手工处理 Mime Type,但有专门图片处理逻辑的博客(例如我的 Moonglade)则需要留意保留图片原本的 Mime Type。
图片水印
给上传的图片自动加水印有助于保护版权,水印内容一般是博客的地址或博主名字。添加水印时要注意图片尺寸调整水印的比例,以免挡住图中重要内容影响阅读。对于过小的图片,可选择性的忽略水印。
另外,考虑到博客有可能会在发展过程中改名,建议添加水印的时候在系统中保留一份原始图片,以便于后期更新水印内容。
具体方法可参考我的文章《ASP.NET Core 给上传的图片加水印》。
图片存储
图片存哪里是个值得思考的问题。一般有 3 个地方存放:文件系统、数据库、云上的 Blob 存储服务。Moonglade 支持文件系统及 Azure Blob 存储。这三者各有优缺点。
文件系统的优势在于直接 serve static file 速度最快,但如果图片目录本身位于网站目录底下,会导致目录不只读而引起潜在安全问题。比如初中时候很流行的给 DVBBS 上传个改了拓展名的 ASP web shell,尽管给 web 服务器上传可执行文件在 2020 年已经基本绝迹了,但依然存在隐患,就好比就算你家里请了 007 当保镖也是需要夜间锁好门。
数据库存图安全性最高,并且让博客的数据只位于一个位置,方便管理和备份,十几年前很流行这么做,但其实读写图片对数据库有一定开销,并且再由网站输出,双倍开销,一般不推荐。
而云端 Blob 存储服务目前来说是最适合这个时代的方案,将图片存储在 Blob 中不仅能保证服务器目录只读,又能采用云本身的安全特性限制非正常访问,还能通过 CDN 加速图片输出。要硬说缺点,就是云服务需要额外的金钱,而没钱,是自己的问题,不是云的问题。
图片防盗链
作为网站开发者,我们有时候不希望自己网站的图片被其他网站直接引用。这在某些场景下会导致自己数据中心里巨大的带宽消耗,也就意味着别人使用我们的图片,我们要为此付钱。例如,你的网站是 a.com,你有一张图片是http://a.com/facepalm.jpg,而b.com在他们的网站上使用一个img标签来引用了你的图片,这导致网络请求是进入你的数据中心,消耗你的资源。因此博客可选择性的启用防盗链功能,具体方法可参考我的文章《ASP.NET / Core 网站图片防盗链》。
附 件
通常程序员的技术博客会提供读者下载代码样例等附件。设计附件功能和设计图片存储非常类似,完全可行。但我更建议技术博客将代码示例等附件托管到其他网站(例如 GitHub)提供读者下载。
自己博客实现附件下载的坏处有:
大文件
不同的 Web 服务器及防火墙产品对文件尺寸的限制不同,而部署博客的用户很可能无权管理这些限制,就会导致大附件无法提供下载。
域及 IP 黑名单
某些公司或组织(特别是安全规范较高的软件公司)会屏蔽非白名单域的文件下载,尽管你可以用浏览器正常打开该域的网页,但无法下载文件(防火墙只允许 HTML/CSS/JS 等,而不允许 ZIP、EXE 等)。而程序员博客的读者很有可能就处在这样的公司里。
CDN 资源耗费
如果你的附件较多,较大,并且你也像设计图片存储一样给附件系统套了个 CDN,此时根据 CDN 服务商计费模式的不同,如果按流量计费,恐怕你的附件下载会导致你的钱包加速瘦身。
而采用三方文件下载(如 GitHub、OneDrive)的好处有:
√ 你的文件不仅可以分享到博客文章中,也可以分享到别的位置;
√ 这些三方服务有自己的 CDN,而不用担心消耗你自己的钱包;
√ 许多文件托管服务有完整的管理功能,例如文件删除、恢复、版本控制、权限等,要是自己在博客系统里写一个这个,需要花费大量时间……
13 敏感词过滤及评论审查
博客难免引来一些抱有敌意的人,也会引来发广告的人,所以通常需要敏感词过滤和评论审查。如果没有审查直接将用户的评论显示在文章下,那么可能会对博主和网站本身带来不良影响。例如,有人发了政治敏感的言论或者不合规的广告,没有经过后台审核就直接显示出来了,而你的博客部署在大陆,那么你的博客很可能会被马上关停整顿,并且自己也会解锁程序员从入门到入狱的成就。也千万不要以为部署在境外就没事了,一些仇恨性质的言论甚至可以帮你引来黑客,在你的博客里下毒,勒索你或你的读者。
因此我强烈建议个人博客启用敏感词过滤及评论审查功能。WordPress 及我的 Moonglade 博客系统均支持敏感词过滤和评论审查。
14 静态化
早期的新闻系统、博客、CMS 为了提高大访问量下的响应速度,都会采用静态化技术,即将服务端渲染完的页面保存为真正的 HTML 文件于磁盘上,进行 static file 的输出,Web 服务器输出 static file 的效率非常高,对于不变的内容,用户的后续访问不会 hit 数据库,因此极大减小了服务器的压力。在 2020 年的今天,静态化已经不是唯一的方案,Redis Cache 也可以帮助我们减少对数据库的频繁访问。对于个人博客来说,如果你的访问量不高,其实并不需要 996 一个静态化或 Redis 出来增加开发和维护成本。但如果你设计的是博客平台,那么最好还是用上静态化或 Redis 吧。
15 通知系统
博客通常通过 Email 的形式给管理员或用户发送通知。但是没有规范或约定表示博客是否一定得使用 Email 进行通知推送,可由博客系统设计者自行决定。
通知通常包括:
向博主发送的通知:新评论、文章被他人博客引用(参见第 5.8, 5.9 章)。
向用户发送通知:新文章发布(订阅 Newsletter)、评论被回复、评论审核通过或被拒。
Email 通知系统要注意垃圾邮件及用户隐私保护问题。
垃圾邮件发给博主本身问题并不大,但得注意邮件系统是否会允许未经博主许可的针对读者的邮件发送,其中可能会被人利用发垃圾邮件,从而导致服务器被封禁。有些服务器供应商,例如微软 Azure,对于邮件有更加严格的规定,部署在部分 PaaS 业务上的代码调用 SMTP 终端会被直接屏蔽。
对于用户隐私问题,在用户向博客系统提供 Email 地址的同时,需要告知用户该 Email 地址会被如何使用(可写在隐私协议或页面可见区域),也可以让用户勾选是否允许博主使用该 Email 进行通知推送。另一个问题是邮件地址暴露,这通常发生在 Newsletter 的订阅群发,如果把所有用户的 Email 地址都放在 To 或 CC 里,那么每个用户都会知道其余所有人的 Email 地址,从而互相约炮、欺诈,因此 Newsletter 请采用 BCC 或单独发送,并允许用户退订。
Moonglade 的通知系统采用 Email 方式,但设计比较基础。一个完善的通知系统需要采用消息队列及事件设计,并采用三方服务。例如 Azure 上可以使用 Storage Queue + Function App + SendGrid,以免遇到大批量 Email 发送的时候原地爆炸。
下篇将主要介绍【博客协议或标准】欢迎关注