引言
有些日子沒寫項目了,正好學了點Nodejs,就說拿koa2+react搭一個博客練練手。
在找博文存儲方案的時候瞭解到目前大多數博文都已markdown或類似的標記化語言文檔存儲,所以就找了較爲新穎的hexo博客框架試用一下,順帶研究下源碼。
起初覺得挺舒服,因爲配置和主題切換起來足夠簡單,官網的文檔也是希望用戶不需要了解太多,只有最簡單的教程,所以沒幾個小時就覺得不夠用了。
我的需求是通過Node實現博文的數據維護和接口,所以接下來我通過 hexo-admin 和 hexo-generator-restful 兩個hexo插件來實驗,前者構建了一個管理界面,有賬號管理、博文編寫和管理等功能,後者提供了hexo中所有內容的輸出接口,巧的是通過查看源碼發現hexo-admin還提供了很多可用來維護博文的輸入接口。
後來我把hexo項目部署到了服務器上,前臺後臺預期功能也實現了,卻索然無味了……
hexo博客搭建
hexo博客在本地搭建起來很簡單,全命令行操作即可,官網也有不怎麼清楚的教程->點此進入。
-
node環境搭建
- windows環境: https://www.cnblogs.com/zhouy...
- centos環境: https://www.cnblogs.com/codey... (沒記錯的話,裝完需要重啓)
- 安裝hexo-cli
npm install -g hexo-cli
- 通過hexo-cli初始化博客項目,將blog-site-name替換成你的項目名
hexo init <blog-site-name>
- 搭建結束,開箱即用,目錄結構如圖
-
命令行進入項目目錄,輸入命令運行。
hexo server
或者hexo s
INFO Start processing INFO Hexo is running at http://localhost:4000 . Press Ctrl+C to stop.
這裏沒什麼說的,之後即可通過訪問 http://localhost:4000 進入博客。
構建接口服務
- 首先安裝 hexo-admin 和 hexo-generator-restful 兩個插件
npm install --save hexo-admin hexo-generator-restful
-
如果你願意的話可以看它們的文檔查看詳細使用方法:
https://github.com/jaredly/he...
https://github.com/yscoder/he... - 我們來看hexo-admin插件的源碼,目錄結構如下:
-
入口文件index.js片段
這裏通過hexo的api方法在服務請求中間件中加入了登陸模塊和路由模塊。
其中路由模塊分後臺管理頁面admin/
和 後臺管理接口admin/api/
hexo.extend.filter.register('server_middleware', function(app) { // 如果配置文件中要求用密碼登陸 if (passwordProtected) { require('./auth')(app, hexo); // setup authentication, login page, etc. } // Main routes app.use(hexo.config.root + 'admin/', serveStatic(path.join(__dirname, 'www'))); app.use(hexo.config.root + 'admin/api/', bodyParser.json({limit: '50mb'})); // setup the json api endpoints api(app, hexo); });
-
剖析api.js
api.js
中就是所有接口代碼。
這是接口封裝函數:var use = function (path, fn) { app.use(hexo.config.root + 'admin/api/' + path, function (req, res) { var done = function (val) { if (!val) { res.statusCode = 204 return res.end(''); } res.setHeader('Content-type', 'application/json') res.end(JSON.stringify(val, function(k, v) { // tags and cats have posts reference resulting in circular json.. if ( k == 'tags' || k == 'categories' ) { // convert object to simple array return v.toArray ? v.toArray().map(function(obj) { return obj.name }) : v } return v; })) } res.done = done res.send = function (num, data) { res.statusCode = num res.end(data) } fn(req, res) }) }
類型太多,這裏以博文接口爲例,可以看到通過下級路由
publish
、unpublish
、remove
、rename
實現了對博文的發佈,撤回草稿,移除,改名操作,並且根據GET
、POST
請求類型對/post
功能進行了區分。use('posts/', function (req, res, next) { var url = req.url if (url[url.length - 1] === '/') { url = url.slice(0, -1) } var parts = url.split('/') var last = parts[parts.length-1] if (last === 'publish') { return publish(parts[parts.length-2], req.body, res) } if (last === 'unpublish') { return unpublish(parts[parts.length-2], req.body, res) } if (last === 'remove') { return remove(parts[parts.length-2], req.body, res) } if (last === 'rename') { return rename(parts[parts.length-2], req.body, res) } var id = last if (id === 'posts' || !id) return next() if (req.method === 'GET') { var post = hexo.model('Post').get(id) if (!post) return next() return res.done(addIsDraft(post)) } if (!req.body) { return res.send(400, 'No post body given'); } update(id, req.body, function (err, post) { if (err) { return res.send(400, err); } res.done({ post: addIsDraft(post), tagsCategoriesAndMetadata: tagsCategoriesAndMetadata() }) }, hexo); });
其他細節就不贅述,可以直接看其源碼,但有一點要說明一下,就是hexo創建博文的方式:通過
/new
我們可見一斑。hexo通過new創建實體markdown文件,再通過update方法更新其內容。use('pages/new', function (req, res, next) { if (req.method !== 'POST') return next() if (!req.body) { return res.send(400, 'No page body given'); } if (!req.body.title) { return res.send(400, 'No title given'); } hexo.post.create({title: req.body.title, layout: 'page', date: new Date()}) .error(function(err) { console.error(err, err.stack) return res.send(500, 'Failed to create page') }) .then(function (file) { var source = file.path.slice(hexo.source_dir.length) hexo.source.process([source]).then(function () { var page = hexo.model('Page').findOne({source: source}) res.done(addIsDraft(page)); }); }); });
- hexo-generator-restful