1.編碼規范
1.1 編碼格式與語法
項目默認編碼格式統一為UTF-8格式,語法采用ES6+語法
1.2 代碼注釋
注釋符號后要有一個空格
1.2.1 函數/方法注釋
函數/方法注釋放置于函數/方法的上方,主要描述函數/方法功能以及參數類型,參數和返回值說明
/**
* 功能
* @param {參數類型} 參數名 參數說明
* @return {返回值類型} 返回值 返回值說明
*/
1.2.2 單行注釋
對代碼做簡要說明
// 功能簡要說明
1.3 代碼分段及縮進
每段代碼應放在一個代碼塊中。塊內的代碼都應該統一地縮進一個單位。
1.3.1 使用空格作為縮進
使用2個空格作為一個縮進單位。
1.3.2 代碼塊符號
代碼塊的開始符號要放在行尾,不可單獨一行;代碼塊結束符號要單獨一行。
function demo() { // 代碼塊開始符號
// ...
} // 代碼塊結束符號
1.4 空白行分隔
不同功能或多個代碼塊之間,使用空白行分隔
/**
* 方法1
*/
function demo1() {
}
/**
* 方法2
*/
function demo1() {
...... // 代碼塊1
...... // 代碼塊2
}
1.5 命名規則
全部使用英文單詞
1.5.1 文件命名
- 控制器,模型,服務的文件名使用小寫名詞。
- 中間件使用下劃線分割命名。
- 使用中間件使用將下劃線命名改為首字母小寫的駝峰命名。
- 控制器,服務的類名為首字母大寫的文件名+Controller。
1.5.2 變量與常量命名
盡量使用const代替let
若變量需要改變才使用let
固定常量為全大寫,其余使用首字母小寫的駝峰命名法
1.5.3 函數/方法命名
使用首字母小寫的駝峰命名
1.6 引號
一般情況使用單引號,若字符串拼接,使用"``"和"${}"
1.7 分號
不用分號
2.項目規范
庫的安裝和項目的初始化全部使用yarn
2.1 項目生成
egg-init 項目名 --type=simple
yarn install
2.2 安裝第三方庫
$ yarn add 庫名
2.3 項目運行
2.3.1 項目開發運行
$ yarn dev
2.3.2 項目部署運行
$ yarn start
2.3.3 項目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
以上目錄約定如下:
- app/router.js 用于配置URL路由規則。
- app/controller/ 用于解析用戶輸入,處理后返回響應結果。
- app/extend/ 用于框架內部對象的拓展(request,response,context,application)和工具類(helper)的編寫。
- app/middleware/ 用于編寫中間件。
- app/service/ 用于編寫業務邏輯,如數據庫操作的封裝,api請求的封裝等。
- app/public/ 用于放置靜態文件。
- app/view/ 用于放置模板文件(可能不需要)。
- app/model/ 用于放置數據模型(若使用Sequelize)。
- app/router/ 用戶放置分離的路由
- migrations/ 用與放置數據庫遷移的文件。
- logs/ 日志存放目錄。
- test/ 測試文件目錄。
- app.js 用于自定義啟動時的初始化工作。
2.5 項目相關文件說明
所有代碼均在'use strict'嚴格模式下開發
2.5.1 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' })
2.5.2 配置文件
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
2.5.3 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})
}
}
}
}
2.5.4 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
}
2.5.5 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
2.5.6 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)
}
帶參路由,兩種形式,以及獲取參數的方式:
- 使用形如/uri/:id的uri,在Controller中使用ctx.request.params獲取 例如:
// router.js
router.get('/user/:id', controller.user.info)
// controller
const { ctx } = this
const { id } = ctx.request.params
- 使用形如/uri?id=1&age=1的uri,在Controller中使用ctx.query獲取 例如:
// router.js
router.get('/user?id=1&age=1', controller.user.msg)
// controller
const { ctx } = this
const { id, age } = ctx.query
2.6 安全配置
開發的時候關閉csrf,防止無法請求接口
// csrf關閉
config.security = {
csrf: {
enable: false
}
}
2.7 Sequelize
2.7.1 安裝
$ yarn add egg-sequelize mysql2
2.7.2 啟用與配置
在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})
})
}
}
2.7.3 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, // 檢查郵件格式 (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
}
2.7.4 migrations的使用
- 使用yarn migrate:new生成初始化文件。
- 將需要生成的表中的字段填入文件的up方法里,在down中填入刪除表的方法。
- 若需生成數據表,則使用yarn migrate:up。
- 若需要刪除數據表,則使用yarn migrate:down。
- migrations文件命名為'時間+表名.js'。
- 數據庫遷移中要在up方法中要添加id字段、時間字段createAt和updateAt。
2.7.5 操作數據庫
一般在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
})
2.8 Redis
2.8.1 安裝
$ yarn add egg-redis
2.8.2 啟用與配置
在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: '緩存數據庫名稱',
}
}
2.8.3 使用方法
具體方法和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(鍵名) 獲取單一鍵值
2.9 Socket.IO
2.9.1 安裝
$ yarn add egg-socket.io uws
2.9.2 啟用與配置
在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主機端口號'
}
}
2.9.3 文件格式
新建名為io的文件夾,并在其中分別建立兩個文件夾controller和middleware,控制器和中間件的文件命名格式以及編碼格式與eggjs的一樣目錄如下:
io
├── controller
└── middleware
2.9.4 Socket.IO路由配置
通過io.of設置命名空間,route()方法第一個參數是訂閱的話題,第二個是使用的控制器
app.io.of('/').route('new message', io.controller.chat.newMessage)
2.9.5 使用方法
- 獲取url參數
const {參數} = this.socket.handshake.query
- 加入房間
this.ctx.socket.join('房間') // 加入房間
- 獲取客戶端socketId
this.ctx.socket.id
- 獲取當前房間所有客戶端
this.app.io.of('/').adapter.clients([房間], (err, clients) => {
})
- 設置命名空間
const nsp = this.app.io.of('/')
- 推送數據
- this.ctx.socket.emit('主題', '信息') // 只有發送者能看到
- this.ctx.socket.broadcast('主題', '信息') // 只要發送者不能看到,其他人都能看到
- this.app.io.emit('主題', '信息') // 所有人都能看到
- this.app.io.of('命名空間').to('房間').emit('主題', '信息') // 向該命名空間下該房間內所有客戶端發送
2.10 參數校驗
2.10.1 安裝
$ yarn add egg-validate
2.10.2 啟用
在plugin.js中啟用validate
exports.validate = {
enable: true,
package: 'egg-validate'
}
2.10.3 使用方法
validate有兩個參數,第一個是需要校驗的參數,另一個是需要校驗的對象。
const { user } = this.ctx.request.body
this.ctx.validate({
username: { type: 'string', required: true },
password: { type: 'string', required: true }
}, user)
2.11 模板渲染(選用nunjucks)
- 模板文件默認目錄在app/view中
- EggJS結合模板文檔:https://eggjs.org/zh-cn/core/view.html
- 模板語法文檔:http://mozilla.github.io/nunjucks/templating.html
2.11.1 安裝
$ yarn add egg-view-nunjucks
2.11.2 啟用與配置
在plugin.js中啟用nunjucks
exports.nunjucks = {
enable: true,
package: 'egg-view-nunjucks'
}
在config.default.js中配置渲染引擎
- 對指定后綴文件使用模板引擎渲染
config.view = {
mapping: {
'.nj':'nunjucks'
}
}
- 渲染時無需寫文件后綴
config.view = {
defaultExtension: '.nj'
}
2.11.3 使用方法
Context對象存在三個接口使用模板引擎,使用renderString時需要指定模板引擎,如果定義了defaultViewEngine這里可以省略:
- render(name, locals) 渲染模板文件,并賦值給ctx.body
- renderView(name, locals) 渲染模板文件,僅返回不賦值
- renderString(tpl, locals) 渲染模板字符串,僅返回不賦值
例子:
- 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"}
]})
}
- router.js
router.get('/', controller.home.test);
- view/index.nj
<div class="wrap">
<button class="btn btn-warning">test</button>
{% for item in data %}
<div>{{item.name}}</div>
{% endfor%}
</div>
2.11.4 靜態文件
- 到config.js中開啟static,默認是注釋掉的,egg-static屬于內置插件
exports.static = true;
- 靜態文件存放路徑:app/public
- 引用方式
- 外部查看
http://localhost:7001/public/文件名.后綴 - 模板調用
public/+文件在public文件夾下的路徑
2.12 Git規范
2.12.1 分支類型
feature
- 功能開發分支
bugfix - 問題修復分支
develop - 在項目沒有經過測試并達到生產環境前,全部合并到dev分支,開發新功能也從dev分支遷出
master - 生產環境版本
2.12.2 分支命名
基本格式(全為英文)
- feature/功能名稱
- bugfix/bug名稱
例子:
- feature/user
- bugfix/login_error
2.12.3 開發流程
master 遷出develop分支 -> develop分支遷出feature功能分支 -> 提交pr審查代碼 -> 提交QA -> 合并到develop -> 合并到master
2.12.4 提交格式
使用git cz代替git commit
插件地址:https://github.com/commitizen/cz-cli
- 安裝
$ npm install -g commitizen
- 在項目中運行下面命令,使其支持 Angular 的 Commit message 格式
$ commitizen init cz-conventional-changelog --save --save-exact
- 使用
$ git add .
$ git cz
- 格式的選擇
- feat: 新功能提交
- fix: 修復一個bug
- docs: 只修改了文檔
- style: 修改一些不會影響代碼含義的東西(空格,格式化,分號等等)
- refactor: 代碼更改既不修復錯誤也不添加功能
- perf: 代碼更改提高了性能
- test: 添加缺少的測試或更正現有的測試
- build: 影響構建系統的更改或外部依賴關系的更改 (例如: gulp, broccoli, npm)
- ci: 改變測試配置文件和腳本(例如: T
- ravis, Circle, BrowserStack, SauceLabs)
- chore: 沒有更改
src
或者test
文件的更改 - revert: 恢復之前的提交
- 詳細問題
記錄修改的文件名?
- 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?
2.13 接口自測
2.13.1 軟件
- 名稱:postman
- 下載地址:https://www.getpostman.com/
2.13.2 操作規范
對負責的模塊單獨建立文件夾,將接口存放進去。將接口請求后的數據格式與約定返回的數據格式做對比。
3.RESTful API規范
3.1 請求協議
- http
- https
3.2 請求方法
請求方法 | 功能 |
---|---|
GET | 獲取資源 |
POST | 新增資源 |
PUT | 更新整個資源 |
PATCH | 更新個別資源 |
DELETE | 刪除資源 |
3.3 狀態碼
3.3.1 成功狀態碼
狀態碼 | 定義 |
---|---|
200 | 請求成功。客戶端向服務器請求數據,服務器返回相關數據 |
201 | 資源創建成功。客戶端向服務器提供數據,服務器創建資源 |
202 | 請求被接收。但處理尚未完成 |
204 | 客戶端告知服務器刪除一個資源,服務器移除它 |
3.3.2 錯誤狀態碼
狀態碼 | 錯誤描述 |
---|---|
400 | 請求無效。數據不正確,請重試 |
401 | 請求沒有權限。缺少API token,無效或者超時 |
403 | 請求未被授權。當前權限無法獲取指定的資源 |
404 | 請求失敗。請求資源不存在 |
406 | 請求失敗。請求頭部不一致,請重試 |
422 | 請求失敗。請驗證參數 |
3.3.3 服務器錯誤狀態碼
狀態碼 | 定義 |
---|---|
500 | 服務器發生錯誤,請檢查服務器 |
502 | 網關錯誤 |
503 | 服務不可用,服務器暫時過載或維護 |
504 | 網關超時 |
3.3.4 自定義狀態碼
具體而定
3.4 版本號
- 通過版本號可以區分api的版本
- 通過/api/v1/*代表v1版本
- 通過/api/v2/*代表v2版本
3.5 URL規范
RESTful API的所有操作都是針對特定資源進行的。資源就是URL所表示的,URL需要符合以下規范:
- 只能是名詞不能是動詞
- 小寫字符
- 不可使用下劃線'_',可以使用連字符'-'
- CRUD不可出現在URL中
- 參數列表要用encode
- 避免層級過深的URI,盡量使用查詢參數代替路徑中的實體導航,如GET /user?sex=female&age=30
具體形式如下:
- /api/{資源名}/{描述名}
- /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的權限信息
3.6 請求體格式
{
"data": {
"鍵":"值"
}
}
例子:
{
"data": {
"username":"klren",
"password":"123456"
}
}
3.7 返回體格式
正常返回
{
"code": "狀態碼",
"msg":"狀態描述",
"data":"具體內容"
}
例子:
{
"code": 200,
"msg": "success",
"data": {
"username": "klren",
"email":"klren@qq.com"
}
}
錯誤返回
{
"code": "錯誤狀態碼",
"msg": "錯誤信息",
"data": {
"error": "錯誤詳情",
}
}
例子:
{
"code": "400",
"msg": "數據格式錯誤",
"data": {
"error": "格式錯誤"
}
}
3.8 過濾、分頁與排序
采用'?'符號進行這些操作
3.8.1 過濾
使用唯一的查詢參數進行過濾
例子:
- GET /user?id=1 返回id等于1的用戶
3.8.2 排序
使用sort,后面跟著鍵名和排序方式
例子:
- GET /user?sort=age,desc 查詢用戶根據年齡倒序
3.8.4 分頁
使用limit和offset,后面跟具體數字。limit后面跟每頁最多數據量,offset后面跟數據起始位。
請求例子:
- GET /user?limit=10&offset=0 獲取從第0位開始的10個用戶數據
返回體例子:
{
"code":200,
"message":'success',
"data":{
"total": 0,
"pageIndex": 1,
"pageSize": 10,
"list":[{},{},{}...]
}
}
3.9 請求格式
- Content-Type:application/json 數據發送
- Content-Type:multipart/form-data 文件上傳