部落格又搬家囉,來不及參與本站過去的新同學應該不知"又"搬家的又字而來,先來段白頭宮女話當年好了...
我從 2004 年 1 月開始寫部落格,如今已堂堂邁入第 14 個年頭。想當年部落格興起,我也趕流行在 PCHome 個人新聞台開了個小站,寫了幾篇廢文瑣事雜文,但跟大部分的人一樣,興頭一過便放著長草。一年後家裡再添一口,再興起繼續發廢文寫點東西的念頭,無奈那陣子個人新聞台系統極度不穩,被內容消失與資料庫錯誤搞到怒火攻心,心一橫便決定搬到有富爸爸 Google 撐腰的 Blogger,這是第一次搬家。
在 Blogger 寫了兩年,初衷只是延伸公司處理問題寫 KB 備忘兼分享的習慣,改成直接把筆記寫在 Internet 上罷了,但不時有路過的高手前輩現身指點,有不少意外收獲,倒也讓我樂此不疲,漸漸寫成習慣。2007 僥倖當選微軟 MVP,深感自己肩負社會責任之重大,部落格應走向更專業化經營(謎之聲:先生,您哪位?),而 Blogger 功能擴充不易、備份與管理困難,自己平日就在寫網站時要風有風,說雨是雨,因系統限制坐困愁城很不是滋味。當時靠著 MVP 前輩 Rex Tang 指點,我也仿效去註冊一個 DNS 網域 (darkthrad.net) 租了網路空間,用 Community Server (ASP.NET WebForm + SQL) 架起部落格跟討論區平台。CS 本身功能十分強大完整且為開源專案,遇到不順眼的小地方可以動手改到開心,加上擁有自己的 Domain Name,跟先前寄宿公共平台爽度完全不同呀~ 第二次搬家的心路歷程當時也有寫筆記留念:漫漫搬家路---
Community Server 從 2007 使用至今超過十年,中間幾度翻修。討論區因使用率偏低而廣告留言嚴重收攤,僅保留部落格功能;初期使用的 Captcha 機制 (TrimothyHumphrey's CAPTCHA) 漸漸不敵垃圾留言,2011 年改裝 reCaptcha才好一些,但好景不常,2012 年 reCAPTCHA 威風不再,逼得我最後自己寫了陽春 Captcha,網路上現成的垃圾留言攻擊程式瞄準的都是大廠名牌 Captcha,土砲版從來不是鎖定目標,反而較少成為攻擊對象。(但偶爾還是會中)
後續較大的調整是因應 Google 對網站行動裝置相容性的要求,不支援行動瀏覽的網站搜尋排名會受到影響,為此我惡補 CSS,修改排版 HTML 及樣式,胡亂搞了套偽 RWD 才過關。
坦白說,Community Server 雖已有十年歷史,功能、穩定性都很好,架構概念也很專業,再戰十年應不是問題。但我還是決定更換平台展開第三次搬家,有幾個原因:
2018 年 7 月起,Chrome 開始將未使用 HTTPS 的網站明顯標示「不安全」,並將調降其在搜尋結果中的排名。我比別人卡認真,我比別人卡打拼,為什麼為什麼被說不安全排名比人爛?
不幸地,我租用的共享式站台空間無法支援 SSL 憑證。
CS 走的是 WebForm + SQL Server,架構考慮周詳且嚴謹,有層層介面隔離邏輯,網頁配置全面元件化,資料庫存取也幾乎都走 Stored Procedure。可想而知,代價是修改擴充手續繁瑣,加個功能得調兩個介面改三支程式。久久才有修改需求,每回都要先溫習一次架構才能動手。我是 KISS (Keep It Simple, Stupid) 法則的忠實信徒,華麗壯觀的架構向來不是我的菜,另一方面,CS 平台仍跑在 .NET 2.0,平日寫程式已用慣 .NET 4.5 / C# 6.0 的各式新函式與語法糖,繼續維護只准用 .NET 2.0/3.5 太痛苦,砍掉重練是眼前的較佳解。
共享式網站租用空間(同一個IIS上掛載多名房客的網站)對機器的控制度很有限,資料備份、Log 分析也諸多不便。Azure 虛擬機器可以登入遠端桌面,操作管理完全比照平日開發測試的實體機,親切方便度壓倒性勝出。能百分之百存取控制機器,安裝申請 SSL 憑證,安裝各家網站平台都不是問題,而我腦中有不少觀察網站行為或實驗的怪點子,都要靠獨立機器才能實現。
大方向已定 - 另建新的部落格網站,跑 Azure 虛擬機器上,然後把舊文章通通搬過去。
下一步是選擇新的部落格系統,沒什麼懸念,我很快就選定 Miniblog.Core,理由很簡單:
第一,它是當今檯面上僅有用 ASP.NET Core 寫的部落格平台,ASP.NET Core成為主流在即,藉此強迫自己學習上手,是絕佳的安排。
第二,Miniblog.Core 既稱 Mini,架構自然走單純簡潔風,沒有拆出一堆 Interface、Component、Provider 專案,是我鍾愛的 KISS 風格。而作者 Mads Kristensen是 Visual Studio 神級外掛 WebEssentials 的作者,有什麼比觀摩神人的作品更讓人快速成長呢?(事實也證明,在修改過程中我學到許多新穎觀念與前端技巧,受益良多。)
初步了解 Miniblog.Core,發現它用 XML 保存文章及留言,平時則將所有內容讀入記憶體進行查詢,以個人部落格文章資料量絕對可行。但對於無法用 SQL 工具統計及批次調整,總讓我有些不踏實感,找到在 ASP.NET Core 使用 SQLite + Dapper 的做法後,我決定一鼓作氣將儲存核心由 XML 換成 SQLite,另外陸續調整擴充功能以相容舊版,基本上新部落的雛型就完成了。
利用間暇時間邊做邊學,陸續進行近一個月,新站落成,於 9/21 正式啟用。以下是這次搬家歷程的技術筆記:
Miniblog.Core 是 ASP.NET MVC,要頂替舊站的前題是能將包含 .aspx 的舊 URL 對應到新址,我是用 ASP.NET Core 的 UrlRewriting Middleware ,將有 .aspx 的 URL 統交給 UrlRewriteController,再查表對照轉址。
var rewrite = new RewriteOptions()
.AddRewrite("(.+)[.]aspx", "UrlRewrite/Index?path=$1", skipRemainingRules: true);
app.UseRewriter(rewrite);
在舊站寫了一支程式,將舊文章依新版 Schema 欄位轉成 JSON,再進行必要的資料對應、轉換。新站匯入時直接將 JSON 反序列化回物件陣列,再用 Entity Framework 塞入資料庫,比預期簡單。時代進步,新工具威力愈來愈強大,做起來比以前輕鬆愉很多。
Miniblog.Core 支援 Live Writer,只需提供網址、帳號、密碼就可以用 Windows Live Writer 或 Open Live Writer貼文,非常神奇。剖析背後靠的是已屬業界標準的 MetaWeblog 介面,Mininblog.Core 借用第三方套件,MetaWeblog,NuGet 即可安裝,實作 IMetaWeblogPorivder 再配合 app.UseMetaWeblog("/metaweblog"),網站就有了離線文章編輯功能。不過,我最終決定隨新站上線,也從此改用 Markdown 寫部落格,找到好用的 Markdown Monster (作者 Rick Strahl是微軟 MVP),能搭配 MetaWeblog 直接發佈,事實上這篇就是我第一篇用 Markdown 寫的部落格文章。關於改用 Markdown 的經驗,之後另外再發文分享。
Miniblog.Core 專案引用了 WebEssentials.AspNetCore.ServiceWorker,網站具備 Progressive Web App功能,背後有 Service Worker 支援離線使用及優化效能,還能被釘選到手機或平台主畫面當成 App 使用,不過目前沒提供任何訂閱、通知等服務就是了。
Miniblog.Core 用了不少先進的前端技巧,例如:<script> preload/async 載入、圖檔延遲載入(捲到可視範圍才真的下載圖檔)... 等等,我如劉姥姥進大觀園,邊改邊開眼界,看到目瞪口呆。另一方面,十年過去我的前端能力也小有長進,這次改版有留意避開影響效能的寫法。原則上,新版部落格的載入速度與流暢度應該有大幅改進。
ASP.NET Core 不再提供內建 OutputCache 機制,WebEssentials.AspNetCore.OutputCaching也是 Mads Kristensen 的作品,Miniblog.Core 吃狗食(Dogfooding)也是很合情合理滴。原本苦惱文章更新時要如何強制清除 Cache,後來找到一個超簡單的做法 - 設定 FileDependencies,修改檔案內容,相依的快取都會被清除,下回就會讀到最新內容。
services.AddOutputCaching(options =>
{
options.Profiles["default"] = new OutputCacheProfile
{
Duration = 3600,
FileDependencies = new string[] { "filename.txt" }
};
});
ASP.NET Core 有一套新的 appSettings.json 機制,可在 appSettings..json 加入特定執行環境(例如: 開發、測試、正式)適用的設定,也可透過環境變數、命令列參數指定,蠻靈活的。這部分待未來有更完整心得時再介紹。
ASP.NET Core 的 Dependency Inject 用得很深,深到你想裝不認識都不行。例如:Controller 要取得文章資料、網站設定,做法是在 Startup.cs 中先註冊服務:
services.AddSingleton<IBlogService, SqliteBlogService>();
services.Configure<BlogSettings>(Configuration.GetSection("blog"));
Controller 建構時將要用到的服務當成參數傳入。
private readonly IBlogService _blog;
private readonly IOptionsSnapshot<BlogSettings> _settings;
public RobotsController(IBlogService blog, IOptionsSnapshot<BlogSettings> settings)
{
_blog = blog;
_settings = settings;
}
在 cshtml 則是寫成 @inject IOptionsSnapshot settings
原本 Community Server 內建以 SQL 全文檢索為基礎的站內搜尋功能,SQLite 沒有這種東西。一度考慮改用 Lucene.NET實作,但考量實作工程不小,目前使用的 ASP.NET Core 能跨 Windows / Linux 平台,一旦引入 Lucene.NET,未來要搬移到 Linux 得多花功夫研究。評估後決定閃開讓專業的來,交給 Google Custom Search,Google 自訂搜尋引擎。
忘了提本次搬家的重要目標,支援 HTTPS。我跟大部分的個人網站一樣,選擇免費的 Let's Encrypt 憑證,網路上相關的教學文很多,這裡不多贅述。但 Let's Encrypt SSL 憑證有個缺點是三個月要更新一次,原本想自己寫個程式自動更新,但發現已有佛心的開發者寫好了 - IIS 自動安裝Let’s Encrypt 免費 SSL 並到期自動更新,這部分有空再來研究。
總之,新站已經正式啟用囉,如果大家發現運作有問題,請留言或在 Facebook 貼文反映。