【KOA框架】koa框架基础及swagger接口文档搭建

koa是express的一层封装,语法比express更加简洁。所以有必要了解下koa的相关开发方法。
在这里插入图片描述

代码实现

  • package.json
{
  "name": "koapp",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "dev": "npx nodemon src/app.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": "",
  "dependencies": {
    "jsonwebtoken": "^9.0.2",
    "koa": "^2.15.3",
    "koa-bodyparser": "^4.4.1",
    "koa-jwt": "^4.0.4",
    "koa-router": "^13.0.1",
    "koa2-swagger-ui": "^5.11.0",
    "mysql": "^2.18.1",
    "swagger-jsdoc": "^6.2.8"
  }
}
  • 入口文件app.js
const Koa = require("koa");
const logger = require("./mid/logger");
const hello = require("./mid/hello");
const userRouter = require("./router/user");
const publicRouter = require("./router/public");
const sysRouter = require("./router/sys");
const bodyParser = require("koa-bodyparser");
const handlerError = require("./mid/errorHandle");
const Result = require("./utils/result");

const jwt = require("koa-jwt");
const secret = "zhangsanfeng";

const app = new Koa();
app.use(handlerError);

// 中间件 自定义了 401 响应,将用户验证失败的相关信息返回给浏览器
app.use(function (ctx, next) {
  return next().catch((err) => {
    if (401 == err.status) {
      ctx.status = 401;
      ctx.body = Result.error(401, "无效的token");
    } else {
      throw err;
    }
  });
});

app.use(
  jwt({
    secret,
    // passthrough: true,
    // cookie: "token", // 从 cookie 中获取token
    debugger: true,
  }).unless({ path: [/^\/public/, /^\/sys\/login/] })
);

// 注册路由中间件
app.use(bodyParser());

app.use(userRouter.routes()).use(userRouter.allowedMethods());
app.use(publicRouter.routes()).use(publicRouter.allowedMethods());
app.use(sysRouter.routes()).use(sysRouter.allowedMethods());

// 全局异常处理;
app.on("error", (error, ctx) => {
  console.log("ssss", error.status);
});

app.listen(3000);
  • 错误中间件
const Result = require("../utils/result");

async function handlerError(ctx, next) {
  try {
    await next(); // 执行后代的代码
    if (!ctx.body) {
      // 没有资源
      ctx.status = 404;
      // ctx.body = "404";
    }
  } catch (error) {
    // 如果后面的代码报错 返回500
    ctx.status = error.status;
    if (error.status == 404) {
      ctx.body = Result.error("找不到资源");
    } else if (error.status == 500) {
      console.log("🚀 ~ app.on ~ error.status:", error.status);
      ctx.body = Result.error("服务器异常,请稍后再试");
    } else if (error.status === 401) {
      ctx.body = Result.error("未授权 " + error.message);
    } else {
      ctx.body = Result.error("未知错误");
    }
    // ctx.body = "500";
  }
}

module.exports = handlerError;
  • 日志中间件
async function logger(ctx, next) {
  console.log("logger before...");
  await next();
  const rt = await ctx.response.get("X-Response-Time");
  console.log(`${ctx.method} ${ctx.url} - ${rt}`);
  console.log(ctx.username, "from mid");
  console.log("logger after...");
}
module.exports = logger;
  • 路由

不需要拦截的路由

const Router = require("koa-router");
const router = new Router();

router.prefix("/public");

router.get("/", async (ctx) => {
  console.log("🚀 ~ router.get ~ ctx:", ctx.params);
  ctx.body = {
    id: ctx.params.id,
    name: "小明1111",
    age: 18,
    sex: "男",
  };
  // ctx.throw(401);
});

router.get("/:id", async (ctx) => {
  console.log("🚀 ~ router.get ~ ctx:", ctx.params);
  ctx.body = {
    id: ctx.params.id,
    name: "小明",
    age: 18,
    sex: "男",
  };
  // ctx.throw(401);
});

module.exports = router;
  • 登录获取token的路由
const Router = require("koa-router");
const router = new Router();
const jwt = require("koa-jwt");
const { sign } = require("jsonwebtoken");
const Result = require("../utils/result");
const secret = "zhangsanfeng";

router.prefix("/sys");

router.post("/login", async (ctx, next) => {
  const body = ctx.request.body;
  if (body.username !== "zhangsan" || body.pwd !== "123456") {
    ctx.body = Result.error("账号或密码错误");
    // ctx.throw(401, "账号或密码错误");
  } else {
    const token = sign({ abcsd: "aaaaaa" }, secret, { expiresIn: "1h" });
    ctx.body = Result.success({ token });
  }
});

router.get("/userInfo", async (ctx, next) => {
  ctx.body = {
    name: "zhangsanfeng",
    age: 20,
    sex: "男",
  };
});

router.get("/logout", async (ctx, next) => {
  ctx.body = Result.success("退出成功");
  ctx.cookies.set("token", "", { signed: false, maxAge: 0 });
});

module.exports = router;
  • 用户路由
const Router = require("koa-router");
const router = new Router();

router.prefix("/user/s");

router.get("/:id", async (ctx) => {
  console.log("🚀 ~ router.get ~ ctx:", ctx.params);
  ctx.body = {
    id: ctx.params.id,
    name: "小明2222",
    age: 18,
    sex: "男",
  };
  // ctx.throw(401);
});

router.get("/", async (ctx) => {
  console.log("🚀 ~ router.get ~ ctx:", ctx.query);
  ctx.body = {
    id: ctx.params.id,
    name: "小明3333",
    age: 18,
    sex: "男",
  };
});

router.post("/", async (ctx) => {
  console.log("🚀 ~ router.get ~ ctx:", ctx.request.body);
  ctx.body = {
    id: ctx.params.id,
    name: "小明4444",
    age: 18,
    sex: "男",
  };
});

module.exports = router;
  • 工具方法
class Result {
  constructor(message, code, data) {
    this.message = message;
    this.code = code;
    this.data = data;
  }

  static success() {
    return new Result("success", 0, null);
  }
  static success(data) {
    return new Result("success", 0, data);
  }
  static error(message) {
    return new Result(message, null, 1);
  }
}

module.exports = Result;

实现效果

  • 未登录获取列表信息
    在这里插入图片描述

  • 登录账号或密码错误

在这里插入图片描述

  • 登录成功
    在这里插入图片描述
  • 获取用户信息成功
    在这里插入图片描述

swagger文档配置

  • 配置参考文档:https://swagger.io/docs/specification/v3_0/paths-and-operations/
  • post接口文档的书写
const Router = require("koa-router");
const router = new Router();
const jwt = require("koa-jwt");
const { sign } = require("jsonwebtoken");
const Result = require("../utils/result");
const secret = "zhangsanfeng";
const mysql = require("../mysql");

router.prefix("/sys");

/**
 * @swagger
 * tags:
 *   name: Users
 *   description: User management
 */

/**
 * @swagger
 * components:
 *   schemas:
 *     User:
 *       type: object
 *       properties:
 *         username:
 *           type: string
 *           description: username.
 *         password:
 *           type: string
 *           description: password.
 *       required:
 *         - username
 *         - password
 */

/**
 * @swagger
 * /sys/login:
 *   post:
 *     summary: login system.
 *     tags: [Users]
 *     requestBody:
 *       description: Optional description in *Markdown*
 *       required: true
 *       content:
 *         application/json:
 *           schema:
 *             type: object
 *             properties:
 *               username:
 *                 type: string
 *                 example: zhangsan
 *               pwd:
 *                 type: string
 *                 example: 123456
 *     description: User login system.
 *     responses:
 *       200:
 *         description: user login system.
 *         content:
 *           application/json:
 *             schema:
 *               type: array
 *               items:
 *                 $ref: '#/components/schemas/User'
 */
router.post("/login", async (ctx, next) => {
  const body = ctx.request.body;
  if (body.username !== "zhangsan" || body.pwd !== "123456") {
    ctx.body = Result.error("账号或密码错误");
    // ctx.throw(401, "账号或密码错误");
  } else {
    const token = sign({ abcsd: "aaaaaa" }, secret, { expiresIn: "1h" });
    ctx.body = Result.success({ token });
  }
});

router.get("/userInfo", async (ctx, next) => {
  const id = 2;
  // 查询数据库
  mysql.query("select * from tb_user where id = ?", [id], (err, result) => {
    console.log("🚀 ~ mysql.query ~ result:", result);
    if (err) {
      ctx.body = Result.error("查询失败");
    } else {
      ctx.body = Result.success(result[0]);
    }
  });

  ctx.body = {
    name: "zhangsanfeng",
    age: 20,
    sex: "男",
  };
});

router.get("/logout", async (ctx, next) => {
  ctx.body = Result.success("退出成功");
  ctx.cookies.set("token", "", { signed: false, maxAge: 0 });
});

module.exports = router;
  • 全局配置swaager
const Router = require("koa-router");
const router = new Router();
const swaggerJSdoc = require("swagger-jsdoc");
const path = require("path");

const swaggerDefinition = {
  openapi: "3.0.0",
  info: {
    title: "koa2搭建的app项目接口API",
    version: "1.0.0",
    description: "测试项目的接口文档",
  },
  host: "localhost:8080",
  basePath: "/",
};

const options = {
  swaggerDefinition,
  apis: [path.join(__dirname, "../router/*.js")],
};

const swaggerSpec = swaggerJSdoc(options);

router.get("/swagger.json", (ctx) => {
  ctx.set("Content-Type", "application/json");
  ctx.body = swaggerSpec;
});

module.exports = router;
  • 入口文件配置swagger
const Koa = require("koa");
const { koaSwagger } = require("koa2-swagger-ui");
console.log("🚀 ~ koaSwagger:", koaSwagger);

const logger = require("./mid/logger");
const hello = require("./mid/hello");
const userRouter = require("./router/user");
const publicRouter = require("./router/public");
const sysRouter = require("./router/sys");
const bodyParser = require("koa-bodyparser");
const handlerError = require("./mid/errorHandle");
const Result = require("./utils/result");
const swagger = require("./utils/swagger");

const jwt = require("koa-jwt");
const secret = "zhangsanfeng";

// 数据库连接
const mysql = require("./mysql/index");
mysql.connect();

const app = new Koa();
app.use(handlerError);

// 中间件 自定义了 401 响应,将用户验证失败的相关信息返回给浏览器
app.use(function (ctx, next) {
  return next().catch((err) => {
    if (401 == err.status) {
      ctx.status = 401;
      ctx.body = Result.error(401, "无效的token");
    } else {
      throw err;
    }
  });
});

app.use(
  jwt({
    secret,
    // passthrough: true,
    // cookie: "token", // 从 cookie 中获取token
    debugger: true,
  }).unless({
    path: [/^\/public/, /^\/sys\/login/, /^\/swagger/, /^\/apidocs/],
  })
);

// 注册路由中间件
app.use(bodyParser());

app.use(userRouter.routes()).use(userRouter.allowedMethods());
app.use(publicRouter.routes()).use(publicRouter.allowedMethods());
app.use(sysRouter.routes()).use(sysRouter.allowedMethods());
app.use(swagger.routes()).use(swagger.allowedMethods());

app.use(
  koaSwagger({
    routePrefix: "/swagger",
    swaggerOptions: {
      url: "/swagger.json",
    },
  })
);

// 全局异常处理;
app.on("error", (error, ctx) => {
  console.log("ssss", error.status);
});

app.listen(3000);

效果测试

测试登录成功

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风清云淡_A

觉得有帮助的话可以给点鼓励

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值