eggjs全棧開發規範

編碼規範

編碼格式與語法

項目默認編碼格式統一爲UTF-8格式,語法採用ES6+語法

代碼註釋

註釋符號後要有一個空格

函數/方法註釋

函數/方法註釋放置於函數/方法的上方,主要描述函數/方法功能以及參數類型,參數和返回值說明

/**
 * 功能
 * @param  {參數類型} 參數名 參數說明
 * @return {返回值類型} 返回值 返回值說明
 */

單行註釋

對代碼做簡要說明

// 功能簡要說明

代碼分段及縮進

每段代碼應放在一個代碼塊中。塊內的代碼都應該統一地縮進一個單位。

使用空格作爲縮進

使用2個空格作爲一個縮進單位。

代碼塊符號

代碼塊的開始符號要放在行尾,不可單獨一行;代碼塊結束符號要單獨一行。

function demo() { // 代碼塊開始符號
  // ...} // 代碼塊結束符號

空白行分隔

不同功能或多個代碼塊之間,使用空白行分隔

/**
 * 方法1
 */function demo1() {
} 
/**
 * 方法2
 */function demo1() {
  ...... // 代碼塊1

  ...... // 代碼塊2} 

命名規則

全部使用英文單詞

文件命名

  • 控制器,模型,服務的文件名使用小寫名詞。
  • 中間件使用下劃線分割命名。
  • 使用中間件使用將下劃線命名改爲首字母小寫的駝峯命名。
  • 控制器,服務的類名爲首字母大寫的文件名+Controller。

變量與常量命名

儘量使用const代替let
若變量需要改變才使用let
固定常量爲全大寫,其餘使用首字母小寫的駝峯命名法

函數/方法命名

使用首字母小寫的駝峯命名

引號

一般情況使用單引號,若字符串拼接,使用"``“和”${}"

分號

不用分號

項目規範

庫的安裝和項目的初始化全部使用npm

項目生成

egg-init 項目名 --type=simple
npm install

安裝第三方庫

$ npm i --save 庫名

項目運行

項目開發運行

$ npm run dev

項目部署運行

$ npm run start

項目docker運行

向package.json中的scripts鍵添加一個值

"scripts": {
  ......
  "docker": "egg-sequelize db:migrate && egg-scripts start",
  ......}

2.4 項目目錄

├── app.js
├── app
│   ├── router.js 
│   ├── controller            
│   ├── extend           
│   ├── middleware
│   ├── service
│   ├── public
│   ├── view
│   ├── router
│   ├── io     (自建socket.io目錄)
│   │   ├── middleware
│   │   └── controller
│   └── model (自建Sequelize目錄)
├── config
│   ├── plugin.js
│   ├── config.default.js
│   └── config.prod.js
├── migrations(自建Sequelize目錄)
├── logs
└── test
    └── app
        ├── middleware
        └── controller

以上目錄約定如下:
1.app/router.js 用於配置URL路由規則。
2.app/controller/ 用於解析用戶輸入,處理後返回響應結果。
3.app/extend/ 用於框架內部對象的拓展(request,response,context,application)和工具類(helper)的編寫。
4.app/middleware/ 用於編寫中間件。
5.app/service/ 用於編寫業務邏輯,如數據庫操作的封裝,api請求的封裝等。
6.app/public/ 用於放置靜態文件。
7.app/view/ 用於放置模板文件(可能不需要)。
8.app/model/ 用於放置數據模型(若使用Sequelize)。
9.app/router/ 用戶放置分離的路由
10.migrations/ 用與放置數據庫遷移的文件。
11.logs/ 日誌存放目錄。
12.test/ 測試文件目錄。
13.app.js 用於自定義啓動時的初始化工作。

項目相關文件說明

所有代碼均在’use strict’嚴格模式下開發

extend

包含四個對象對應的文件,以及一個helper工具類

1.代碼格式

'use strict'

module.exports = {
  /**
   * 方法說明
   * @param {參數類型} 參數名 參數說明
   * @return {返回值類型} 返回值名 返回值說明
   */
  方法名(參數) {
    // 處理邏輯
    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']
```javascript
* 在路由中配置,同樣使用中間件時名稱使用駝峯方式
例如:
```javascript
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: ["^[a-z]+$",'i'],     // 只允許字母
        is: /^[a-z]+$/i,          // 與上一個示例相同,使用了真正的正則表達式
        not: ["[a-z]",'i'],       // 不允許字母
        isEmail: true,            // 檢查郵件格式 ([email protected])
        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
```javascript
router.get('/', controller.home.test);
···
3.view/index.nj
```html
<div class="wrap">
  <button class="btn btn-warning">test</button>
  {% for item in data %}
  <div>{{item.name}}</div>
  {% endfor%}</div>

靜態文件

1.到config.js中開啓static,默認是註釋掉的,egg-static屬於內置插件

exports.static = true;

2.靜態文件存放路徑:app/public
3.引用方式

  • 外部查看
    http://localhost:7001/public/文件名.後綴
  • 模板調用
    public/+文件在public文件夾下的路徑

Git規範

分支類型

feature

  • 功能開發分支
    bugfix
  • 問題修復分支
    develop
  • 在項目沒有經過測試並達到生產環境前,全部合併到dev分支,開發新功能也從dev分支遷出
    master
  • 生產環境版本

分支命名

基本格式(全爲英文)

  • feature/功能名稱
  • bugfix/bug名稱
    例子:
  • feature/user
  • bugfix/login_error

開發流程

master 遷出develop分支 -> develop分支遷出feature功能分支 -> 提交pr審查代碼 -> 提交QA -> 合併到develop -> 合併到master

提交格式

使用git cz代替git commit
插件地址:https://github.com/commitizen/cz-cli
1.安裝

$ npm install -g commitizen

2.在項目中運行下面命令,使其支持 Angular 的 Commit message 格式

$ commitizen init cz-conventional-changelog --save --save-exact

3.使用

$ git add .
$ git cz

4.格式的選擇

  • feat: 新功能提交
  • fix: 修復一個bug
  • docs: 只修改了文檔
  • style: 修改一些不會影響代碼含義的東西(空格,格式化,分號等等)
  • refactor: 代碼更改既不修復錯誤也不添加功能
  • perf: 代碼更改提高了性能
  • test: 添加缺少的測試或更正現有的測試
  • build: 影響構建系統的更改或外部依賴關係的更改 (例如: gulp, broccoli, npm)
  • ci: 改變測試配置文件和腳本(例如: T
  • ravis, Circle, BrowserStack, SauceLabs)
  • chore: 沒有更改 src 或者 test 文件的更改
  • revert: 恢復之前的提交
    5.詳細問題
    記錄修改的文件名?
  • What is the scope of this change (e.g. component or file name)? (press enter t
    o skip)
    簡短的說明?
  • Write a short, imperative tense description of the change:
    長說明(可不寫)?
  • Provide a longer description of the change: (press enter to skip)
    是否用破壞性修改?
  • Are there any breaking changes?
    修改是否影響到一些開啓的issues(可不寫)?
  • Does this change affect any open issues?

接口自測

軟件

  • 名稱:postman
  • 下載地址:https://www.getpostman.com/

操作規範

對負責的模塊單獨建立文件夾,將接口存放進去。將接口請求後的數據格式與約定返回的數據格式做對比。

RESTful API規範

請求協議

  • http
  • https

請求方法

請求方法 功能
GET 獲取資源
POST 新增資源
PUT 更新整個資源
PATCH 更新個別資源
DELETE 刪除資源

狀態碼

成功狀態碼

狀態碼 定義
200 請求成功。客戶端向服務器請求數據,服務器返回相關數據
201 資源創建成功。客戶端向服務器提供數據,服務器創建資源
202 請求被接收。但處理尚未完成
204 客戶端告知服務器刪除一個資源,服務器移除它

錯誤狀態碼

狀態碼 錯誤描述
400 請求無效。數據不正確,請重試
401 請求沒有權限。缺少API token,無效或者超時
403 請求未被授權。當前權限無法獲取指定的資源
404 請求失敗。請求資源不存在
406 請求失敗。請求頭部不一致,請重試
422 請求失敗。請驗證參數

服務器錯誤狀態碼

狀態碼 定義
500 服務器發生錯誤,請檢查服務器
502 網關錯誤
503 服務不可用,服務器暫時過載或維護
504 網關超時

自定義狀態碼

具體而定

版本號

  • 通過版本號可以區分api的版本
  • 通過/api/v1/*代表v1版本
  • 通過/api/v2/*代表v2版本

URL規範

RESTful API的所有操作都是針對特定資源進行的。資源就是URL所表示的,URL需要符合以下規範:

  • 只能是名詞不能是動詞
  • 小寫字符
  • 不可使用下劃線’_’,可以使用連字符’-’
  • CRUD不可出現在URL中
  • 參數列表要用encode
  • 避免層級過深的URI,儘量使用查詢參數代替路徑中的實體導航,如GET /user?sex=female&age=30
    具體形式如下:
    1./api/{資源名}/{描述名}
    2./api/{資源名}/{對象id}/{描述名}
    例子:
  • GET http://www.demo.com/api/v1/user/1 獲取用戶1的信息
  • POST http://www.demo.com/api/v1/user/login 登錄
  • PUT http://www.demo.com/api/v1/user/1 更新用戶1的全部信息
  • DELETE http://www.demo.com/api/v1/user/1 刪除用戶1
  • PATCH http://www.demo.com/api/v1/user/1 更新用戶1部分信息
  • GET http://www.demo.com/api/v1/user/1/role 獲取用戶1的權限信息

請求體格式

{
  "data": {
    "鍵":"值"
  }}

例子:

{
  "data": {
    "username":"klren",
    "password":"123456"
  }}

返回體格式

正常返回

{
  "code": "狀態碼",
  "msg":"狀態描述",
  "data":"具體內容"}

例子:

{
  "code": 200,
  "msg": "success",
  "data": {
    "username": "klren",
    "email":"[email protected]"
  }}

錯誤返回

{
  "code": "錯誤狀態碼",
  "msg": "錯誤信息",
  "data": {
    "error": "錯誤詳情",
  }}

例子:

{
  "code": "400",
  "msg": "數據格式錯誤",
  "data": {
    "error": "格式錯誤"
  }}

過濾、分頁與排序

採用’?'符號進行這些操作

過濾

使用唯一的查詢參數進行過濾
例子:

  • GET /user?id=1 返回id等於1的用戶

排序

使用sort,後面跟着鍵名和排序方式
例子:

  • GET /user?sort=age,desc 查詢用戶根據年齡倒序

分頁

使用limit和offset,後面跟具體數字。limit後面跟每頁最多數據量,offset後面跟數據起始位。
請求例子:

  • GET /user?limit=10&offset=0 獲取從第0位開始的10個用戶數據
    返回體例子:
{
  "code":200,
  "message":'success',
  "data":{
    "total": 0,
    "pageIndex": 1,
    "pageSize": 10,
    "list":[{},{},{}...]
  }}

請求格式

  • Content-Type:application/json 數據發送
  • Content-Type:multipart/form-data 文件上傳
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章