編寫 service
阿新 • • 發佈:2018-12-12
在實際應用中,Controller 一般不會自己產出資料,也不會包含複雜的邏輯,複雜的過程應抽象為業務邏輯層 Service。
我們來新增一個 Service 抓取 Hacker News 的資料 ,如下:
// app/service/news.js
const Service = require('egg').Service;
class NewsService extends Service { async list(page = 1) { // read config const { serverUrl, pageSize } = this.config.news; // use build-in http client to GET hacker-news api const { data: idList } = await this.ctx.curl(`${serverUrl}/topstories.json`, { data: { orderBy: '"$key"', startAt: `"${pageSize * (page - 1)}"`, endAt: `"${pageSize * page - 1}"`, }, dataType: 'json', }); // parallel GET detail const newsList = await Promise.all( Object.keys(idList).map(key => { const url = `${serverUrl}/item/${idList[key]}.json`; return this.ctx.curl(url, { dataType: 'json' }); }) ); return newsList.map(res => res.data); } }
module.exports = NewsService;
還需增加 app/service/news.js
中讀取到的配置:
// config/config.default.js // 新增 news 的配置項 exports.news = { pageSize: 5, serverUrl: 'https://hacker-news.firebaseio.com/v0', };
編寫擴充套件
框架提供了一種快速擴充套件的方式,只需在 app/extend
目錄下提供擴充套件指令碼即可
在這裡,我們可以使用 View 外掛支援的 Helper 來實現:
npm i moment --save
// app/extend/helper.js
const moment = require('moment');
exports.relativeTime = time => moment(new Date(time * 1000)).fromNow();
在模板裡面使用:
<!-- app/view/news/list.tpl --> {{ helper.relativeTime(item.time) }}
編寫 Middleware
假設有個需求:我們的新聞站點,禁止百度爬蟲訪問。
// app/middleware/robot.js // options === app.config.robot module.exports = (options, app) => { return async function robotMiddleware(ctx, next) { const source = ctx.get('user-agent') || ''; const match = options.ua.some(ua => ua.test(source)); if (match) { ctx.status = 403; ctx.message = 'Go away, robot.'; } else { await next(); } } }; // config/config.default.js // add middleware robot exports.middleware = [ 'robot' ]; // robot's configurations exports.robot = { ua: [ /Baiduspider/i, ] };
配置檔案
寫業務的時候,不可避免的需要有配置檔案,框架提供了強大的配置合併管理功能:
- 支援按環境變數載入不同的配置檔案,如
config.local.js
,config.prod.js
等等。 - 應用/外掛/框架都可以配置自己的配置檔案,框架將按順序合併載入
- 具體合併邏輯可參見配置檔案。
-
// config/config.default.js exports.robot = { ua: [ /curl/i, /Baiduspider/i, ], }; // config/config.local.js // only read at development mode, will override default exports.robot = { ua: [ /Baiduspider/i, ], }; // app/service/some.js const Service = require('egg').Service; class SomeService extends Service { async list() { const rule = this.config.robot.ua; } } module.exports = SomeService;
- 單元測試
- 單元測試非常重要,框架也提供了 egg-bin 來幫開發者無痛的編寫測試。
- 測試檔案應該放在專案根目錄下的 test 目錄下,並以
test.js
為字尾名, - 即
{app_root}/test/**/*.test.js
。