Dotnet9

联系请关注微信公众号:Dotnet9

文章

博客系统知多少:揭秘那些不为人知的学问(二)

汪宇杰博客 - 2022-03-08 21:55:33 | 架构设计 | 博客

上篇《博客系统知多少:揭秘那些不为人知的学问(一)》介绍了博客的历史、我的博客故事及博客的受众来源。本篇精彩继续,介绍博客基本功能设计要点。

目录

由于文章篇幅较长,本文将分为4篇推送,目录如下:

  1. “博客”的前世今生
  2. 我的博客故事
  3. 谁是博客的受众?
  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. 博客协议或标准
    • 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. 设计博客系统有哪些知识点
    • 6.1 时区真的全用UTC?
    • 6.2 HTML还是Markdown
    • 6.3 MVC还是SPA
    • 6.4 安全
  7. 结束语

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造成灾难(我的博客系统因为面向英语用户,不考虑中文支持,所以并不解决这个问题)。

(图:文章列表中的摘要)

(图:meta description标签代码)

摘要可以自动抓取文章前几百字,也可以像微信公众号那样要求用户手工填写。我的博客采用的是自动取文章前400字。结合SEO的关系,我的文章通常开头段落就是概要,这样可以让用户在搜索引擎预览页面就能看到准确内容,而不是页面上无关紧要的UI元素。

(图:必应搜索引擎识别的内容摘要)

文章的状态通常包括:草稿、发布、回收。用户仅能看到已发布的文章,管理员可在后台更改文章状态。

02 评论(Comment)

评论是博客中作者和读者互动的主要方式。有些博客要求读者登录后才能发表评论,而有些可以允许游客评论(比如我的博客及WordPress)。登录的好处在于可以识别你的读者,并有效防止垃广告评论。但要求登录也会给用户造成操作上多了一个步骤,嫌麻烦的用户就不会进行评论。

我的博客及WordPress默认都设计为需要管理员在后台审核评论后,才能放出显示。这也能有效避免垃圾广告、骚扰信息甚至是一些恶意的煽动性言论。对于提供Email地址的用户,管理员也能够在后台回复用户的评论,并由博客系统向用户发送Email通知。

(图:Moonglade的评论区)

对于技术博客,评论可考虑开放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。简介是对于标题的补充说明,便于用户查看。设计路由名称的原因请参考下一段介绍的标签的设计。

(图:Moonglade博客系统的一个文章分类)

分类的另一个功能就是产生 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。

(图:Moonglade博客系统的标签)

对于用户来说,最容易犯错之一的就是把标签用成搜索关键词。例如用户写了一篇关于 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年所有的文章。归档功能主要用于给读者按时间查询,看看博主某个时间都在干什么。设计这样的功能可以提高读者对博主的兴趣,也是个人对外形象的一种展示。

(图:Moonglade博客系统的归档)

06 页面(Page)

页面是博客的可选功能之一,事实上,它更接近于CMS的功能。有些内容并不适合以文章的形式发布,比如“关于”页面。这样的页面通常与发布时候的时间无关,内容也经常更新,排版设计也非常自由,不单纯是文字。

页面通常不需要评论、标签和分类等属性,但可以有发布和编辑时间。和文章一样,页面也需要注意Slug。

(图:我博客的关于页面)

在我的博客系统中,页面也选择是否隐藏侧边栏,用户也可以完全编写页面的HTML及CSS代码,并把页面添加为导航菜单。WordPress对于页面的处理更加完备,接近于CMS系统。

07 订阅(Subscription)

读者订阅博客的主要方式有Feed(RSS/ATOM)及Newsletter。Feed方式本质上是被动订阅,需要客户端软件发起请求给服务器,检查是否有新文章发表,才能显示到客户端里。Newsletter一般采用Email形式主动发送给订阅用户,但这要求博客系统的编写者实现Email订阅功能,也要求管理员维护Email服务。订阅一般只推送近期发表的新文章,例如前10、20篇,而不会每次都推送全部文章导致客户端爆炸。

(图:Moonglade的RSS/ATOM订阅源)

订阅一般可按文章分类提供,以便于只对某些分类感兴趣的读者阅读。有些博客系统也提供文章评论的订阅源,以便读者观摩吐槽大会。

关于RSS及ATOM的详细介绍请看5.1、5.2章节。

08 版本控制

更接近CMS的博客系统通常提供版本控制功能,允许用户回滚文章或页面的历史版本。设计版本控制的时候,不能只考虑往前回滚,得还能再滚得回来。通常,用户每次编辑一篇已经写好的文章,就会产生一个新版本,类似于git对于一个文件的commit。博客的版本控制也类似于代码版本控制,你可以选择保存一篇文章的完整内容作为历史版本,也可以选择每次只保存变化量信息(delta)。保存完整内容不容易后续花费大量时间精力 ,但是会占用较多存储空间。保存内容变化量节省数据库空间,但实现代码容易占用大量精力。

09 主题及个性化

好用的博客系统通常支持主题,毕竟个性化是博客本身应有的特点之一。WordPress积累了大量的主题库,也允许自制主题。但是我的博客只支持更改主题色,还有很大上升空间。

10 用户及权限

博客系统分为个人、团队及博客平台。个人博客系统一般为单用户(例如我的博客),不需要设计权限、注册等功能。多用户博客则需要实现不同的角色和权限,比如博客管理员、审核专员、撰稿人、评论管理员等等。无论是单用户还是多用户博客,集成一套成熟的三方RBAC方案可能是最高效的选择,多数三方方案也都支持SSO,例如我博客支持的Azure AD。

11 插件

插件功能可以在不更改博客代码的情况下,按需拓展博客的功能。WordPress以及BlogEngine都支持插件,但Moonglade还不行。

(图:WordPress的插件市场)

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发送的时候原地爆炸。

下篇将主要介绍【博客协议或标准】欢迎关注

汪宇杰

文章评论