🚀 Big News: Socket Acquires Coana to Bring Reachability Analysis to Every Appsec Team.Learn more
Socket
Book a DemoInstallSign in
Socket

caibird-mjs

Package Overview
Dependencies
Maintainers
1
Versions
2
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

caibird-mjs

## 序

4.0.3
latest
Source
npm
Version published
Weekly downloads
1
-50%
Maintainers
1
Weekly downloads
 
Created
Source

caibird

  我一直认为typescript对前端的代码质量和开发效率的提升意义非常重大。

  所谓“善战者无赫赫之功,善医者无煌煌之名”,如果能把typescript用好,它就是前端代码质量和开发效率的“善战者和善医者”。

版本

v4.0-RC

  3.0之前也实现了类型同构,但整体功能非常重,接入成本也比较高。

  4.0后将专注于做一个MVC+DI+类型同构的通用型框架,接入成本低且将拥抱开源。

安装

npm i caibird@rc

仓库

源码:https://github.com/xiaocaibird/caibird-new

demo: https://github.com/xiaocaibird/caibird-demo

简介

  首先说明,这里的后端指的nodejs服务端。现在大部分前端项目,都有自己的nodejs服务,可能仅做为接入层和简单的BFF层,也可能有更多的后端职责。

  一般情况下,nodejs端(以下统称为:server端)的代码也会和前端(以下统称为:client端)的代码放在一个工程里管理。可即使在同一个工程里,当我们在server端开发完业务api,并在client端调用时,想要获取api的字段类型,通常要做不少额外工作。

  另外当我们需要快速从调用方(client端)的逻辑,跳转到被调用方(server端)的逻辑时,也不能像一个函数调用一样快速跳转。

  所以这里抛出一个合理的猜想:

既然在一个工程里了,是否有办法能让业务功能在开发过程中,
零成本的接入client端到server端的类型检查以及快速调试?

  于是我就想到了要做“前后端类型同构”,关键能力如下:

  • 提供client+server的跨端类型同构能力
  • 提供server端api(nodejs api)的方法级调用能力
  • 完全基于ts和koa,无需任何中间层和额外的协议文件,在开发时能随时响应api的字段变化
  • 在开发时能直接从client端跳转到server端的api代码或参数字段

server端示例

// src/server/index.ts
// 服务端入口
import Koa from 'koa';
import { controllersRouter } from 'caibird';

import * as Controllers from './controllers';

const app = new Koa();

app.use(controllersRouter({
    Controllers
}, app));

app.listen(8080, '127.0.0.1');

// src/server/controllers/test.ts
import { Controller, httpMethodFilter, View, BaseController } from 'caibird';

@httpMethodFilter('POST')
@Controller()
export class TestController extends BaseController {
    @httpMethodFilter(['POST', 'GET'])
    public async getConfig(reqData: { obj: { a: string, b: number } }) {
        return { prop12: true };
    }

    public async getMsg() {
        return '你好';
    }

    @httpMethodFilter('GET')
    public async toUrl(reqData: { url: string }) {
        return View.Redirect(reqData.url);
    }
}

client端示例


import { ApiService } from 'caibird/client';

// CaibirdGlobal 是一个跨端的全局类型命名空间,CaibirdGlobal.Controllers是所有server端的Controller类型
const apiService = new ApiService<CaibirdGlobal.Controllers>({
    transformGetMethodJsonData: {
        enabled: true,
        queryKey: 'caibirdurljsonparamskey',
    },
});

const fn = async () => {
    // 仅获取response data
    const res1 = await apiService.call.Test.getConfig({ obj: { a: '', b: 0 } }, { httpMethod: 'GET' });
    // 获取完整的response body
    const res2 = await apiService.call.Test.getConfig({ obj: { a: '', b: 0 } }, { isRaw: true });

    console.log(res1);
    console.log(res2);
}

fn();

核心模块

Controller装饰器

  • 用于标记需要controllersRouter中间件(以下简称中间件)处理的controller

  • options:

    type Options = {
        // 命名后缀,一般不用传。 默认为'Controller'。
        // 需要与中间件的controllerSuffix配合使用
        suffix?: string
    };
    
  • 核心概念:

    • action:controller中的一个方法,对应一个接口响应
    • filter:可灵活组合,拦截进入controller或action的请求,实现AOP

koa中间件:controllersRouter

  • options字段说明:

    type Options = {
        // 传入所有自定义的controllers,必填
        Controllers: UninitControllers;
    
        // koa-body的配置,传false可禁用自动 use koa-body
        koaBodyOptions?: KoaBodyMiddlewareOptions | false;
        
        // koa-views的配置,传false可禁用自动 use koa-views
        koaViewsOptions?: KoaViewsOptions | false;
    
        // url路由前缀,一般需要与client端的ApiService对应配置一起使用。默认为空
        prefix?: string;
        
        /* controller命名后缀,默认为'Controller'
        若定义的名称为:TestController,那么在url路由和调用中的名称会识别为Test
        若定义的名称为:TestCtrl,则保持不变 */
        controllerSuffix?: string;
        
        /* 当路由里缺省controllerName或actionName时对应的默认值
        可设置:"localhost/" "localhost/user" 这类短地址默认指向的controller和action */
        defaultPathMatchs?: RoutePathMatchs;
        
        /* 全局默认filter,当controller和action没有同类filter时,会执行这里的filter */
        globalFilters?: Filter.BindConfig[];
        
        /* 接口响应状态为success时,json body里的code。默认为0
        如要自定义,需要与client端的ApiService对应配置一起使用 */
        successCode?: number;
        
        // 开启后,get请求会从queryKey读取并解析reqData。需和ApiService的对应配置一起使用
        transformGetMethodJsonData?: {
            enabled: boolean;
            queryKey: string;
        };
        
        /* 可自定义传入action的reqData */
        getRequestData?: (nowReqData: object, ctx: Context) => InnerDeclares.MayPromise<object>;
        
        /* 在接口设置json body前,可做最后修改 */
        getJsonBody?: (nowJsonBody: object, context: Context) => InnerDeclares.MayPromise<object>;
        
        /* 在接口响应Response前,可做最后调整 */
        onResponse?: (ctx: Context, next: Next) => InnerDeclares.MayPromise<void>;
    };
    

clinet端接口调用:ApiService

  • new ApiService<TControllers, TControllerSuffix>(initOptions)

    • 泛型TControllers:必须是个object,其中每个值都要是一个Controller,或是Controller的实例

    • 泛型TControllerSuffix:一般不传,默认为'Controller',和中间件的controllerSuffix配合使用

    • Options:

      // 以下大部分字段,除了可以直接提供最终值,也可以提供一个同步或异步函数以获取最终值
      type InitOptions = {
          // 重试次数,默认不重试
          retry?: number;
          
          // 默认为 "/"
          origin?: ValueOrFunc<string, OmitUrlPreCallFuncParams>;
          
          // 请求api时统一在url上加的前缀,一般要和中间件的对应配置一起使用。默认为空
          prefix?: ValueOrFunc<string, OmitUrlPreCallFuncParams>;
          
          // 当接口返回json数据时,哪些code会认为success状态,一般要和中间件的对应配置一起使用。
          // 默认为:[0]
          successCodes?: ValueOrFunc<SuccessCodes, PostCallFuncParams>;
          
          // 超时时间。默认为空
          timeout?: PreCallValueOrFunc<number>;
          
          // 默认为POST
          httpMethod?: PreCallValueOrFunc<HttpMethod>;
          
          // 默认为空,content-type等字段如果没有设置,会在请求时根据数据类型自动设置。
          headers?: PreCallValueOrFunc<Record<string, number | string>>;
          
          // 默认的公共请求数据,每次请求都会带上,极端场景才会用到。
          commonReqData?: PreCallValueOrFunc<ReqData>;
          
          // 公共的axios RequestConfig
          baseRequestConfig?: PreCallValueOrFunc<BaseRequestConfig>;
          
          // 开启后,get请求会将reqData序列化后用queryKey传递。需和中间件的对应配置一起使用
          transformGetMethodJsonData?: {
              enabled: boolean;
              queryKey: string;
          };
          
          // 在发起请求前,最后调整axios RequestConfig
          getFinallyRequestConfig?: (params: PreCallFuncParams & {
              nowRequestConfig: RequestConfig;
          }) => InnerDeclares.MayPromise<RequestConfig>;
          
          // 获取到Response后,可进行预处理
          onReturnResponse?: (params: PostCallFuncParams) => InnerDeclares.MayPromise<Response<ResData, RawResponse>>;
          
          // 异常处理
          onError?: (error: unknown, params: OmitUrlPreCallFuncParams) => InnerDeclares.MayPromise<unknown>;
      };
      
  • apiService.call.controller.action(reqData, callOptions)

    • reqData:根据对应的controller.action自动映射类型

    • callOptions:

      // 可传所有InitOptions中的字段,并覆盖
      type CallOptions = InitOptions & {
          /* 为false时,会先判断code是否success,非success的code会直接抛异常 
          为true时会得到原始接口回包,开发者需自行判断状态码code是否符合预期。
          对应的返回类型也会因isRaw的值不同而变化。
          默认为false */
          isRaw?: boolean;
          
          /* 是否使用formData请求接口 */
          isFormData?: boolean;
      }
      
    • return类型:

      • isRaw为false或不传,return的类型为接口数据类型(response Data)
      • isRaw为true时,return的类型为原始回包(response Body)

其它模块

  • View:

    用于让action响应除了json body以外的数据类型。目前添加了几个常用的,之后会持续丰富:

    • View.Redirect:302跳转
    • View.StaticFile:返回静态文件
    • View.Render:渲染html
    • View.Buffer:返回buffer数据
    • View.Xml:返回xml数据
  • createFilter:

    创建自定义filter,如权限验证等。

    • 内置filter:httpMethodFilter

FAQs

Package last updated on 09 Mar 2024

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts