摘要:項目都很小,但為了進一步了解,特意選擇了作為框架基礎開發后端服務。能將請求限制在同源網站,即只有擁有專有令牌的網站發送請求才會正確響應。項目生產靜默部署,啟動使用,停止使用。不足工具函數的訪問需要自己手動添加擴展另沒有寫測試,希望下次補上。
前言
這段時間,用Eggjs作為后端服務框架開發了幾個項目。項目都很小,但為了進一步了解Eggjs,特意選擇了Eggjs作為框架基礎開發后端服務。期間也遇到過一些問題和坑,還有幾個值得注意的點,下面來講一下我這段時間開發的總結。
Egg.js 為企業級框架和應用而生 ,我們希望由 Egg.js 孕育出更多上層框架,幫助開發團隊和開發人員降低開發和維護成本。
這個是Eggjs文檔對Eggjs的解釋,關于Eggjs的詳細介紹和使用請點解前面的地址;相對于Egg.js 1.x版本的文檔,已經有很大的改進了,很多關鍵的地方都可以比較完整講解和帶有代表性的實例。
步驟 開始用的Egg.js版本是2.2.1,對環境有一定的要求,本人用的配置如下:
操作系統:macOS
運行環境:v9.8.0
使用腳手架快速創建項目:
$ npm i egg-init -g $ egg-init egg-example --type=simple $ cd egg-example $ npm i
項目安裝完畢,啟動項目:
$ npm run dev $ open localhost:7001
至此,項目順利建立及啟動完畢。
項目結構:(摘自文檔)
egg-project ├── package.json ├── app.js (可選) ├── agent.js (可選) ├── app | ├── router.js │ ├── controller │ | └── home.js │ ├── service (可選) │ | └── user.js │ ├── middleware (可選) │ | └── response_time.js │ ├── schedule (可選) │ | └── my_task.js │ ├── public (可選) │ | └── reset.css │ ├── view (可選) │ | └── home.tpl │ └── extend (可選) │ ├── helper.js (可選) │ ├── request.js (可選) │ ├── response.js (可選) │ ├── context.js (可選) │ ├── application.js (可選) │ └── agent.js (可選) ├── config | ├── plugin.js | ├── config.default.js │ ├── config.prod.js | ├── config.test.js (可選) | ├── config.local.js (可選) | └── config.unittest.js (可選) └── test ├── middleware | └── response_time.test.js └── controller └── home.test.js
上述目錄也是一個給開發者一個目錄創建的指南,但按照文檔建立的項目目錄結構沒有那么全,基本上標注為“可選”的都是初始沒有的,在/config目錄里也只有plugin.js和config.default.js兩個文件,其他文件要自己根據需求創建。
建立控制器Controller初始項目里會有一個示例Controller,在創建一個新的Controller可以參考/app/controller/home.js的示例,一般而言,推薦使用module.exports暴露出一個類或者參數為app返回一個類的函數(文檔示例中為箭頭函數,其他方式沒試過不清楚),類里面包含著這塊業務的一些操作,下面在控制器文件目錄/app/controller/里新建一個文件名為user.js的控制器文件:
// 繼承egg的控制器 const Controller = require("egg").Controller; class UserController extends Controller { async index() { const { ctx } = this; const { name } = ctx.request.body; ctx.body = `hi, ${name}`; } async getUserById() { const { userId } = this.ctx.request.body; // 使用業務函數查詢用戶信息 const userInfo = await this.service.user.findById(userId); this.ctx.body = { msgCode: 0, message: "成功", data: userInfo }; } } // 注意:一定要將控制器暴露出去,否則請求的時候會報找不到該controller的錯誤; module.exports = UserController;添加路由
路由代碼在/app目錄之下,文件名router.js,添加路由的代碼如下:
// 參數app為全局應用的對象 module.exports = app => { const { router, controller, middleware } = app; // 在這里controller相當于app下的controller文件目錄,user為user.js,index為控制器類的index方法 router.get("/", controller.user.index); };編寫業務
通常,controller主要處理數據的結構和處理返回的結果,具體的涉及的業務由service業務類方法完成,編寫service,在目錄/app/service/下建立user.js文件,并編寫代碼:
// 同樣要繼承egg的Service類 const Service = require("egg").Service; class UserService extends Service { // 根據用戶id查找用戶 async findUserById(id) { const mysql = this.app.mysql; const result = await mysql.get("users", { id }); return result; } } module.exports = UserService;添加插件
eggjs simple 版本旨在根據業務需求添加eggjs的插件來搭建上層框架。在本人開發過程中,用到的一些插件做簡要說明。
插件安裝:
$ npm i --save egg-pluginName
在文件/config/plugin.js添加配置:
exports.pluginName = { enable: true, package: "egg-pluginName", };
需要插件初始化配置的情況下,修改/config/config.default.js:
config.pluginName = { // 配置項 };egg-mysql
egg-redis因使用mysql數據庫,需要一個nodejs對mysql的操作庫,基于eggjs選擇了egg-mysql ,操作文檔點擊這里?;镜臄祿煸鰟h查改都能操作,寫起來還挺方便,但是有個需求,編寫某個接口,返回當前用戶某一段時間的數據,這就比較蛋疼了,找了很久,就連egg-mysql封裝的庫ali-rds查看了源碼也找不到這類方法,無奈之下,只能通過組織原生的mysql查詢語句去動態拼湊,雖然不推薦,不過如果找到更好的方法,還是愿意改寫的。
查詢指定日期數據的mysql相關參考資料:
http://www.jb51.net/article/1...
https://www.cnblogs.com/benef...
https://www.cnblogs.com/softi...
redis相當于基于內存的一個微型數據庫,其存取速度非??欤a執行的時候幾乎感覺不到阻塞,這里使用egg-redis作為項目對redis的操作庫,文檔點擊這里。文檔說明解析了基本的存取和設置操作,對于較為復雜的操作只能通過查看redis的官方文檔,對應的命令小寫即為方法名:
redis命令DECR,對應的方法使用:
// /app/controller/user.js const value = await this.app.redis.decr(keyName);擴展內置對象
添加過幾個插件之后,發現其中的源代碼都是以擴展內置對象的方式去掛載相關的庫或者插件的。
ctx文檔原文:“一般來說屬性的計算在同一次請求中只需要進行一次,那么一定要實現緩存,否則在同一次請求中多次訪問屬性時會計算多次,這樣會降低應用性能。
推薦的方式是使用 Symbol + Getter 的模式。”
const jwt = require("jsonwebtoken"); const JWT = Symbol("Context#jwt"); module.exports = { get jwt() { if (!this[JWT]) { this[JWT] = jwt; } return this[JWT]; } };
第一次擴展cxt對象的時候,不明白為何要使用Symbol + Getter的模式,后來基于這個問題,查找資料,發現這種方式更能避免和其他屬性名發生沖突,上述代碼中,ctx的jwt定義為只讀方式。在方便維護的同時,生成一個帶有命名空間(Context#jwt)字符串描述的Symbol實例數據, 作為ctx的屬性,通過只讀屬性jwt來獲取內部的JWT屬性。
PS:
ctx.JWT == ctx[JWT] // false
關于Symbol的介紹和使用請參考阮一峰的ES6。
app在擴展app對象的時候,遇到個問題,就是如果需要獲取ctx怎么辦?
查找文檔,找到了在擴展app對象時,只需要在函數體里添加一句代碼:
const ctx = app.createAnonymousContext();
就可以獲取ctx對象,這對于使用其他函數提供了一道橋梁。
編寫中間件eggjs的中間件處理流程遵循koa的洋蔥式請求模型
中間件的寫法:
module.exports = options => { return async function middleWareFunctionName (ctx, next) { // 控制器之前業務處理代碼 // ... await next(); //控制器之后業務處理代碼 // ... } }
中間件以返回一個處理業務的函數為主體,函數接收兩個參數:ctx、next。ctx則是請求級別的對象,next()方法可以讓請求進入下一個步驟。特別注意的是:在一個控制器中,有對請求到達下一步之前做一些操作的,可以控制next()在代碼流程中的位置,其后也可以處理請求之后的操作。
制定定時任務在eggjs寫定時任務也是非常簡單的,關注于業務代碼,加以簡單的配置,即可使用定時任務。
下面是一個簡單的定時統計業務數據的定時任務:
const Subscription = require("egg").Subscription; class Statistics extends Subscription { // 通過 schedule 屬性來設置定時任務的執行間隔等配置 static get schedule() { return { cron: "00 59 23 * * *", // 秒 分 時 日 月 年 // interval: "10s", // 設置時間間隔觸發,單位s為秒,ms為毫秒 type: "worker", // all 指定所有的 worker 都需要執行, worker 為某一個 worker 執行 }; } // subscribe 是真正定時任務執行時被運行的函數 async subscribe() { // 定時任務業務代碼 // ... } } module.exports = Statistics;
在程序啟動的時候,就會在配置的指定時機執行相關的業務代碼。
配置(待補充) csrf的討論eggjs在v2.x版本之后默認開啟了csrf插件,已確保基于cookie存儲驗證信息的網站信息安全。
csrf能將請求限制在同源網站,即只有擁有“專有令牌”的網站發送請求才會正確響應。此處容易與jwt的作用混淆,可以看看這篇文章。
跨域使用egg-cors;前后端分離用戶驗證
使用jwt驗證
jwt則在認證方式上跟csrf上有所不同,jwt可以在不使用cookie的情況下,以token的方式在前后端交互數據的body里傳輸,也可以在header里設置相關信息,詳細可以參考這篇文章。
日志(待補充)類的寫法遠程機開發部署
文檔中,有《應用部署》一文,里面介紹的很詳細的。使用egg-script插件啟動生產環境中的應用程序。項目生產靜默部署,啟動使用npm start,停止使用npm stop。另,在開發環境里想要使用pm2管理進程后臺啟動,--watch會不斷打印控制臺日志,原因不清楚。生產環境部署
啟動命令:
$ npm install --production $ npm start
停止命令:
$ npm stop總結 優點
使用eggjs開發企業級應用還是相當方便的,雖然說要根據需求裝,但安裝和配置步驟非常簡單,很多有用的業務配置都能夠很方便快速配置好,還可以區分環境,項目結構和調用方式很合理。不足
工具函數的訪問需要自己手動添加擴展另
沒有寫測試,希望下次補上。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/95471.html
摘要:今天就簡要說說下的實現。主要的原因是的文檔寫的不太清楚,方便新人快速上手。導致我們一目十行去掃文檔的時候,有時總會覺得有些莫名,的實現就是其中之一。其實這和我本身對的了解不深入有關,但是也沒有文檔和我說實現啊。 這兩天真的是宅的骨頭都發霉了,春困秋乏夏打盹,也是醉了。今天就簡要說說eggjs下Restful API的實現。主要的原因是egg的文檔寫的不太清楚,方便新人快速上手。話說eg...
摘要:最近學習,學習過程中使用官方推薦的庫,感覺官方庫不太好用,基礎的沒問題。介紹這個輪子其實是很早以前就造好的,主要參考的數據庫操作方式。將設置表名設置查詢字段聯表等操作進行鏈式操作,給人一種語義化操作數據庫的感覺。 最近學習eggjs,學習過程中使用官方推薦的MySQL庫,感覺官方庫不太好用,基礎的CURD沒問題。但是復雜點的操作就不行了,雖然官方還有一個egg-sequelize,但是...
摘要:總所周知,的策略讓每次都要發送碼驗證,為了方便,我在的里作了前置攔截。結果不幸從此發生最開始沒有看官方文檔,以為應該加在里面,又沒有考慮到要上傳格式的文檔,所以直接結果發送的是。這很正常,閱讀源碼知為時會自動添加的頭。不加又以上傳了。 總所周知,egg的csrf策略讓post每次都要發送token碼驗證,為了方便,我在axios的interceptor里作了前置攔截。 結果不幸從此發生...
摘要:前兩天將一個部署到服務器時,用就是啟動不了,錯誤信息為無權限創建目錄。查看工作目錄權限,當前用戶是有權限的,看了源碼,原來是用的包的問題。首先,在生產環境下的啟動是通過啟動的。 前兩天將一個egg部署到服務器時,用npm start就是啟動不了,錯誤信息為無權限創建log目錄。查看工作目錄權限,當前用戶是有權限的,看了源碼,原來是用的npm包的問題。這里簡單記錄下解決過程。 首先,在生...
閱讀 1719·2021-11-22 15:33
閱讀 2094·2021-10-08 10:04
閱讀 3545·2021-08-27 13:12
閱讀 3423·2019-08-30 13:06
閱讀 1470·2019-08-29 16:43
閱讀 1395·2019-08-29 16:40
閱讀 788·2019-08-29 16:15
閱讀 2748·2019-08-29 14:13