编写一个koa2中间件-接口必填参数的校验

写在开头

koa2 一个由 Express 原班人马打造的,基于 Node 平台的下一代 Web 开发框架。

它属于前端老熟客了,这里就不过多介绍。

本章主要讨论一下如何编写其中间件(middleware)并编写一个"接口必填参数校验"的中间件。

初始化koa2项目🏗️

为了方便快速创建 koa2 项目,我们可以全局安装一下 koa2 的脚手架 koa-generator

javascript 复制代码
npm install -g koa-generator

它等同于 vueclireactreact-create-app 这些脚手架。

测试安装是否成功:

javascript 复制代码
koa2 -V

注意命令是 koa2 哦,能正常看到版本号就说明安装成功了。

初始化项目:

javascript 复制代码
koa2 your-project-name

能得到这么一个项目目录:

安装项目依赖:

javascript 复制代码
npm install

启动项目:

javascript 复制代码
npm run start

// or

npm run dev

dev 命令具有热更新的效果,修改项目代码会及时更新,而热更新是通过 nodemon 包来实现的。

项目启动后,默认端口是 3000

基于脚手架创建的项目,默认在 routes 文件夹中帮我们生成了一些接口,你可以访问以下这些路径测试看看:

javascript 复制代码
http://localhost:3000/
http://localhost:3000/string
http://localhost:3000/json
http://localhost:3000/users
http://localhost:3000/users/bar

中间件语法🚀

准备完项目后,就可以来尝试编写 koa2 的中间件了。

那么,什么是中间件呢?

koa2 框架中,你可以将HTTP请求想象成一条水流,而中间件则像是连接在这条水流上的各种管道。每个中间件都会对这条水流(即HTTP请求和响应)进行处理和改写。

中间件本质是一个异步函数,它可以访问请求对象、响应对象和下一个中间件函数。

语法也挺简单的,写完异步函数往 app.use 注册一下就行了。

javascript 复制代码
const middleware = async (ctx, next) => {
  // TODO: 你需要处理逻辑,从ctx能获取到很多信息,如参数列表
  
  // 抛给下一个中间件
  await next();
};

app.use(middleware);

注意,中间件的注册有顺序先后的❗

必填参数校验的中间件🎯

了解中间件基本语法后,我们就可以来编写自己的中间件了,这次要写的中间件主要是针对参数校验的。

很多时候,前端拿到后端开发的接口后,经常能看到一些接口参数被标记成必填,本次要写的中间件就是为了完成这个功能。

在项目根目录下创建 middleware 文件夹,用于存放各种中间件。

创建 middleware/requiredParams.js 文件,用于编写本次的中间件,功能不复杂,咱们贴上来直接瞧瞧。

javascript 复制代码
module.exports = (options = {}) => {
  return async (ctx, next) => {
    /**
     * @name 用于校验接口必填参数的中间件
     * @param { string | Array<string> | Object } requiredParams
     * @return { boolean }
     */
    ctx.requiredParams = (requiredParams) => {
      // 获取接口的请求方式
      let method = ctx.method;
      // 获取接口的参数列表
      let params =  method.toLocaleUpperCase() === "GET" ? ctx.query : ctx.request.body;
      // 接口需要检验必填参数
      if (!isEmpty(requiredParams)) {
        // 只有一个必填参数需要验证
        if (typeof requiredParams === "string") {
          if (!(requiredParams in params) || (!params[requiredParams] && params[requiredParams] !== 0)) {
            ctx.body = "字段" + requiredParams + "为必填";
            return true;
          }
        }
        // 有多个参数需要验证必填
        if (Array.isArray(requiredParams)) {
          let paramsKeys = Object.keys(params);
          const message = [];
          for (let i = 0; i < requiredParams.length; i++) {
            if (!paramsKeys.includes(requiredParams[i]) || (!params[requiredParams[i]] && params[requiredParams[i]] !== 0)) {
              message.push("字段" + requiredParams[i] + "为必填");
            }
          }
          if (message.length > 0) {
            ctx.body = message.join("\n");
            return true;
          }
        }
        // 支持自定义提示语,key为参数名,value为自定义的提示语
        if (Object.prototype.toString.call(requiredParams) === "[object Object]") {
          const message = [];
          for (let key in requiredParams) {
            if (!(key in params) || (!params[key] && params[key] !== 0)) {
              if (requiredParams[key]) {
                message.push(requiredParams[key]);
              } else {
                message.push("字段" + key + "为必填");
              }
            }
          }
          if (message.length > 0) {
            ctx.body = message.join("\n");
            return true;
          }
        }
      }
    };
    await next();
  };
};

/**
 * @name 检验非空的工具函数
 * @param { * } val 检验目标
 * @return { boolean }
 */
function isEmpty(val) {
  if (val === 0) return false;
  // null or undefined
  if (val == null) return true;
  if (typeof val === "boolean") return false;
  if (typeof val === "number") return !val;
  if (val instanceof Error) return val.message === "";
  switch (Object.prototype.toString.call(val)) {
    // String or Array
    case "[object String]":
    case "[object Array]":
      return !val.length;
    // Map or Set or File
    case "[object File]":
    case "[object Map]":
    case "[object Set]": {
      return !val.size;
    }
    // Plain Object
    case "[object Object]": {
      return !Object.keys(val).length;
    }
  }
  return false;
}

app.js 文件中注册:

js 复制代码
...
const requiredParams = require("./middleware/requiredParams.js");


...
app.use(json());
app.use(logger());
app.use(require("koa-static")(__dirname + "/public"));
// 注册
app.use(requiredParams());

中间件代码都写有注释,可以仔细看看,其主要写了三种验证形式,字符串、数组、对象,我们可以分别来看看应用的情况。

http://localhost:3000/users/bar 接口为例:

javascript 复制代码
// routes/users.js
router.get("/bar", function (ctx, next) {
  // 要求 name 参数必传
  if (ctx.requiredParams("name")) return;
  ctx.body = "我是bar接口";
});

效果如下:

测试多个必填参数情况:

javascript 复制代码
// routes/users.js
router.get("/bar", function (ctx, next) {
  // 要求 name、age、sex 参数必传
  if (ctx.requiredParams(["name", "age", "sex"])) return;
  ctx.body = "我是bar接口";
});

测试自定义提示语情况:

javascript 复制代码
// routes/users.js
router.get("/bar", function (ctx, next) {
  if (ctx.requiredParams({
    name: "这个name参数一定一定要填",
    age: "年龄也要填哦",
  })) return;
  ctx.body = "我是bar接口";
});

至此,本篇文章就写完啦,撒花撒花。

希望本文对你有所帮助,如有任何疑问,期待你的留言哦。

老样子,点赞+评论=你会了,收藏=你精通了。

相关推荐
xw59 分钟前
移动端调试上篇
前端
伍哥的传说12 分钟前
Lodash-es 完整开发指南:ES模块化JavaScript工具库实战教程
大数据·javascript·elasticsearch·lodash-es·javascript工具库·es模块·按需导入
@菜菜_达13 分钟前
Lodash方法总结
开发语言·前端·javascript
GISer_Jing18 分钟前
低代码拖拽实现与bpmn-js详解
开发语言·javascript·低代码
YAY_tyy23 分钟前
基于 Vue3 + VueOffice 的多格式文档预览组件实现(支持 PDF/Word/Excel/PPT)
前端·javascript·vue.js·pdf·word·excel
Yvonne爱编码43 分钟前
AJAX入门-AJAX 概念和 axios 使用
前端·javascript·ajax·html·js
在路上`1 小时前
前端学习之后端java小白(三)-sql外键约束一对多
java·前端·学习
Pu_Nine_92 小时前
10 分钟上手 ECharts:从“能跑”到“生产级”的完整踩坑之旅
前端·javascript·echarts·css3·html5
東雪蓮☆3 小时前
从零开始掌握 Web 与 Nginx:入门详解
运维·服务器·前端·nginx
脑子慢且灵3 小时前
【JavaWeb】一个简单的Web浏览服务程序
java·前端·后端·servlet·tomcat·web·javaee