0.1.28 • Published 4 months ago

@snail-js/api v0.1.28

Weekly downloads
-
License
MIT
Repository
-
Last release
4 months ago

中文文档|English Document

项目介绍

  • 基于 Axios 二次封装
  • 使用reflect-metadata创建和处理元数据
  • 提供装饰器定义请求请求的方式,支持所有请求方法和 SSE

安装

npm install @snail-js/api

使用

  1. 请开启TypeScript相关装饰器配置
// tsconfig.json
{
  "module": "ESNext",
  // 模块解析策略
  "moduleResolution": "node",
  "baseUrl": ".",
  // target 必须大于ES6
  "target": "ESNext",
  // lib 需要包含大于ES6的ES版本
  "lib": ["ESNext", "DOM"],
  // 包含reflect-metadata类型
  "types": ["reflect-metadata"],
  "emitDecoratorMetadata": true,
  "experimentalDecorators": true,

  "skipLibCheck": true,
  "strictNullChecks": false
}
  1. 创建Snail后端基本配置实例
// service.ts
import { SnailServer, Server } from "@snail-js/api";

@Server({
  baseURL: "/api",
  timeout: 5000,
})
class BackEnd extends SnailServer {}

export const Service = new BackEnd();
  1. 创建 Api 实例
// user.ts
import { Api, Get, Post, Query, Data, SnailApi } from "@snail-js/api";

import { Service } from "./service";

@Api("user")
class UserApi extends SnailApi {
  @Get()
  get(@Query("id") id: string) {}

  @Post()
  create(@Data() user: User) {}
}
// 创建并导出api
export const userApi = Service.createApi(UserApi);
  1. 发送请求
import { userApi } from "./user";

const { send:getUser, onSuccess, onError, onHitCache } = await userApi.get();

const data = await getUser("1");

SnailMethod 实例

  • 调用Service.createApi(ApiInstance)后会为ApiInstance内被RequestMethod(如:@Get、@Post...)装饰的方法创建一个代理,返回一个函数,此函数包含请求参数,调用此函数返回SnailMethod 实例

SnailMethod 实例方法

  • send 发送请求 异步函数,发送当前请求
  • onSuccess 请求成功回调 注册请求成功事件
  • onError 请求失败回调 注册请求失败事件
  • onHitCache 请求命中缓存回调 注册请求命中缓存事件
  • onFinish 请求完成回调 注册请求完成事件
  • registerStrategies 注册策略 注册方法级策略

SnailMethod 实例属性

  • response : AxiosResponse
  • request : AxiosRequestConfig ,最终请求的 request,这个 request 是被 Versioning 和 Strategy 处理过的
  • version : string,最终请求的版本,如果没有开启 Versioning 则为 undefine
  • name : string,完整的 SnailMethod 名称,格式为ServerName.ApiName.MethodName
  • error : Error | null,请求失败的错误信息,无错误为 null

Server 配置

Api 配置

  • 请使用@Api()装饰自定义 Api 类并继承SnailApi

请求方法装饰器

  • Api类中使用,用于标记请求方法
  • 提供 axios 的全部请求方法Get,Post,Head,Put,Delete,Patch,Options
  • 参数: path?: string; 请求端点路径,与baseUrl,api.url共同拼接组成最终请求路径

参数装饰器

查询参数 @Query

  • @Query(key?:string)

  • 单个参数使用

@Api("user")
class UserApi {
  @Get()
  get(@Query("id") id: string, @Query("sign") sign: string) {}
}

传入 key,标记单个查询参数,拼接到请求?k1=v1&k2=v2

路由参数 @Params

  • @Params(key?:string)

  • 单个参数使用

@Api("user/:id/:sign")
class UserApi {
  @Get()
  get(@Params("id") id: string, @Params("sign") sign: string) {}
}

传入 key,标记单个查询参数,拼接到请求?k1=v1&k2=v2

  • 对象参数使用
class RouteParams {
  id: string;
  sign: string;
}

@Api("user/:id/:sign")
class UserApi {
  @Get()
  get(@Params() params: RouteParams) {}
}

不传入 key,会被标记为对象类型查询参数;也能自动拼接到请求

  • 混合使用
class RouteParams {
  id: string;
  sign: string;
}

@Api("user/:id/:sign")
class UserApi {
  @Get()
  get(@Params() params: Query, @Params("a") a: number) {}
}

请求数据

  • @Data(key?:string)
  • 使用方式和@Params相同,也支持混合使用

策略装饰器@UseStrategy

  • @UseStrategy(...Strategy[])

请求策略

  • 在请求发送前执行,后面的策略返回结果会覆盖前面的策略
  • 若返回处理后的 request,则使用处理后的 request 发送请求,否则使用原始 request 或上一个策略返回的 request 发送请求
class CustomStrategy extends Strategy {
  applyRequest(request: AxiosRequestConfig) {
    request.headers["Access-Token"] = "abcde";
    return request;
  }
}

// 用在Snail,全局的请求策略

@Server({
  baseURL: "/api",
  timeout: 5000,
})
@UseStrategy(CustomStrategy)
class BackEnd extends Snail<ShanheResponse> {}
export const Service = new BackEnd();
// 创建Service实例后再注册策略
Service.registerStrategies(CustomStrategy);

// 用在Api, 当Api下的方法请求时生效
@Api("test")
@UseStrategy(CustomStrategy)
class Test {}
const TestApi = Service.createApi(Test);
// 创建Api实例后再注册策略
TestApi.registerStrategies(CustomStrategy);

// 用在方法,此方法请求时生效
@Api("test")
@UseStrategy(CustomStrategy)
class Test {
  @Get()
  @UseStrategy(CustomStrategy)
  get() {}
}
// 发送请求前注册策略
const TestApi = Service.createApi(Test);
const getSomething = TestApi.get();
{ send, registerStrategies } = getSomething;
getSomething.registerStrategies(CustomStrategy);

响应策略

  • 在收到服务器响应后执行
  • 若返回处理后的 response,则使用处理后的 response 进行下一个策略或返回,否则使用原始 response 或上一个策略返回的 response 返回
// 如何定义
class CustomStrategy extends Strategy {
  applyResponse(response: AxiosResponse) {
    const { status } = response;
    if (status == 200) {
      // do something
    }
    return response;
  }
}

版本管理装饰器@Versioning@Version

版本管理器@Versioning(VersioningOption)

  • 全局管理版本
@Server({
  baseURL: "/api",
  timeout: 5000,
})
@Versioning({
  type: VersioningType.Header,
  defaultVersion: "0.1.0",
})
class BackEnd extends Snail<ShanheResponse> {}

export const Service = new BackEnd();

VersioningOption类型

export enum VersioningType {
  Uri,
  Header,
  Query,
  Custom,
}

interface VersioningCommonOption {
  defaultVersion: string;
}

export interface VersioningUriOption extends VersioningCommonOption {
  type: VersioningType.Uri;
  prefix?: string;
}

export interface VersioningHeaderOption extends VersioningCommonOption {
  type: VersioningType.Header;
  header?: string;
}

export interface VersioningQueryOption extends VersioningCommonOption {
  type: VersioningType.Query;
  key?: string;
}

export interface VersioningCustomOption extends VersioningCommonOption {
  type: VersioningType.Custom;
  extractor: (requestOptions: unknown) => {
    url: string;
    headers: Record<string, any>;
  };
}

export type VersioningOption =
  | VersioningUriOption
  | VersioningHeaderOption
  | VersioningQueryOption
  | VersioningCustomOption;

SnailServerStatusCodeRuleOptions 类型

export class SnailServerStatusCodeRuleOptions {
  // 通常服务端会返回一个状态码,当状态码不符合预期时,会抛出错误
  // 可以使用此选项来定义服务端状态码的规则
  // 当rule函数返回false时,会触发错误
  rule: (statusCode: number) => boolean;
  // response.data中服务端状态码的key,默认为code
  key?: string;
}

临时版本修改器@Version

  • 临时改变方法请求的版本
@Api("test")
class Test {
  @Get("HelloWorld")
  @Version("0.2.0")
  test() {}
}

临时改变 api 版本,便于测试

缓存装饰器@HitSource

  • @HitSource(name:string)
  • 为被装饰的方法设置缓存失效源,当设置的名称方法被调用且正常响应时,被装饰的方法缓存失效
  • name 格式为:serverName:apiName:methodName

    注意:若您配置了 SnailServer/SnailApi 的 name 选项,请使用此 name 作为名称,否则使用类名作为名称

@Api("test", { name: "api1" })
@HitSource("api1")
class Test {
  @Get("HelloWorld")
  @HitSource("api1.test2")
  test1() {}

  @Post()
  test2() {}

  @Get()
  // Test类下任何请求成功,这个方法的缓存都会失效
  @HitSource("api1")
  test3() {}
}

当请求[Post]test成功时,[Get]test/HelloWorld的缓存失效 默认仅 Get 方法会进行缓存 ing 缓存,若要开启其他方法的缓存,请使用@Server({cacheFor:'all'})配置

test3方法请求成功时,不缓存

注意:要使用缓存,请配置@Server({CacheManage})缓存管理器

上传进度装饰器@UploadProgress

  • @UploadProgress((progressEvent: AxiosProgressEvent) => void)

下载进度装饰器@DownloadProgress

  • @DownloadProgress((progressEvent: AxiosProgressEvent) => void)

Server Send Event 服务端推送

创建 sse 端点

@Sse("sse")
class ServerSend extend SnailSse {

  @OnSseOpen()
  handleOpen(event: Event) {
    console.log("sse-open:", event);
  }

  @OnSseError()
  handleError(event: Event) {
    console.log("sse-error:", event);
  }

  // 处理默认message事件
  @SseEvent()
  handleEvent(event: MessageEvent) {
    console.log("sse-event[message]:", event.data);
  }

  // 处理自定义名称事件
  @SseEvent("chunk")
  handleEvent(event: Event) {
    console.log("sse-event[chunk]:", event);
  }
}

export const Sse = Service.createSse(ServerSend);

服务端推送装饰器@Sse

  • @Sse(path:string,options?:{withCredentials?: boolean,version?: string;})
  • 创建一个服务端推送连接,返回一个函数,用于打开 sse 连接
    • 返回的打开函数调用后会返回{eventSource:EventSource,close:function}
      • eventSource: sse 连接实例
      • close: 关闭此 sse 连接的方法

注册onopen装饰器@OnSseOpen

  • @Sse装饰的方法被调用时,将@OnSseOpen装饰的方法注册为@Sse装饰的方法返回的EventSource实例onopen处理函数

注册onerror装饰器@OnSseError

  • @Sse装饰的方法被调用时,将@OnSseError装饰的方法注册为@Sse装饰的方法返回的EventSource实例onerror处理函数

事件处理装饰器@SseEvent

  • @SseEvent(eventName?:string)
  • 未传入eventName,默认注册为message事件处理器
  • 传入eventName,注册为对应名称的事件处理器

TypeScript 支持

默认返回类型

export type StandardResponseData<
  T extends ResponseJsonData = Record<string, any>
> = {
  code: number;
  message: string;
  data: T;
};

定义返回类型

  1. 定义后端标准数据格式
export class CustomResponse {
  status_code: number;
  msg: string;
}
  1. 创建时应用格式
// service.ts
import { SnailServer, Server } from "@snail-js/api";

@Server({
  baseURL: "/api",
  timeout: 5000,
})
class BackEnd extends SnailServer<CustomResponse> {}

export const Service = new BackEnd();
  1. 调用 API 时携带数据格式
import { userApi } from "./user";

class User {
  id: number;
  name: string;
  tel: string;
  age: number;
}

const getUser = userApi.get<User>();
const { send } = getUser;

const res = await send("1");

// 默认情况,以data为key存储数据
// res.data => CustomResponse & { data : User}

API 被调用的返回格式

const getUser = userApi.get<User>();
const { send } = getUser;
const res = await send("1");

// res.data => CustomResponse & { data: User }

const getUser = userApi.get<Blob>();
const { send } = getUser;
const res = await send("1");
// res => AxiosResponse<Blob>

data: T,后端响应数据;默认为ResponseData<T = any>类型;可由用户自定义修改

error: null | Error ; 请求过程中的错误包含在此,可先判断 error 是否为 null 再进行数据处理

hitCache?: boolean; 标识请求是否击中缓存;若从缓存获得数据,则不会发送请求。

  1. 若后端返回数据不是以 data 为 key 包含数据
@Server({
  baseURL: "/api",
  timeout: 5000,
})
class BackEnd extends Snail<CustomResponse, "records"> {}

const res = await userApi.get<User>();
// 自定义数据key
// res.data => CustomResponse & { records : User}

非 json 数据的返回

  • 若后端返回的 content-type 不是 json 类型,send 方法返回的将是AxiosResponse
  • 若后端返回的 content-type 是 json 类型,send 方法返回的将是AxiosResponse.data

代码仓库

作者

  • mc.lee
0.1.28

4 months ago

0.1.27

4 months ago

0.1.26

4 months ago

0.1.25

4 months ago

0.1.24

4 months ago

0.1.23

4 months ago

0.1.22

4 months ago

0.1.21

4 months ago

0.1.20

4 months ago

0.1.19

4 months ago

0.1.18

4 months ago

0.1.17

4 months ago

0.1.16

4 months ago

0.1.15

4 months ago

0.1.14

5 months ago

0.1.13

5 months ago

0.1.12

5 months ago

0.1.11

5 months ago

0.1.10

5 months ago

0.1.9

5 months ago

0.1.8

5 months ago

0.1.7

5 months ago

0.1.6

5 months ago

0.1.5

5 months ago

0.1.4

5 months ago

0.1.3

5 months ago

0.1.2

5 months ago

0.1.1

6 months ago

0.1.0

6 months ago