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
寫(xiě)第一個(gè)api接口
安裝vscode擴(kuò)展
創(chuàng)建控制器
async index() { const { ctx } = this; // 獲取路由get傳值參數(shù)(路由:id) ctx.params; // 獲取url的問(wèn)號(hào)get傳值參數(shù) ctx.query; // 響應(yīng) ctx.body = '響應(yīng)'; // 狀態(tài)碼 ctx.status = 201; }
編寫(xiě)路由
基礎(chǔ)用法
// router.js router.get('/admin/:id', controller.admin.index); // controller async index() { const { ctx } = this; // 獲取路由get傳值參數(shù)(路由:id) ctx.params; // 獲取url的問(wèn)號(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
接下來(lái), 你只需要在 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開(kāi)啟跨域
文檔: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)寫(xiě)入時(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工具來(lái)實(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 # 如果有問(wèn)題需要回滾,可以通過(guò) `db:migrate:undo` 回退一個(gè)變更 # npx sequelize db:migrate:undo # 可以通過(guò) `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)寫(xiě)入時(shí)間戳 tableName: 'user', // 自定義數(shù)據(jù)表名稱 }); return User; };
這個(gè) Model 就可以在 Controller 和 Service 中通過(guò) app.model.User
或者 ctx.model.User
訪問(wèn)到了,例如我們編寫(xiě) 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); // 通過(guò)主鍵查詢單個(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 操作的接口就開(kāi)發(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, // 控制中間件是否開(kāi)啟。 match:'/news', // 設(shè)置只有符合某些規(guī)則的請(qǐng)求才會(huì)經(jīng)過(guò)這個(gè)中間件(匹配路由) ignore:'/shop' // 設(shè)置符合某些規(guī)則的請(qǐng)求不經(jīng)過(guò)這個(gè)中間件。 /** 注意: 1. match 和 ignore 不允許同時(shí)配置 2. 例如:match:'/news',只要包含/news的任何頁(yè)面都生效 **/ // match 和 ignore 支持多種類型的配置方式:字符串、正則、函數(shù)(推薦) match(ctx) { // 只有 ios 設(shè)備才開(kāi)啟 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作一個(gè)通用向?qū)дf(shuō)明
向?qū)Э梢宰屇愕木W(wǎng)站用戶快速上手使用你的web應(yīng)用,提高網(wǎng)站的吸引力。向?qū)б话惴譃楹脦讉€(gè)步驟,每個(gè)步驟收集一些數(shù)據(jù),并且支持退回功能,所有步驟完成后可以得到每一步的收集結(jié)果。這里給大家展示一種比較通用,靈活且簡(jiǎn)單的向?qū)Э蚣堋?/div> 2011-08-08微信小程序?qū)崿F(xiàn)兩邊小中間大的輪播效果的示例代碼
這篇文章主要介紹了微信小程序?qū)崿F(xiàn)兩邊小中間大的輪播效果的示例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-12-12MVVM框架下實(shí)現(xiàn)分頁(yè)功能示例
分頁(yè)這種組件,幾乎每一種框架都有這樣的組件,這篇文章主要介紹了MVVM框架下實(shí)現(xiàn)分頁(yè)功能示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-06-06動(dòng)態(tài)生成的IFRAME,設(shè)置SRC時(shí)的問(wèn)題解決分析
動(dòng)態(tài)生成的IFRAME,設(shè)置SRC時(shí)的,不同位置帶來(lái)的影響。 以下所說(shuō)的是在IE7下運(yùn)行的。IE6下也是同樣。 在這個(gè)blog中,直接點(diǎn)擊運(yùn)行代碼,和把下面代碼保存到為網(wǎng)頁(yè)在運(yùn)行(以本地文件或域名訪問(wèn)),效果不一樣。 先看例子:2008-04-04淺談typescript中keyof與typeof操作符用法
本文主要介紹了typescript中keyof與typeof操作符用法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06全面了解addEventListener和on的區(qū)別
下面小編就為大家?guī)?lái)一篇全面了解addEventListener和on的區(qū)別。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-07-07最新評(píng)論