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 中获取tokendebugger: 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) {// 如果后面的代码报错 返回500ctx.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 中获取tokendebugger: 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);
效果测试
测试登录成功