前言
? ? ? ? Eggjs 是阿里巴巴團(tuán)隊(duì)開發(fā)的,一個(gè)基于 Koajs 的框架,相當(dāng)于二次封裝,和 Koajs 一樣,它的性能也是比較高的,但是相比較 Koajs ,并沒有那么高的自由度,相比于 Koajs 來說,Eggjs 這種帶有規(guī)范性的開發(fā)更適合在企業(yè)中使用。
? ? ? ? Swagger 是RESTFUL接口的文檔在線自動(dòng)生成工具。
? ? ? ? Sequelize 是基于Nodejs功能強(qiáng)大的異步ORM框架。 同時(shí)支持PostgreSQL, MySQL, SQLite and MSSQL多種數(shù)據(jù)庫
項(xiàng)目搭建
? ? ? ? Eggjs 分為兩種搭建方式,快速搭建和逐步搭建,為了讓大家更好的了解 Egg.js,這里使用逐步搭建。
項(xiàng)目目錄
egg_folder
├── app
│ ├── contract
│ │ └── format.js
│ ├── controller
│ │ └── home.js
│ ├── extend
│ │ └── helper.js
│ ├── service
│ │ └── home.js
│ └── router.js
├── config
│ └── config.default.js
│ └── plugin.js
├── node_modules
└── package.json
初始化項(xiàng)目
? ? ? ? 在項(xiàng)目文件夾( egg_folder )里首先 cmd 運(yùn)行npm init來獲取package.json文件,再運(yùn)行npm install egg --save和npm install egg-bin --save-dev安裝 Eggjs 依賴包和 本地開發(fā)工具( 沒安裝 npm 的自行安裝 )。
? ? ? ? 之后在package.json的 script 里面配置運(yùn)行dev命令
/* egg_folder/package.json */
"scripts": {
"dev": "egg-bin dev --port 7007",
"dbinit": "egg-sequelize-auto -o ./app/model -h localhost -p 3306 -d library -u root -x haosql",
"test": "echo \"Error: no test specified\" && exit 1"
},
? ? ? ? 然后在 cmd 運(yùn)行npm run dev,如果順利就會(huì)如下圖所示。
? ? ? ? 如果直接在瀏覽器打開,就會(huì)出現(xiàn)這樣的界面,500 那是因?yàn)槭裁炊紱]做,但至少項(xiàng)目已經(jīng)成功的啟動(dòng)了。
配置 Cookie 秘鑰
? ? ? ? 首先配置 Cookie 秘鑰,詳見官網(wǎng)。
/* egg_folder/config/config.default.js */
'use strict';
exports.keys = 'egg_folder';
keys 配置成一個(gè)字符串,可以按照逗號分隔配置多個(gè) key
配置路由
? ? ? ? 接下來開始配置路由,在 router.js 里開始定義路由:
/* egg_folder/app/router.js */
'use strict';
module.exports = function (app) {
app.router.get('/', 'home.index'); // 第一個(gè)參數(shù)為 url 路徑,第二個(gè)參數(shù)的說直接點(diǎn)就是 controller 下的"文件名.方法"( 例如:'home.index'就會(huì)去找 egg_folder/app/controller 下的 home.js,然后再這個(gè) js 里面找到 index 方法 )。
}
? ? ? ? 其中 app 是 Eggjs 的全局應(yīng)用對象,具體詳見官網(wǎng)
Application 是全局應(yīng)用對象,在一個(gè)應(yīng)用中,只會(huì)實(shí)例化一個(gè),它繼承自 Koa.Application,在它上面我們可以掛載一些全局的方法和對象。我們可以輕松的在插件或者應(yīng)用中擴(kuò)展 Application 對象。
定義 Controller
? ? ? ? 設(shè)置完后就在 home.js 里面定義 index 方法:
/* egg_folder/app/controller/home.js */
'use strict';
const Controller = require('egg').Controller;
// 通過定義 Controller 類的方式來編寫代碼,所有的 Controller 文件都必須放在 app/controller 目錄下。
class HomeController extends Controller {
async index () {
this.ctx.body = 'Hello World!'; // 設(shè)置響應(yīng)內(nèi)容。
}
}
module.exports = HomeController;
? ? ? ? 重新運(yùn)行 npm run dev ,頁面就不會(huì)報(bào)錯(cuò)了。
安裝和配置 Swagger 和 Sequelize
? ? ? ? 現(xiàn)在已經(jīng)完成頁面的顯示了,接下來就可以搭配 Swagger 和Sequelize 開始寫接口了。
? ? ? ? 首先還是安裝依賴包。
npm install egg-sequelize --save
npm install egg-sequelize-auto --save-dev // 對照數(shù)據(jù)庫自動(dòng)生成相應(yīng)的 models 減少了對數(shù)據(jù)庫進(jìn)行增刪改查時(shí)的 sql 語句的編寫。
npm install egg-swagger-doc --save
? ? ? ? 因?yàn)槭遣寮?,所以要?config.default.js 和 plugin.js 里配置插件信息。
/* egg_folder/config/config.default.js */
'use strict';
exports.keys = "egg_folder";
// egg-swagger-doc 配置信息。
exports.swaggerdoc = {
dirScanner: './app/controller', // 配置自動(dòng)掃描的控制器路徑。
// 接口文檔的標(biāo)題,描述或其它。
apiInfo: {
title: 'NAPI', // 接口文檔的標(biāo)題。
description: 'swagger-ui for NAPI document.', // 接口文檔描述。
version: '1.0.0', // 接口文檔版本。
},
schemes: ['http', 'https'], // 配置支持的協(xié)議。
consumes: ['application/json'], // 指定處理請求的提交內(nèi)容類型(Content-Type),例如application/json, text/html。
produces: ['application/json'], // 指定返回的內(nèi)容類型,僅當(dāng)request請求頭中的(Accept)類型中包含該指定類型才返回。
securityDefinitions: { // 配置接口安全授權(quán)方式。
// apikey: {
// type: 'apiKey',
// name: 'clientkey',
// in: 'header',
// },
// oauth2: {
// type: 'oauth2',
// tokenUrl: 'http://petstore.swagger.io/oauth/dialog',
// flow: 'password',
// scopes: {
// 'write:access_token': 'write access_token',
// 'read:access_token': 'read access_token',
// },
// },
},
enableSecurity: false, // 是否啟用授權(quán),默認(rèn) false(不啟用)。
// enableValidate: true, // 是否啟用參數(shù)校驗(yàn),默認(rèn) true(啟用)。
routerMap: true, // 是否啟用自動(dòng)生成路由,默認(rèn) true (啟用)。
enable: true, // 默認(rèn) true (啟用)。
};
// 數(shù)據(jù)庫配置信息。
exports.sequelize = {
dialect: 'mysql', // 數(shù)據(jù)庫類型,支持 mysql,sqlite,mssql,pgsql,oracle。
host: "localhost", // 數(shù)據(jù)庫服務(wù)器地址。
port: 3344, // 數(shù)據(jù)庫連接端口號。
database: "database", // 數(shù)據(jù)庫名稱。
username: "username", // 數(shù)據(jù)庫登錄用戶名。
password: "password", // 數(shù)據(jù)庫登錄密碼。
define: {
freezeTableName: true, // Model 對應(yīng)的表名將與model名相同。
timestamps: false // 默認(rèn)情況下,Sequelize會(huì)將createdAt和updatedAt的屬性添加到模型中,以便您可以知道數(shù)據(jù)庫條目何時(shí)進(jìn)入數(shù)據(jù)庫以及何時(shí)被更新( 確實(shí)是太方便了,然而我們一般用不到 ....)。
}
};
/* egg_folder/config/plugin.js */
'use strict';
// 配置 egg-swagger-doc 插件信息。
exports.swaggerdoc = {
enable: true, // 是否啟用。
package: 'egg-swagger-doc', // 指定包名稱。
};
// 配置 egg-sequelize 插件信息。
exports.sequelize = {
enable: true, // 是否啟用。
package: 'egg-sequelize', // 指定包名稱。
};
? ? ? ? 之后在 package.json 的 script 里面添加把數(shù)據(jù)庫生成 models 的命令( -o 表示生成 models 的路徑,-h 表示主機(jī),-p 表示端口,-d 表示數(shù)據(jù)庫, -u 表示用戶名,-x 表示密碼。別問為什么這么命名.... )。
"scripts": {
"dev": "egg-bin dev --port 7007",
"dbload": "egg-sequelize-auto -o ./app/model -h localhost -p 3344 -d database -u username -x password",
"test": "echo \"Error: no test specified\" && exit 1"
},
? ? ? ? 然后直接運(yùn)行 npm run dbload ,就會(huì)生成相對應(yīng)的 model 了( 如果配置都沒錯(cuò)的話.... )。
編寫 Service 和 Controller
? ? ? ? 配置完畢,就可以開始寫 Service( 業(yè)務(wù)層 ) 了。
/* egg_folder/app/service/book.js */
'use strict';
const Service = require('egg').Service;
// 通過定義 Service 類的方式來編寫代碼,所有的 Service 文件都必須放在 app/service 目錄下。
class BookService extends Service {
constructor(ctx) {
super(ctx); // 調(diào)用父對象上的函數(shù)。
this.tableName = 'Book'; // 數(shù)據(jù)庫表名。
this.database = this.ctx.model[this.tableName]; // 獲取 model 下的表( model 相當(dāng)于數(shù)據(jù)庫的表 )。
}
/**
* 根據(jù)Id獲取表信息。
* @param {*} id
*/
async selectById (id) {
const result = await this.database.findByPk(id); // sequelize 內(nèi)置查詢方法。
return result;
}
}
module.exports = BookService;
? ? ? ? 然后開始寫 Controller ( 控制器 )調(diào)用 Service( 業(yè)務(wù)層 ) 進(jìn)行業(yè)務(wù)處理。
/* egg_folder/app/controller/book.js */
'use strict';
const Controller = require('egg').Controller;
/**
* @controller BookController( 注釋必寫,swagger-doc是根據(jù)這段注釋來生成接口的 )。
*/
class BookController extends Controller {
/** ( 注釋必寫,swagger-doc是根據(jù)這段注釋來生成接口詳細(xì)信息的 )。
* @summary 根據(jù)ID查詢信息。
* @description 根據(jù)ID查詢信息。
* @router get /version01/controllers/selectById ( get 表示設(shè)置請求為 get 請求,最后的 selectById 對應(yīng)下面的 selectById 方法 )。
* @request query integer Id 需要去查新的ID。( get 對應(yīng) query 請求,請求值設(shè)定為 integer 純數(shù)字類型,ID 為請求的字段,注意大小寫,和下面的方法要一一對應(yīng),不然會(huì)報(bào)錯(cuò) )。
* @response 200 JsonBody 返回結(jié)果。( 對應(yīng) contract 里面的驗(yàn)證屬性,下面會(huì)提到 。)
*/
async selectById () {
const ctx = this.ctx; // 當(dāng)前請求的上下文 Context 對象的實(shí)例,通過它我們可以拿到框架封裝好的處理當(dāng)前請求的各種便捷屬性和方法。
const service = this.service; // 應(yīng)用定義的 Service,通過它我們可以訪問到其他業(yè)務(wù)層,等價(jià)于 this.ctx.service 。
const param = ctx.query; // 獲取請求參數(shù)。
const result = await service.book.selectById(param.Id); // 查找 service/book.js 下的 selectById 方法。
this.JsonBody(result);
}
/*
* 對返回的數(shù)據(jù)結(jié)果進(jìn)行封裝。
*/
JsonBody (data) {
this.ctx.body = {
result: data,
};
}
}
module.exports = BookController;
上面 Controller 的注釋必須要按照規(guī)范填寫,不然 swagger-doc 會(huì)無法生成對應(yīng)的結(jié)果,甚至報(bào)錯(cuò)。具體格式可以去 swagger-doc 的官網(wǎng)或 github 查看。
編寫 Contract
? ? ? ? Contract 相當(dāng)于驗(yàn)證,在接收請求和返回?cái)?shù)據(jù)的時(shí)候?qū)?shù)據(jù)進(jìn)行格式約束。
/* egg_folder/app/contract/format.js */
module.exports = {
JsonBody: { // 這個(gè)名字對應(yīng)上面 Controller 注釋的@response 的 JsonBody。
result: { type: 'string' }, // 服務(wù)器返回的數(shù)據(jù)。
},
};
? ? ? ? 最后,cmd 運(yùn)行 npm run dev,然后再瀏覽器上輸入 URL( 由于用了 swagger-doc,所以要在后面加上 /swagger-ui.html 來訪問 swagger-ui,嫌麻煩的也可以自己改 )。
至此 大功告成,利用這些已經(jīng)可以完成對數(shù)據(jù)的增刪查改了,這是我寫的一個(gè)小 DEMO,已經(jīng)成功完成增刪查改。
總結(jié)步驟:
- 初始化項(xiàng)目;
- 建立項(xiàng)目目錄;
- 配置 Cookie 秘鑰;
- 配置路由;
- 安裝和配置 Swagger 和 Sequelize;
- 根據(jù)數(shù)據(jù)庫生成對應(yīng)的 Model;
- 編寫 Service( 業(yè)務(wù)層 ) 和 Controller( 控制器 );
- 編寫 Contract 對格式進(jìn)行約束;
- 最后
npm run dev運(yùn)行項(xiàng)目,結(jié)束