egg.js的基本使用實(shí)例
安裝egg.js
全局切換鏡像: npm config set registry https://registry.npm.taobao.org
我們推薦直接使用腳手架,只需幾條簡(jiǎn)單指令,即可快速生成項(xiàng)目(npm >=6.1.0):
mkdir egg-example && cd egg-example npm init egg --type=simple --registry https://registry.npm.taobao.org npm i
啟動(dòng)項(xiàng)目:
npm run dev open http://localhost:7001
寫第一個(gè)api接口
安裝vscode擴(kuò)展
創(chuàng)建控制器
async index() {
const { ctx } = this;
// 獲取路由get傳值參數(shù)(路由:id)
ctx.params;
// 獲取url的問號(hào)get傳值參數(shù)
ctx.query;
// 響應(yīng)
ctx.body = '響應(yīng)';
// 狀態(tài)碼
ctx.status = 201;
}
編寫路由
基礎(chǔ)用法
// router.js
router.get('/admin/:id', controller.admin.index);
// controller
async index() {
const { ctx } = this;
// 獲取路由get傳值參數(shù)(路由:id)
ctx.params;
// 獲取url的問號(hào)get傳值參數(shù)
ctx.query;
}
資源路由
// app/router.js
module.exports = app => {
const { router, controller } = app;
router.resources('posts', '/api/posts', controller.posts);
// app/controller/v1/users.js
router.resources('users', '/api/v1/users', controller.v1.users);
};
上面代碼就在 /posts 路徑上部署了一組 CRUD 路徑結(jié)構(gòu),對(duì)應(yīng)的 Controller 為 app/controller/posts.js 接下來, 你只需要在 posts.js 里面實(shí)現(xiàn)對(duì)應(yīng)的函數(shù)就可以了。
| Method | Path | Route Name | Controller.Action |
|---|---|---|---|
| GET | /posts | posts | app.controllers.posts.index |
| GET | /posts/new | new_post | app.controllers.posts.new |
| GET | /posts/:id | post | app.controllers.posts.show |
| GET | /posts/:id/edit | edit_post | app.controllers.posts.edit |
| POST | /posts | posts | app.controllers.posts.create |
| PUT | /posts/:id | post | app.controllers.posts.update |
| DELETE | /posts/:id | post | app.controllers.posts.destroy |
// app/controller/posts.js
// 列表頁(yè)
exports.index = async () => {};
// 新增表單頁(yè)
exports.new = async () => {};
// 新增邏輯
exports.create = async () => {};
// 詳情頁(yè)
exports.show = async () => {};
// 編輯表單頁(yè)
exports.edit = async () => {};
// 更新邏輯
exports.update = async () => {};
// 刪除邏輯
exports.destroy = async () => {};
路由分組
// app/router.js
module.exports = app => {
require('./router/news')(app);
require('./router/admin')(app);
};
// app/router/news.js
module.exports = app => {
app.router.get('/news/list', app.controller.news.list);
app.router.get('/news/detail', app.controller.news.detail);
};
// app/router/admin.js
module.exports = app => {
app.router.get('/admin/user', app.controller.admin.user);
app.router.get('/admin/log', app.controller.admin.log);
};
關(guān)閉csrf開啟跨域
文檔:https://www.npmjs.com/package/egg-cors
安裝npm i egg-cors --save
配置插件
// {app_root}/config/plugin.js
exports.cors = {
enable: true,
package: 'egg-cors',
};
config / config.default.js 目錄下配置
config.security = {
// 關(guān)閉 csrf
csrf: {
enable: false,
},
// 跨域白名單
domainWhiteList: [ 'http://localhost:3000' ],
};
// 允許跨域的方法
config.cors = {
origin: '*',
allowMethods: 'GET, PUT, POST, DELETE, PATCH'
};
數(shù)據(jù)庫(kù)
配置和創(chuàng)建遷移文件
配置 安裝并配置egg-sequelize插件(它會(huì)輔助我們將定義好的 Model 對(duì)象加載到 app 和 ctx 上)和mysql2模塊:
npm install --save egg-sequelize mysql2
在config/plugin.js中引入 egg-sequelize 插件
exports.sequelize = {
enable: true,
package: 'egg-sequelize',
};
在config/config.default.js
config.sequelize = {
dialect: 'mysql',
host: '127.0.0.1',
username: 'root',
password: 'root',
port: 3306,
database: 'eggapi',
// 中國(guó)時(shí)區(qū)
timezone: '+08:00',
define: {
// 取消數(shù)據(jù)表名復(fù)數(shù)
freezeTableName: true,
// 自動(dòng)寫入時(shí)間戳 created_at updated_at
timestamps: true,
// 字段生成軟刪除時(shí)間戳 deleted_at
paranoid: true,
createdAt: 'created_at',
updatedAt: 'updated_at',
deletedAt: 'deleted_at',
// 所有駝峰命名格式化
underscored: true
}
};
sequelize 提供了sequelize-cli工具來實(shí)現(xiàn)Migrations,我們也可以在 egg 項(xiàng)目中引入 sequelize-cli。
npm install --save-dev sequelize-cli
egg 項(xiàng)目中,我們希望將所有數(shù)據(jù)庫(kù) Migrations 相關(guān)的內(nèi)容都放在database目錄下,所以我們?cè)陧?xiàng)目根目錄下新建一個(gè).sequelizerc配置文件:
'use strict';
const path = require('path');
module.exports = {
config: path.join(__dirname, 'database/config.json'),
'migrations-path': path.join(__dirname, 'database/migrations'),
'seeders-path': path.join(__dirname, 'database/seeders'),
'models-path': path.join(__dirname, 'app/model'),
};
初始化 Migrations 配置文件和目錄
npx sequelize init:config npx sequelize init:migrations // npx sequelize init:models
行完后會(huì)生成database/config.json文件和database/migrations目錄,我們修改一下database/config.json中的內(nèi)容,將其改成我們項(xiàng)目中使用的數(shù)據(jù)庫(kù)配置:
{
"development": {
"username": "root",
"password": null,
"database": "eggapi",
"host": "127.0.0.1",
"dialect": "mysql",
"timezone": "+08:00"
}
}
創(chuàng)建數(shù)據(jù)庫(kù)
npx sequelize db:create
創(chuàng)建數(shù)據(jù)遷移表
npx sequelize migration:generate --name=init-user
1.執(zhí)行完命令后,會(huì)在database / migrations / 目錄下生成數(shù)據(jù)表遷移文件,然后定義
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
const { INTEGER, STRING, DATE, ENUM } = Sequelize;
// 創(chuàng)建表
await queryInterface.createTable('user', {
id: { type: INTEGER(20).UNSIGNED, primaryKey: true, autoIncrement: true },
username: { type: STRING(30), allowNull: false, defaultValue: '', comment: '用戶名稱', unique: true},
password: { type: STRING(200), allowNull: false, defaultValue: '' },
avatar_url: { type: STRING(200), allowNull: true, defaultValue: '' },
sex: { type: ENUM, values: ['男','女','保密'], allowNull: true, defaultValue: '男', comment: '用戶性別'},
created_at: DATE,
updated_at: DATE
});
},
down: async queryInterface => {
await queryInterface.dropTable('user')
}
};
執(zhí)行 migrate 進(jìn)行數(shù)據(jù)庫(kù)變更
# 升級(jí)數(shù)據(jù)庫(kù) npx sequelize db:migrate # 如果有問題需要回滾,可以通過 `db:migrate:undo` 回退一個(gè)變更 # npx sequelize db:migrate:undo # 可以通過 `db:migrate:undo:all` 回退到初始狀態(tài) # npx sequelize db:migrate:undo:all
模型
創(chuàng)建模型
// app / model / user.js
'use strict';
module.exports = app => {
const { STRING, INTEGER, DATE } = app.Sequelize;
// 配置(重要:一定要配置詳細(xì),一定要?。。。?
const User = app.model.define('user', {
id: { type: INTEGER(20).UNSIGNED, primaryKey: true, autoIncrement: true },
username: { type: STRING(30), allowNull: false, defaultValue: '', comment: '用戶名稱', unique: true},
password: { type: STRING(200), allowNull: false, defaultValue: '' },
avatar_url: { type: STRING(200), allowNull: true, defaultValue: '' },
sex: { type: ENUM, values: ['男','女','保密'], allowNull: true, defaultValue: '男', comment: '用戶性別'},
created_at: DATE,
updated_at: DATE
},{
timestamps: true, // 是否自動(dòng)寫入時(shí)間戳
tableName: 'user', // 自定義數(shù)據(jù)表名稱
});
return User;
};
這個(gè) Model 就可以在 Controller 和 Service 中通過 app.model.User 或者 ctx.model.User 訪問到了,例如我們編寫 app/controller/user.js:
// app/controller/user.js
const Controller = require('egg').Controller;
function toInt(str) {
if (typeof str === 'number') return str;
if (!str) return str;
return parseInt(str, 10) || 0;
}
class UserController extends Controller {
async index() {
const ctx = this.ctx;
let keyword = this.ctx.params.keyword;
let Op = this.app.Sequelize.Op;
ctx.body = await ctx.model.User.findAll({
where:{
id:{
[Op.gt]:6
},
username: {
[Op.like]:'%'+keyword+'%'
}
},
attributes:['id','username','sex'],
order:[
['id','DESC']
],
limit: toInt(ctx.query.limit),
offset: toInt(ctx.query.offset)
});
}
async show() {
let id = parseInt(this.ctx.params.id);
// 通過主鍵查詢單個(gè)數(shù)據(jù)
// let detail = await this.app.model.User.findByPk(id);
// if (!detail) {
// return this.ctx.body = {
// msg: "fail",
// data: "用戶不存在"
// }
// }
// 查詢單個(gè)
let detail = await this.app.model.User.findOne({
where: {
id,
sex: "女"
}
});
this.ctx.body = {
msg: 'ok',
data: detail
};
}
async create() {
const ctx = this.ctx;
const { name, age } = ctx.request.body;
// 創(chuàng)建單條記錄
const user = await ctx.model.User.create({ name, age });
// 批量創(chuàng)建
await ctx.model.User.bulkCreate([{
name: "第一個(gè)",
age: 15
},
{
name: "第二個(gè)",
age: 15
},
{
name: "第三個(gè)",
age: 15
}]);
ctx.status = 201;
ctx.body = user;
}
async update() {
const ctx = this.ctx;
const id = toInt(ctx.params.id);
const user = await ctx.model.User.findByPk(id);
if (!user) {
ctx.status = 404;
return;
}
const { name, age } = ctx.request.body;
await user.update({ name, age });
ctx.body = user;
}
async destroy() {
const ctx = this.ctx;
const id = toInt(ctx.params.id);
const user = await ctx.model.User.findByPk(id);
if (!user) {
ctx.status = 404;
return;
}
await user.destroy();
ctx.status = 200;
}
}
module.exports = UserController;
最后我們將這個(gè) controller 掛載到路由上:
// app/router.js
module.exports = app => {
const { router, controller } = app;
router.resources('user', '/user', controller.user);
};
針對(duì) users 表的 CURD 操作的接口就開發(fā)完了
錯(cuò)誤和異常處理
// app/middleware/error_handler.js
module.exports = (option, app) => {
return async function errorHandler(ctx, next) {
try {
await next();
} catch (err) {
// 所有的異常都在 app 上觸發(fā)一個(gè) error 事件,框架會(huì)記錄一條錯(cuò)誤日志
ctx.app.emit('error', err, ctx);
const status = err.status || 500;
// 生產(chǎn)環(huán)境時(shí) 500 錯(cuò)誤的詳細(xì)錯(cuò)誤內(nèi)容不返回給客戶端,因?yàn)榭赡馨舾行畔?
const error = status === 500 && ctx.app.config.env === 'prod'
? 'Internal Server Error'
: err.message;
// 從 error 對(duì)象上讀出各個(gè)屬性,設(shè)置到響應(yīng)中
ctx.body = { error };
if (status === 422) {
ctx.body.detail = err.errors;
}
ctx.status = status;
}
};
};
中間件
config.middleware = ['errorHandler'];
config.errorHandler = {
ceshi: 123,
// 通用配置(以下是重點(diǎn))
enable:true, // 控制中間件是否開啟。
match:'/news', // 設(shè)置只有符合某些規(guī)則的請(qǐng)求才會(huì)經(jīng)過這個(gè)中間件(匹配路由)
ignore:'/shop' // 設(shè)置符合某些規(guī)則的請(qǐng)求不經(jīng)過這個(gè)中間件。
/**
注意:
1. match 和 ignore 不允許同時(shí)配置
2. 例如:match:'/news',只要包含/news的任何頁(yè)面都生效
**/
// match 和 ignore 支持多種類型的配置方式:字符串、正則、函數(shù)(推薦)
match(ctx) {
// 只有 ios 設(shè)備才開啟
const reg = /iphone|ipad|ipod/i;
return reg.test(ctx.get('user-agent'));
},
};
參數(shù)驗(yàn)證
https://www.npmjs.com/package/egg-valparams
npm i egg-valparams --save
// config/plugin.js
valparams : {
enable : true,
package: 'egg-valparams'
},
// config/config.default.js
config.valparams = {
locale : 'zh-cn',
throwError: false
};
class XXXController extends app.Controller {
// ...
async XXX() {
const {ctx} = this;
ctx.validate({
system : {type: 'string', required: false, defValue: 'account', desc: '系統(tǒng)名稱'},
token : {type: 'string', required: true, desc: 'token 驗(yàn)證'},
redirect: {type: 'string', required: false, desc: '登錄跳轉(zhuǎn)'}
});
// if (config.throwError === false)
if(ctx.paramErrors) {
// get error infos from `ctx.paramErrors`;
}
let params = ctx.params;
let {query, body} = ctx.request;
// ctx.params = validater.ret.params;
// ctx.request.query = validater.ret.query;
// ctx.request.body = validater.ret.body;
// ...
ctx.body = query;
}
// ...
}
到此這篇關(guān)于egg.js的基本使用實(shí)例的文章就介紹到這了,更多相關(guān)egg.js基本使用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaScript記錄光標(biāo)在編輯器中位置的實(shí)現(xiàn)方法
這篇文章主要介紹了JavaScript記錄光標(biāo)在編輯器中位置的實(shí)現(xiàn)方法,涉及JavaScript鼠標(biāo)事件結(jié)合頁(yè)面元素的操作技巧,需要的朋友可以參考下2016-04-04
js給dropdownlist添加選項(xiàng)的小例子
js給dropdownlist添加選項(xiàng)的小例子,需要的朋友可以參考一下2013-03-03
七行JSON代碼把你的網(wǎng)站變成移動(dòng)應(yīng)用過程詳解
這篇文章主要介紹了七行JSON代碼把你的網(wǎng)站變成移動(dòng)應(yīng)用過程詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值2019-07-07
JavaScript彈出新窗口后向父窗口輸出內(nèi)容的方法
這篇文章主要介紹了JavaScript彈出新窗口后向父窗口輸出內(nèi)容的方法,涉及javascript窗口操作的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-04-04
JS實(shí)現(xiàn)的簡(jiǎn)單折疊展開動(dòng)畫效果示例
這篇文章主要介紹了JS實(shí)現(xiàn)的簡(jiǎn)單折疊展開動(dòng)畫效果,可實(shí)現(xiàn)類似百度頁(yè)面分享按鈕一樣的折疊展開動(dòng)畫效果,涉及javascript頁(yè)面元素屬性動(dòng)態(tài)操作相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2018-04-04
JS使用cookie實(shí)現(xiàn)DIV提示框只顯示一次的方法
這篇文章主要介紹了JS使用cookie實(shí)現(xiàn)DIV提示框只顯示一次的方法,涉及JavaScript基于cookie標(biāo)記控制頁(yè)面元素樣式修改的技巧,需要的朋友可以參考下2015-11-11
微信小程序?qū)崿F(xiàn)滾動(dòng)Tab選項(xiàng)卡
這篇文章主要為大家詳細(xì)介紹了微信小程序?qū)崿F(xiàn)滾動(dòng)Tab選項(xiàng)卡,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-11-11

