eggjs全栈开发规范

*/

方法名(参数) {

// 处理逻辑

return 返回值

},

get 属性名() {

// 属性相关逻辑

return 属性

},

set 属性名(值) {

this.set(键, 值)

},}

2.application.js

对应Application对象

访问方式:

  • ctx.app

  • Controller, Helper, Service中都能使用this.app访问,例如this.app.config访问配置对象

  • Middleware中使用 ctx.app 访问

  • 将app对象作为函数的第一个参数注入 module.exports = app => {}

3.context.js

对应context对象

访问方式:

  • middleware中this就是 ctx

  • controller中使用this.ctx访问

  • helper,service中使用this.ctx访问

4.request.js

对应request.js对象

访问方式:

  • ctx.request

相关方法:

  • ctx.request.body 获取客户端请求的body参数

  • ctx.request.headers 获取客户端请求的header

  • ctx.request.query/ctx.query 获取URL内的参数

  • ctx.request.params 获取路由配置的参数

5.response.js

对应response.js对象

访问方式:

  • ctx.response

相关方法:

  • ctx.response.body/ctx.body 响应给客户端的body参数

6.helper.js

工具类,将请求成功和请求失败返回封装的函数以及错误码的封装写到里面

访问方式:

  • ctx.helper

  • 若要在非请求状态下,调用ctx,比如service中使用ctx.helper,则使用以下方法

const { app } = this.ctx;const ctx = app.createAnonymousContext();

ctx.helper.ROOTURL //此变量即可调用

封装状态码,将其解释写在helper里,方便调用

module.exports = {

errorCode: {

200: ‘请求成功。客户端向服务器请求数据,服务器返回相关数据’,

201: ‘资源创建成功。客户端向服务器提供数据,服务器创建资源’,

202: ‘请求被接收。但处理尚未完成’,

204: ‘客户端告知服务器删除一个资源,服务器移除它’,

206: ‘请求成功。但是只有部分回应’,

400: ‘请求无效。数据不正确,请重试’,

401: ‘请求没有权限。缺少API token,无效或者超时’,

403: ‘用户得到授权,但是访问是被禁止的。’,

404: ‘发出的请求针对的是不存在的记录,服务器没有进行操作。’,

406: ‘请求失败。请求头部不一致,请重试’,

410: ‘请求的资源被永久删除,且不会再得到的。’,

422: ‘请求失败。请验证参数’,

500: ‘服务器发生错误,请检查服务器。’,

502: ‘网关错误。’,

503: ‘服务不可用,服务器暂时过载或维护。’,

504: ‘网关超时。’,

}}

在Controller中响应客户端时,使用

this.ctx.body = {

code: 400,

message: this.ctx.helper.errorCode[400],

data:{

error:‘err’

}}

封装请求成功的方法

module.exports= {

success: ({ ctx, code=200, res=null }) => {

ctx.status = 200

ctx.body = {

code: code,

message: ctx.helper.errorCode[code],

data: res

}

}}

封装请求失败的方法

module.exports = {

fail: ({ ctx, code=500, res=null }) => {

ctx.status = 200

ctx.body = {

code: code,

message: ctx.helper.errorCode[code],

data: {

error: res

}

}

}}

请求封装使用例子

ctx.helper.success({ ctx, code:200, res:‘success’ })

ctx.helper.fail({ ctx, code:500, res:‘fail’ })

配置文件

1.plugin.js

引入第三方插件

代码格式:

exports.插件名 = {

enable: true,

package: ‘库名’}

例如:

exports.jwt = {

enable: true,

package: ‘egg-jwt’}

2.config.{{env}}.js

访问方式:

  • this.app.config

  • this.config

代码格式:

‘use strict’

module.exports = appInfo => {

const config = exports = {}

// 全局变量

config.变量名=‘’

// 插件名

config.插件名= {

// 相关配置

}}

例如:

‘use strict’

module.exports = appInfo => {

const config = exports = {}

// redis 配置

config.redis = {

client: {

port: process.env.RS_PORT || 6379,

host: process.env.RS_HOST || ‘0.0.0.0’,

password: process.env.RS_PASSWORD || ‘’,

db: 0,

}

}}

  • 默认配置放置在config.default.js,所有环境都会加载

  • 本地环境使用config.local.js

  • 开发环境使用config.prod.js

Middleware

中间件。对于一些错误拦截,请求处理,需要使用中间件完成。

配置方法:

  • 文件名命名使用下划线分割,在config.{{env}}.js文件中的middleware中配置,使用的是驼峰方式配置

例如:中间件文件名为demo-middleware 在config.{{env}}.js中的配置

config.middleware = [‘demoMiddleware’]

  • 在路由中配置,同样使用中间件时名称使用驼峰方式

例如:

module.exports = app => {

const demo = app.middleware.demoMiddleware()

app.router.get(‘/url’, demo, app.controller.demo)}

代码格式:

‘use strict’

module.exports = () => {

return async function 方法名(ctx, next) {

await next()

// 相关逻辑

}}

例如:

‘use strict’

module.exports = () => {

return async function notFoundHandler(ctx, next) {

await next()

if (ctx.status === 404 && !ctx.body) {

ctx.body = { error: ‘Not Found’ }

}

}}

处理错误信息的中间件

‘use strict’

module.exports = (option, app) => {

return async function(ctx, next) {

try {

await next()

} catch (err) {

app.emit(‘error’, err, this)

const status = err.status || 500

// 生产环境下不将错误内容返回给客户端

const error = status === 500 && app.config.env === ‘prod’

? ‘服务器错误,请联系管理员!’
err.message

ctx.helper.fail({ctx, code:status, res:error})

if(status === 422) {

ctx.helper.fail({ctx, code:status, res:err.errors})

}

}

}}

Service

保持Controller中逻辑简洁,以及业务逻辑的独立性,抽象出的Service可以被多个Controller调用。比如封装数据库操作的方法,API请求封装,第三方服务调用等。

访问方式:

  • this.service

Service支持多级目录,访问的时候可以通过目录名级联访问

  • app/service/biz/user.js => ctx.service.biz.user

  • app/service/sync_user.js => ctx.service.syncUser

  • app/service/HackerNews.js => ctx.service.hackerNews

代码格式(类的方式):

  • 类名使用首字母大写的驼峰命名法

  • 获取ctx,app,service,config,logger等对象使用const {对象} = this的方式获取

‘use strict’

const Service = require(‘egg’).Service

class 类名 extends Service {

/**

  • 方法说明

  • @param {参数类型} 参数名 参数说明

  • @return {返回值类型} 返回值名 返回值说明

*/

async 方法名(参数名) {

// 方法逻辑

return 返回值

}}

方法例子如下:

/**

  • 添加礼物

  • @param {string} livecode 编号

  • @return {int} giftnum 礼物数量

*/async addGift(livecode) {

const { app } = this

const giftnum = await this.ctx.model.query(UPDATE live SET gift=gift+1 WHERE livecode='${livecode}')

return giftnum}

Controller

三种功能,处理restful接口用户传来的参数;模板渲染;请求代理

访问方式:

  • 可以支持多级目录,访问的时候可以通过目录名级联访问

例如:

  • app.controller.post.create() // 代码放在 app/controller/post.js

  • app.controller.sub.post.create() // 代码放在 app/controller/sub/post.js

代码格式(类的方式):

  • 命名使用文件名首字母大写+Controller

  • 获取ctx,app,service,config,logger等对象使用const {对象} = this的方式获取

‘use strict’

const Controller = require(‘egg’).Controller

class 首字母大写的文件名+Controller extends Controller {

async 方法名() {

// 相关逻辑

ctx.body = {

// 返回给restful api接口的请求者

}

}}

例如:

‘use strict’

const Controller = require(‘egg’).Controller

class HomeController extends Controller {

async index() {

const {ctx} = this

if(ctx.isAuthenticated) {

ctx.body = {

user: ctx.user

}

} else {

ctx.body = {

user: ‘fail’

}

}

}}

module.exports = HomeController

router.js路由文件

描述请求的URL与controller建立的联系。将各个路由分离开来,分别放入router文件夹,并在router.js中引入

代码格式:

‘use strict’

/**

  • @param {Egg.Application} app - egg application

*/

module.exports = app => {

const { router, controller } = app

router.操作(‘/URL路径’, 中间件1, …中间件n, controller.文件名.方法) }

例如:

‘use strict’

/**

  • @param {Egg.Application} app - egg application

*/

module.exports = app => {

const { router, controller } = app

const roleCheck = app.middleware.roleCheck()

router.get(‘/user’, app.jwt, roleCheck, controller.user.getAllUser)}

带参路由,两种形式,以及获取参数的方式:

1.使用形如/uri/:id的uri,在Controller中使用ctx.request.params获取 例如:

// router.js

router.get(‘/user/:id’, controller.user.info)

// controllerconst { ctx } = thisconst { id } = ctx.request.params

1.使用形如/uri?id=1&age=1的uri,在Controller中使用ctx.query获取 例如:

// router.js

router.get(‘/user?id=1&age=1’, controller.user.msg)

// controllerconst { ctx } = thisconst { id, age } = ctx.query

安全配置


开发的时候关闭csrf,防止无法请求接口

// csrf关闭

config.security = {

csrf: {

enable: false

}}

Sequelize


安装

$ npm i -s egg-sequelize mysql2

启用与配置

在plugin.js中启用Sequlize

exports.sequelize = {

enable: true,

package: ‘egg-sequelize’}

在config.{{env}}.js中配置数据库连接.账户相关的信息,开发状态下将信息填入config.local.js;部署环境下,将信息填入config.prod.js

config.sequelize = {

dialect: ‘mysql’,

database: process.env.DB_DATABASE || ‘数据库名’,

host: process.env.DB_HOST || ‘IP地址’,

port: process.env.DB_PORT || ‘数据库端口号’,

username: process.env.DB_USER || ‘数据库用户名’,

password: process.env.DB_PASSWORD || ‘数据库密码’,

timezone: ‘+08:00’}

在package.json中配置数据库迁移命令

“scripts”: {

“migrate:new”: “egg-sequelize migration:create”,

“migrate:up”: “egg-sequelize db:migrate”,

“migrate:down”: “egg-sequelize db:migrate:undo”}

开发过程中配置自动同步数据库(仅开发模式),在app.js中写入

module.exports = app => {

if(app.config.env === ‘local’) {

app.beforeStart(async () => {

await app.model.sync({force:true})

})

}}

model数据模型开发

文档参考:https://demopark.github.io/sequelize-docs-Zh-CN/models-definition.html

  • 文件名为表名

  • 在文件前面引入需要的字段类型const {类型} = Sequelize

代码格式:

‘use strict’

module.exports = app => {

const {类型} = app.Sequelize

const 首字母大写的表名 = app.model.define(‘表名’, {

字段名: {

type: 类型,

// 其他属性

// 是否是唯一

unique: true,

// 定义主键

primaryKey: true,

// 自增

autoIncrement: true,

// 校验

validate: {

is: [“1+$”,‘i’], // 只允许字母

is: /2+$/i, // 与上一个示例相同,使用了真正的正则表达式

not: [“[a-z]”,‘i’], // 不允许字母

isEmail: true, // 检查邮件格式 (foo@bar.com)

isUrl: true, // 检查连接格式 (http://foo.com)

isIP: true, // 检查 IPv4 (129.89.23.1) 或 IPv6 格式

isIPv4: true, // 检查 IPv4 (129.89.23.1) 格式

isIPv6: true, // 检查 IPv6 格式

isAlpha: true, // 只允许字母

isAlphanumeric: true, // 只允许使用字母数字

isNumeric: true, // 只允许数字

isInt: true, // 检查是否为有效整数

isFloat: true, // 检查是否为有效浮点数

isDecimal: true, // 检查是否为任意数字

isLowercase: true, // 检查是否为小写

isUppercase: true, // 检查是否为大写

notNull: true, // 不允许为空

isNull: true, // 只允许为空

notEmpty: true, // 不允许空字符串

equals: ‘specific value’, // 只允许一个特定值

contains: ‘foo’, // 检查是否包含特定的子字符串

notIn: [[‘foo’, ‘bar’]], // 检查是否值不是其中之一

isIn: [[‘foo’, ‘bar’]], // 检查是否值是其中之一

notContains: ‘bar’, // 不允许包含特定的子字符串

len: [2,10], // 只允许长度在2到10之间的值

isUUID: 4, // 只允许uuids

isDate: true, // 只允许日期字符串

isAfter: “2011-11-05”, // 只允许在特定日期之后的日期字符串

isBefore: “2011-11-05”, // 只允许在特定日期之前的日期字符串

max: 23, // 只允许值 <= 23

min: 23, // 只允许值 >= 23

isCreditCard: true, // 检查有效的信用卡号码

// 也可以自定义验证:

isEven(value) {

if (parseInt(value) % 2 != 0) {

throw new Error(‘Only even values are allowed!’)

// 我们也在模型的上下文中,所以如果它存在的话,

// this.otherField会得到otherField的值。

}

}

}

}

},{

// 配置表名

tableName: ‘表名’,

// 不添加时间戳属性 (updatedAt, createdAt)

timestamps: true,

// 不删除数据库条目,但将新添加的属性deletedAt设置为当前日期(删除完成时)。

// paranoid 只有在启用时间戳时才能工作

paranoid: true,

// 不使用驼峰样式自动添加属性,而是下划线样式,因此updatedAt将变为updated_at

underscored: true,

// 禁用修改表名; 默认情况下,sequelize将自动将所有传递的模型名称(define的第一个参数)转换为复数。 如果你不想这样,请设置以下内容

freezeTableName: true,

// 不使用createdAt

createdAt: false,

// 我想 updateAt 实际上被称为 updateTimestamp

updatedAt: ‘updateTimestamp’,

})

首字母大写的表名.associate = function() {

// 表关联

}

return 首字母大写的表名}

例如:

‘use strict’

module.exports = app => {

const {STRING} = app.Sequelize

const Role = app.model.define(‘role’, {

name: {

type: STRING,

unique: true,

allowNull: false

}

}, {

timestamps: true,

tableName: ‘role’,

underscored: false

})

Role.associate = function() {

app.model.Role.hasMany(app.model.UserRole)

}

return Role}

migrations的使用

  • 使用yarn migrate:new生成初始化文件。

  • 将需要生成的表中的字段填入文件的up方法里,在down中填入删除表的方法。

  • 若需生成数据表,则使用yarn migrate:up。

  • 若需要删除数据表,则使用yarn migrate:down。

  • migrations文件命名为’时间+表名.js’。

  • 数据库迁移中要在up方法中要添加id字段、时间字段createAt和updateAt。

操作数据库

一般在Service中进行数据库操作,常用方法有findOne, findAll, create, destory, update等。文档参考:https://demopark.github.io/sequelize-docs-Zh-CN/models-usage.html

例子:

const result = await this.ctx.service.role.create({

name: name})

Redis


安装

$ npm i -s egg-redis

启用与配置

在plugin.js中启用Sequlize

exports.redis = {

enable: true,

package: ‘egg-redis’}

在config.{{env}}.js中配置数据库连接

config.redis = {

client: {

port: process.env.RS_PORT || ‘Redis主机端口号’,

host: process.env.RS_HOST || ‘Redis主机地址’,

password: process.env.RS_PASSWORD || ‘’,

db: ‘缓存数据库名称’,

}}

使用方法

具体方法和redis原生方法基本一致,原生api地址:https://www.cheatography.com/tasjaevan/

cheat-sheets/redis/

调用方法:

  • app.redis

常用方法:

  • app.redis.expire(键名, 时间) 设置键的失效时间

  • app.redis.lpush(键名, 值) 存入列表

  • app.redis.lrange(键名, 起始位, 终止位) 读取列表

  • app.redis.set(键名, 值, 时间) 设置单一键值

  • app.redis.get(键名) 获取单一键值

Socket.IO


安装

$ npm i -s egg-socket.io uws

启用与配置

在plugin.js中启用Sequlize

exports.io = {

enable: true,

package: ‘egg-socket.io’}

在config.{{env}}.js中配置数据库连接

config.io = {

init: {

wsEngine: ‘uws’ // 使用uws

},

namespace: {

‘命名空间路径’: {

connectionMiddleware: [‘中间件名称’], // 处理客户端连接与断开连接时的中间件

packetMiddleware: [‘中间件名称’] // 处理客户端发送信息到服务端时的中间件

}

},

redis: {

host: process.env.RS_HOST || ‘Redis主机地址’,

port: process.env.RS_PORT || ‘Redis主机端口号’

}}

文件格式

新建名为io的文件夹,并在其中分别建立两个文件夹controller和middleware,控制器和中间件的文件命名格式以及编码格式与eggjs的一样目录如下:

io

├── controller

└── middleware

Socket.IO路由配置

通过io.of设置命名空间,route()方法第一个参数是订阅的话题,第二个是使用的控制器

app.io.of(‘/’).route(‘new message’, io.controller.chat.newMessage)

使用方法

1.获取url参数

const {参数} = this.socket.handshake.query

2.加入房间

this.ctx.socket.join(‘房间’) // 加入房间

3.获取客户端socketId

this.ctx.socket.id

4.获取当前房间所有客户端

this.app.io.of(‘/’).adapter.clients([房间], (err, clients) => {})

5.设置命名空间

const nsp = this.app.io.of(‘/’)

6.推送数据

  • this.ctx.socket.emit(‘主题’, ‘信息’) // 只有发送者能看到

  • this.ctx.socket.broadcast(‘主题’, ‘信息’) // 只要发送者不能看到,其他人都能看到

  • this.app.io.emit(‘主题’, ‘信息’) // 所有人都能看到

  • this.app.io.of(‘命名空间’).to(‘房间’).emit(‘主题’, ‘信息’) // 向该命名空间下该房间内所有客户端发送

参数校验


安装

$ npm i -s egg-validate

启用

在plugin.js中启用validate

exports.validate = {

enable: true,

package: ‘egg-validate’}

使用方法

validate有两个参数,第一个是需要校验的参数,另一个是需要校验的对象。

const { user } = this.ctx.request.bodythis.ctx.validate({

username: { type: ‘string’, required: true },

password: { type: ‘string’, required: true }}, user)

模板渲染(选用nunjucks)


  • 模板文件默认目录在app/view中

  • EggJS结合模板文档:https://eggjs.org/zh-cn/core/view.html

  • 模板语法文档:http://mozilla.github.io/nunjucks/templating.html

安装

$ npm i -s egg-view-nunjucks

启用与配置

在plugin.js中启用nunjucks

exports.nunjucks = {

enable: true,

package: ‘egg-view-nunjucks’}

在config.default.js中配置渲染引擎

1.对指定后缀文件使用模板引擎渲染

config.view = {

mapping: {

‘.nj’:‘nunjucks’

}}

2.渲染时无需写文件后缀

config.view = {

defaultExtension: ‘.nj’}

使用方法

Context对象存在三个接口使用模板引擎,使用renderString时需要指定模板引擎,如果定义了defaultViewEngine这里可以省略:

  • render(name, locals) 渲染模板文件,并赋值给ctx.body

  • renderView(name, locals) 渲染模板文件,仅返回不赋值

  • renderString(tpl, locals) 渲染模板字符串,仅返回不赋值

例子:

1.controller/home.js

async test() {

const ctx = this.ctx

await ctx.render(‘index’,{data:[

{id: 1, name: “test”},

{id: 2, name: “test”},

{id: 3, name: “test”},

{id: 4, name: “test”},

{id: 5, name: “test”}

]})}

···

2.router.js

router.get(‘/’, controller.home.test);

···

3.view/index.nj

test

{% for item in data %}

{{item.name}}

{% endfor%}

静态文件

最后

好了,这就是整理的前端从入门到放弃的学习笔记,还有很多没有整理到,我也算是边学边去整理,后续还会慢慢完善,这些相信够你学一阵子了。

做程序员,做前端工程师,真的是一个学习就会有回报的职业,不看出身高低,不看学历强弱,只要你的技术达到应有的水准,就能够得到对应的回报。

学习从来没有一蹴而就,都是持之以恒的,正所谓活到老学到老,真正懂得学习的人,才不会被这个时代的洪流所淘汰。


  1. a-z ↩︎

  2. a-z ↩︎

  • 14
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值