我的第一个Node项目(学习记录)

以人为镜可以明得失 以代码为镜可以通逻辑

1、创建文件夹,初始化项目

mkdir koa-app # 创建目录
cd koa-app    # 进入项目
npm init -y # 生成package.json
npm install koa koa-router koa-bodyparser  mysql2 sequelize require-directory nodemon # 下载所需依赖包
touch app.js # 生成入口文件

2、定义项目启动命令

{
  "name": "koa-app",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "scripts": {
    "start:dev": "nodemon app.js",
    "start:prod": "node app.js"
  },
  "author": "Joker",
  "license": "ISC",
  "dependencies": {
    "koa": "^2.13.1",
    "koa-router": "^10.0.0",
    "mysql2": "^2.2.5",
    "require-directory": "^2.1.1",
    "sequelize": "^6.5.1"
  },
  "devDependencies": {
    "koa-bodyparser": "^4.3.0",
    "nodemon": "^2.0.7"
  }
}

3、定义入口文件(app.js)

const Koa = require('koa')
const bodyParser = require('koa-bodyparser')

const InitManger = require('./core/init')
const catchErros = require('./middlewares/exception')

const app = new Koa()

// 全局错误处理
app.use(catchErros)
app.use(bodyParser())

// 初始化导入路由
InitManger.initCore(app)


const PORT = process.env.PORT || 3000
app.listen(PORT, () => {
  console.log(`Server is runing on ${PORT}!`)
})

4、sequelize 连接 mysql

安装相关依赖

npm install mysql2 sequelize -S

创建数据库配置文件

mkdir config
touch config/db.js

定义数据库连接配置

// config/db.js

 module.exports = {
  database: {
    host: 'localhost',
    database: 'dbName',
    user: 'root',
    password: 'root-password'
  }
}

定义 sequelize 文件

// config/index.js

const Sequelize = require('sequelize')
const { host, database, user, password } = require('../config/config').database


const sequelize = new Sequelize(database, user, password, {
  host,
  dialect: 'mysql',
  logging: true,
  timezone: '+08:00',
})

sequelize.sync({
  force: true,
})

module.exports = sequelize

定义 model

mkdir models
touch models/User.js
const { Sequelize, Model } = require('sequelize')
const { sequelize } = require('../config/sequelize')

class User extends Model { }

User.init({
  nickname: Sequelize.STRING(32),
  age: Sequelize.INTEGER(3),
  sex: Sequelize.STRING(1)
}, {
  sequelize,
  tableName: 'user'
})

module.exports = User

5、定义路由

mkdir app/api/v1/
touch app/api/v1/user.js
const Router = require('koa-router')
const router = new Router({
  prefix: '/v1/user'
})

router.get('/post', async (ctx) => {
  const body = await ctx.request.body

  await User.create(body)
})

module.exports = router

6、定义全局异常处理中间件

mkdir middlewares
touch middlewares/exception.js
touch utils/http-exception.js # 定义已知异常类
const catchError = async (ctx, next) => {
  try {
    await next()
  } catch (error) {
    console.log(error, 'this is error...')
  }
}

module.exports = catchError

6.1、明确已知错误还是未知错误

// utils/http-exception.js
/**
 * 默认的异常
 */
class HttpException extends Error {
  constructor(msg = '错误请求', errorCode = 10000, code = 400) {
    super()
    this.errorCode = errorCode
    this.code = code
    this.msg = msg
  }
}

class ParameterException extends HttpException {
  constructor(msg, errorCode) {
    super()
    this.code = 400
    this.msg = msg || '参数错误'
    this.errorCode = errorCode || 10000
  }
}

class AuthFailed extends HttpException {
  constructor(msg, errorCode) {
    super()
    this.code = 401
    this.mag = msg || '授权失败'
    this.errorCode = errorCode || 10004
  }
}

class NotFound extends HttpException {
  constructor(msg, errorCode) {
    super()
    this.code = 404
    this.msg = msg || '未找到该资源'
    this.errorCode = errorCode || 10005
  }
}

class Forbidden extends HttpException {
  constructor(msg, errorCode) {
    super()
    this.code = 403
    this.msg = msg || '禁止访问'
    this.errorCode = errorCode || 10006
  }
}

class Oversize extends HttpException {
  constructor(msg, errorCode) {
    super()
    this.code = 413
    this.msg = msg || '上传文件过大'
    this.errorCode = errorCode || 10007
  }
}

class InternalServerError extends HttpException {
  constructor(msg, errorCode) {
    super()
    this.code = 500
    this.msg = msg || '服务器出错'
    this.errorCode = errorCode || 10008
  }
}

module.exports = {
  HttpException,
  ParameterException,
  AuthFailed,
  NotFound,
  Forbidden,
  Oversize,
  InternalServerError
}

6.2、 定义异常处理中间件

// middlewares/exception.js
const { HttpException } = require('../utils/http-exception')

// 全局异常监听
const catchError = async(ctx, next) => {
  try {
    await next()
  } catch(error) {
    // 已知异常
    const isHttpException = error instanceof HttpException
    // 开发环境
    const isDev = global.config.service.enviroment === 'dev'

    // 在控制台显示未知异常信息:开发环境下,不是HttpException 抛出异常
    if (isDev && !isHttpException) {
      throw error
    }

    /**
     * 是已知错误,还是未知错误
     * 返回:
     *      msg 错误信息
     *      error_code 错误码
     */
    if (isHttpException) {
      ctx.body = {
        msg: error.msg,
        error_code: error.errorCode
      }
      ctx.response.status = error.code
    } else {
      ctx.body = {
        msg: '未知错误',
        error_code: 9999
      }
      ctx.response.status = 500
    }
  }
}

module.exports = catchError

7、全局自动注册路由

touch utils/initRouter.js
const requireDirectory = require('require-directory')
const Router = require('koa-router')

class InitManger {
  static initCore(app) {

    InitManger.app = app
    InitManger.initLoadRouters()
  }

  static initLoadRouters() {
    const apiDirectory = `${process.cwd()}/app/api`
    requireDirectory(module, apiDirectory, {
      visit: whenLoadModule
    })

    function whenLoadModule(obj) {
      if (obj instanceof Router) {
        InitManger.app.use(obj.routes())
      }
    }
  }
}


module.exports = InitManger

8、访问接口

访问接口地址 localhost:3000/api/v1/user/add
参数: {“nickname”: “JOKER”,”age”: “18”,”sex”: “男”}

{
    "message": "用户添加成功",
    "code": 10000
}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!