配置一个相对完整的Koa脚手架

一个相对完整的Koa项目结构通常包括以下文件夹和文件:

config:存放配置文件,如数据库连接信息、日志配置等。
controllers:存放控制器文件,处理业务逻辑。
middlewares:存放中间件文件,用于处理请求、错误处理、日志记录等。
models:存放数据模型文件,用于定义数据结构和数据库交互。
routes:存放路由文件,定义API端点和路由处理逻辑。
services:存放服务文件,封装业务逻辑和数据访问。
utils:存放工具函数和通用功能的文件。
logs:存放日志文件,用于记录应用程序的运行日志。
app.js:应用程序的入口文件,用于创建Koa实例、加载中间件和路由等。
package.json:管理项目的依赖和脚本。
.env:存放环境变量配置。

erlang 复制代码
project/
├── config/
│   ├── db.js
│   ├── logger.js
│   └── ...
├── controllers/
│   ├── authController.js
│   └── ...
├── middlewares/
│   ├── errorHandler.js
│   ├── logger.js
│   └── ...
├── models/
│   ├── user.js
│   └── ...
├── routes/
│   ├── authRoutes.js
│   └── ...
├── services/
│   ├── authService.js
│   └── ...
├── utils/
│   ├── validation.js
│   └── ...
├── logs/
│   ├── error.log
│   ├── info.log
│   └── ...
├── app.js
├── package.json
├── .env

跨域配置

javascript 复制代码
app.use(async (ctx, next) => {
  ctx.set('Access-Control-Allow-Credentials', 'true'); // 允许发送 Cookie
  ctx.set('Access-Control-Allow-Origin', ctx.request.header.origin); // 允许发送 Cookie
  ctx.set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); // 允许的请求方法
  ctx.set('Access-Control-Allow-Headers', 'Content-Type');
  ctx.set('Content-Type', 'application/json;charset=utf-8');
    // 对于预检请求(OPTIONS请求),直接返回200状态码
  if (ctx.method === 'OPTIONS') {
    ctx.status=204;
    ctx.body=""
  } else {
    await next();
  }
});

中间件

koa-bodyparser

解析请求体并将其存储在ctx.request.body属性中

javascript 复制代码
pnpm i koa-bodyparser
javascript 复制代码
const onerror = require('koa-bodyparser')
onerror(app)

koa-json-error

将响应体转换为JSON格式,并设置适当的Content-Type头

javascript 复制代码
pnpm i koa-json-error
javascript 复制代码
const onerror = require('koa-json-error')
app.use(onerror({
  format: (err) => { // 返回错误的格式
    return { code: err.status, message: err.message, result: err.stack }
  },
  postFormat: (e, { stack, ...rest }) => process.env.NODE_ENV === 'production' ? rest : { stack, ...rest },
}))

log4js

日志记录

javascript 复制代码
pnpm i log4js
javascript 复制代码
const log4js = require('log4js');
log4js.configure({
  // 定义日志的输出位置,可以包括控制台输出和文件输出。可以根据日志级别分别配置不同的文件输出
  appenders: {
    consoleOut: {
      type: 'console',
      layout: {
        type: 'pattern',
        pattern: '%d{yyyy-MM-dd hh:mm:ss} [%p] %m'
      },
    },
    fileOut: {
      type: 'file',
      filename: `./logs/logger`,
      pattern: 'yyyy-MM-dd.log',
      layout: {
        type: 'pattern',
        pattern: '%d{yyyy-MM-dd hh:mm:ss} [%p] %m'
      },
      alwaysIncludePattern: true,
    },
    errorOut: {
      type: 'file',
      filename: `./logs/error`,
      pattern: 'yyyy-MM-dd.log',
      layout: {
        type: 'pattern',
        pattern: '%d{yyyy-MM-dd hh:mm:ss} [%p] %m'
      },
      alwaysIncludePattern: true,
    },
  },
  // categories定义日志的策略,可以根据日志级别选择不同的输出方式。
  categories: {
    default: {
      appenders: ['consoleOut', 'fileOut'],
      level: 'info',
    },
    error: {
      appenders: ['consoleOut', 'errorOut'],
      level: 'error',
    },
  },
});

module.exports = {
  info: (content) => {
    let logger = log4js.getLogger('info')
    logger.info(content);
  },
  error: (content) => {
    let logger = log4js.getLogger('error')
    logger.error(content);
  }
};

better-sqlite3

一个轻量级数据库,无需安装额外服务 github.com/WiseLibs/be...

javascript 复制代码
pnpm i better-sqlite3
javascript 复制代码
const Database = require('better-sqlite3');
app.use(async (ctx, next) => {
  ctx.database = new Database('./database/data.db', { verbose: console.log }); // 将db挂在ctx上下文对象的database属性上
  await next();
});

// routes/user.js
router.get('/', jwtMiddleware, async (ctx) => {
  const { getUser } = require('../model/user')
  const result = await getUser(ctx.database)
  ctx.body = { data: result }
})

// model/user.js 得先创建用户表
async function addUser(db, data) {
  const stmt = db.prepare('INSERT INTO users (name, password, create_time) VALUES (?, ?, ?)')
  const info = stmt.run(data.name, data.password, new Date().toLocaleString())
  return info
}

async function getUser(db) {
  const stmt = db.prepare('select * from users')
  const data = stmt.all()
  return data
}

jsonwebtoken

鉴权 www.npmjs.com/package/jso...

我把用户名存入了token并放入cookie中,后续客户端请求设置携带cookie请求,既能鉴权也可直接获取用户信息

javascript 复制代码
pnpm i jsonwebtoken

封装jwt验证中间件

javascript 复制代码
const jwt = require('jsonwebtoken');
const jwtMiddleware = async (ctx, next) => {
  // 从 Cookie 中获取 JWT
  const token = ctx.cookies.get('token');
  
  if (token) {
    try {
      // 验证 JWT
      const decoded = jwt.verify(token, 'secritykey');

      // 将解码后的用户信息添加到请求上下文中
      ctx.state.user = decoded;
    } catch (error) {
      // JWT 验证失败
      ctx.status = 401;
      ctx.body = { code: '-1', message: '未登录' };
      return;
    }
  } else {
    // Cookie 中没有 JWT
    ctx.status = 401;
    ctx.body = { code: '-1', message: '未登录' };
    return;
  }

  await next();
};

module.exports = jwtMiddleware
javascript 复制代码
const jwt = require('jsonwebtoken')

router.get('/login', async (ctx) => {
  const { name } = ctx.request.query
  const token = jwt.sign({ name }, 'secritykey', { algorithm: 'RS256', expiresIn: '1h' }); // 生成JWT令牌,有效期为1小时
  // 将JWT令牌设置为Cookie
  ctx.cookies.set('token', token, { httpOnly: true });
  ctx.body = { data: '1' }
})

router.get('/', async (ctx) => {
  const user = ctx.state.user || {}
  console.log(user);
})
相关推荐
轻口味36 分钟前
命名空间与模块化概述
开发语言·前端·javascript
前端小小王1 小时前
React Hooks
前端·javascript·react.js
迷途小码农零零发1 小时前
react中使用ResizeObserver来观察元素的size变化
前端·javascript·react.js
娃哈哈哈哈呀2 小时前
vue中的css深度选择器v-deep 配合!important
前端·css·vue.js
旭东怪2 小时前
EasyPoi 使用$fe:模板语法生成Word动态行
java·前端·word
ekskef_sef4 小时前
32岁前端干了8年,是继续做前端开发,还是转其它工作
前端
sunshine6414 小时前
【CSS】实现tag选中对钩样式
前端·css·css3
真滴book理喻5 小时前
Vue(四)
前端·javascript·vue.js
蜜獾云5 小时前
npm淘宝镜像
前端·npm·node.js