npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

web-route

v1.0.2

Published

Use Typescript annotations to load API routes and easily configure APIs

Downloads

5

Readme

web-route

服务接口开发主要包含接口配置 + 业务逻辑。业务逻辑以外接口配置部分的过程和痛点总结如下:

  1. router 模块中定义接口方法、路径并定义处理逻辑的 controller 痛点:router 模块内代码简单,但接口多了后整个 router 模块就显得繁杂而冗长。
  2. controller 模块中进行业务逻辑以外的参数解析、参数校验等工作。 痛点1:参数校验逻辑并不难,但代码实现过程却繁杂而冗长。 痛点2:如果服务内需要单接口的跨域、鉴权等设置,则需要在跨域、鉴权的中间件中特殊处理,这样接口逻辑又被耦合进了其他地方。

本工具库用于帮助接口配置部分的快速开发,并为解决上述痛点,封装了

  • 路由自动注册
  • 单接口跨域配置
  • 请求源域名校验
  • contentType校验
  • 鉴权
  • 参数校验

等基本逻辑。旨在

  • 让开发者能够把精力主要放在业务逻辑上,技术上的接口配置 web-route 尽量帮你简单做。
  • 将单接口配置的代码高内聚在一处
  • 接口配置和业务逻辑低耦合
  • 可对单接口进行配置以增强单接口灵活性。

1. 安装

npm install web-route --save

2. 使用示例

2.1 服务启动

// index.ts
import Koa from 'koa';
import path from 'path';
import { register } from 'web-route';

const app = new Koa();

const router = register({
  // 设置 controller 文件 glob 路径(以 typescript 注解方式注册路由,详见2.2)
  annControllerPath: path.resolve(__dirname, './controllers/*.ts'),
  
  // 设置 controller 文件 glob 路径(commonjs 普通方式注册路由,详见2.3)
  controllerPath: path.resolve(__dirname, './controllers/*.js'),  

  // 全局设置接口默认配置
  defaultConfig: {
    // 全局接口默认为 get
    method: 'get',

    // 全局接口支持跨域
    cors: true,

    // 请求源域名白名单
    originWhiteList: ['http://127.0.0.1'],

    // 接口默认 content-type
    contentType: 'application/json;charset=utf-8',

    // 权限校验
    authValidate: (ctx, next) => {
      const token = ctx.get('token');
      if(token === 'something') {
        next();
      } else {
        ctx.status = 401;
      }
    },

    // 接口默认开启权限校验
    isAuthValidate: true,
  },
});

app.use(router.routes());
app.listen(8080, () => {
  console.log('启动。。。');
});

2.2 以 typescript 注解方式注册路由

// controller/hi.ts
import { RequestMapping, ParamValidRule } from "web-route";
import { Context } from "koa";

@RequestMapping('/hi')  // 定义模块内路由的路径前缀
class HiController {
  
  // 定义路径为 /hi/sayHiAgain、请求方式为 get、允许跨域、验证参数 a 为必填且为字符串 & 参数 b 为必填
  @RequestMapping({ path: '/sayHiAgain', method: 'get', cors: true, paramValidate: {
    a: [ParamValidRule.REUQIRED, ParamValidRule.STRING],
    b: ParamValidRule.REUQIRED
  } }) 
  sayHiAgain(ctx: Context) {
    console.log('hi world again...');
    ctx.body = {
      success: true,
    };
  }
}

export default HiController;

2.3 commonjs 普通方式注册路由

module.exports = [{
  path: '/js/hello',
  method: 'get',
  cors: true,
  handler(ctx, next) {
      ctx.body = {
        success: true,
        msg: 'hello'
      };
  }
}];

2.4 接口测试

# 发送测试请求
curl --location --request GET 'http://localhost:8080/hi/sayHiAgain?a=hello' \
--header 'Origin: localhost:9000' \
--header 'Host: localhost:9000'
# 返回结果
{success: false, code: 1001, info: '[a]值不能为空;[a]值应为字符串;[b]值不能为空'}

3. 开发指南及API

3.1 路由注册及全局配置

import { register } from 'web-route';
const router = register(options);     // options: RegisterOptions
app.use(router.routes());
app.listen(8080, () => {
  console.log('启动。。。');
});
// register 接收参数结构如下 typescript 定义
type RegisterOptions = { 
  annControllerPath?: string,                 // ts注解方式编译时加载路由的模块 glob 路径
  controllerPath?: string;                    // js commonjs 运行时方式加载路由的模块 glob 路径
  defaultConfig?: {                           // 指定接口的全局默认配置
    method?: string;                                                 // 接口方法
    cors?: CORS;                                                     // 跨域配置
    originWhiteList?: string[];                                      // 请求源域名白名单
    contentType?: string,                                            // content-type
    authValidate?: Middleware;                                       // 权限校验
    isAuthValidate?: boolean;                                        // 是否进行权限校验
  },          
  requestLogCallback?: RequestLogCallbackFn   // 每个请求完成后的回调函数
};

RegisterOptions

|属性|说明|类型|可选值|默认值| |---|---|---|---|---| |annControllerPath|ts 注解方式加载路由的模块 glob 路径|string|有效的glob路径|无| |controllerPath|js commonjs 方式加载路由的模块 glob 路径|string|有效的glob路径|无| |defaultConfig|指定接口的全局默认配置|GlobalRouteConfig|如下 GlobalRouteConfig 介绍|如下 GlobalRouteConfig 默认值| |requestLogCallback|每个请求完成后的回调函数,可用于获取请求信息、响应时间等信息|Function|有效的函数|无|

GlobalRouteConfig

|属性|说明|类型|可选值|默认值| |---|---|---|---|---| |method|接口全局请求方法|string|有效的 http 请求方法|get| |cors|跨域配置|boolean | CORS|true|false|跨域配置对象|false,不支持跨域| |originWhiteList|请求源域名白名单,支持正则匹配,不设置或为空数组则不做服务的访问源限制,否则只有白名单内的请求源域名才能访问|string[]|可访问本服务的请求源域名的集合|[]| |contentType|content-type|string|合法的content-type值|无| |authValidate|权限校验函数,支持异步函数|Koa 中间件函数|不设置或Koa中间件函数|无| |isAuthValidate|默认是否开启 authValidate 权限校验|boolean|true | false|false|

RequestLogCallbackFn

type RequestLogCallbackFn = (info: {
  path: string;             // 请求路径
  method: string;           // 请求方法
  startTime: number;        // 收到请求的时间戳
  endTime: number;          // 请求处理完毕的时间戳
  duration: number;         // 请求处理时间 ms
  ctx: Context;             // Koa ctx
}) => void;

3.2 接口定义

支持两种方式加载路由:

  • ts 注解方式:在类的方法上使用 @RequestMapping 注解,并向 @RequestMapping 传入配置对象。
  • js commonjs 方式:module.exports 导出接口配置的集合。

两种方式只是写法不同以及注解方式能够指定模块内接口统一的path前缀,除此以外接口的配置完全相同,配置如下。

3.2.1 接口配置对象

type RouteConfig = {
  path: string;                                                      // 接口路径
  method?: typeof HTTP_METHOD[keyof typeof HTTP_METHOD];             // 接口方法
  cors?: CORS;                                                       // 跨域配置
  originWhiteList?: string[];                                        // 请求源域名白名单
  contentType?: typeof CONTENT_TYPE[keyof typeof CONTENT_TYPE],      // content-type
  authValidate?: Middleware;                                         // 权限校验
  isAuthValidate?: boolean;                                          // 是否进行权限校验
  paramValidate?: ParamValidateFn;                                   // 参数校验
  handler: Handler | Handlers;                                       // 处理函数
}

RouteConfig

|属性|说明|类型|可选值|默认值| |---|---|---|---|---| |path|接口路径|string|任何路径|无| |method|接口全局请求方法|string|有效的 http 请求方法|get或全局默认配置| |cors|跨域配置|boolean | CORS|true|false|跨域配置对象|false或全局默认配置| |originWhiteList|如全局和接口均未设置白名单则接口无限制,否则接口可访问请求源域名为全局设置+接口设置|string[]|可访问本服务的请求源域名的集合|无或全局默认配置| |contentType|content-type|string|-|无或全局默认配置| |authValidate|权限校验函数,接口设置将覆盖全局设置|Koa 中间件函数|不设置或Koa中间件函数|无或全局设置| |isAuthValidate|是否开启 authValidate 权限校验|boolean|true | false|false或全局设置| |paramValidate|参数校验配置|boolean|true | false|无| |handler|接口处理逻辑函数或函数集合|function | function[]|有效的函数逻|无|

# cors 跨域配置

  1. 如接口被配置成可跨域访问,则会自动为跨域接口添加一个可访问的 options 预检请求接口,以保证非简单跨域请求的访问。
  2. 如果接口没做跨域配置,则为 originWhiteList 里面的域名自动添加允许跨域,如果做了跨域配置,则使用配置。
  3. 考虑到 JSONP 的方式受原理限制,只能 get、发送的非 XHR 请求、有可能受到 CSP 影响等缺陷,不支持 JSONP 跨域。
type CORS = boolean | CORS_CONFIG;
  • 当 cors 为 false 接口不支持跨域

  • 当 cors 为 true 接口支持跨域,且 cors 跨域响应头如下:

    Access-Control-Allow-Origin: '*'
    Access-Control-Allow-Headers: '*'
    Access-Control-Allow-Methods: '*'
    Access-Control-Allow-Credentials: true
  • 当 cors 为配置对象 CORS_CONFIG,详细配置响应头

    type CORS_CONFIG = {
      origin?: string,
      headers?: string,
      methods?: string,
      credentials?: string
    };
    CORS_CONFIG

    |属性|说明|类型|可选值|默认值| |---|---|---|---|---| |origin|指定 Access-Control-Allow-Origin|string|protocol://domain|| |headers|指定 Access-Control-Allow-Headers|string|有效的 http 请求方法|| |methods|指定 Access-Control-Allow-Methods|string|有效的 http 请求方法|*| |credentials|指定 Access-Control-Allow-Credentials|boolean|true|true|

# paramValidate 参数校验配置

type validFn = (paramName: string, paramValue: any, requestBody?: Record<string, any>, ctx?: Context) => string | boolean | Promise<string> | Promise<boolean>;

type ParamValidateFn = { [name: string]: validFn | validFn[] };

先举个栗子:

{
  // ...其他配置...
  paramValidate: {
    param1: async (name, value) => {
      // 验证逻辑
      if ('异步校验成功') {
        return true;
      } else {
        return '失败提示';
      }
    },
    param2: [
      (name, value, requestBody, ctx) => {
        // 验证逻辑1
        if ('校验成功') {
          return true;
        } else {
          return '失败提示';
        }
      },
      (name, value, requestBody, ctx) => {
        // 验证逻辑2
        if ('校验成功') {
          return true;
        } else {
          return '失败提示';
        }
      }
    ]
  }
  // ...其他配置...
}

说明:

  • paramValidate 是一个对象,对象属性为参数名,对象值为校验函数或校验函数的集合(多个校验条件)。
  • 校验函数支持异步校验。
  • 校验函数可接收四个参数,分别是 参数名、参数值、所有请求参数、Koa 的 ctx。
  • 检验函数返回字符串将被认作校验不通过,且字符串将被作为校验不成功的提示,无返回值或返回值非字符串,则认为校验通过。 web-route 内置了常用的参数校验函数,通过 ParamValidRule 对外暴露
    import { ParamValidRule } from "web-route";
    // 或
    const { ParamValidRule } = require("web-route");
    |函数名|校验逻辑| |---|---| |REUQIRED|必填| |STRING|字符串| |NUMBER|数字| |EMAIL|邮箱地址| |URL|url| |MOBILE_NUMBER|11位手机号| |TELEPHONE_NUMBER|座机号,是否包含区号均可| |PHONE_NUMBER|11位手机号或座机号| |ID_CARD_NUMBER|身份证号| |CN|全中文| |NO_SPACE|不包含空格| |NO_SPECIAL_CHAR|中文、英文、数字、下划线组成|

3.2.2 ts 注解配置接口

import { RequestMapping, ParamValidRule } from "web-route";
import { Context, Next } from "koa";

@RequestMapping('/hi')  // 定义模块内路由的路径前缀
class HiController {
  
  // 定义路径为 /hi/sayHiAgain、请求方式为 get、允许跨域、验证参数 a 为必填且为字符串 & 参数 b 为必填
  @RequestMapping({ 
    // ...这里写接口配置...
  }) 
  sayHi(ctx: Context, next: Next) {
    console.log('hi world again...');
    ctx.body = {
      success: true,
    };
  }
}

3.2.3 js commonjs 模块配置接口

module.exports = [{
  path: '/js/hello',
  method: 'get',
  // ...这里写接口配置...
  handler(ctx, next) {
      ctx.body = {
        success: true,
        msg: 'hello'
      };
  }
}];