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"
}
}
- 入口文件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;
实现效果
-
未登录获取列表信息
-
登录账号或密码错误
- 登录成功
- 获取用户信息成功