Skip to content

使用生成代码 ​

实现 HTTP 客户端接口 ​

生成的客户端层不绑定框架。只要实现一次 HttpClient 接口,接上项目里现成的 HTTP 库就行:

typescript
import { HttpClient, RequestConfig } from './generated/clients/http-client';

class MyHttpClient implements HttpClient {
  async get<T>(url: string, params?: any, config?: RequestConfig): Promise<T> {
    const response = await fetch(url, {
      method: 'GET',
      headers: config?.headers,
      signal: config?.signal,
    });
    return response.json();
  }

  async post<T>(url: string, data?: any, config?: RequestConfig): Promise<T> {
    const response = await fetch(url, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        ...config?.headers,
      },
      body: JSON.stringify(data),
      signal: config?.signal,
    });
    return response.json();
  }

  // 实现 put、delete、patch 方法...
}

使用生成的客户端 ​

typescript
import { UserServiceClient } from './generated/clients/user-client';
import { MyHttpClient } from './http-client';

const httpClient = new MyHttpClient();
// 不传前缀:proto 已经声明了完整路径(/api/v1/users/{user_id})
const userService = new UserServiceClient(httpClient);

// 调用 API
const user = await userService.getUser({ userId: '123' });
console.log(user);

baseUrl 参数 ​

客户端构造函数接受可选的第二个参数 baseUrl,会拼接到每个 proto 声明的请求路径前面。默认是空字符串,也就是直接用 proto 里声明的路径发请求。

只有当前端请求路径和 proto 声明的不一致时才需要传——典型场景是网关(比如 Nginx)在转发时加了前缀。比如 proto 声明的是 /user/get,Nginx 把所有请求都改写成 /api/user/get,那就传 /api:

typescript
// 后端 proto:rpc GetUser(...) { option (google.api.http) = { get: "/user/get" }; }
// 前端走 Nginx 转发,统一加了 /api → /api/user/get
const userService = new UserServiceClient(httpClient, '/api');

如果前后端路径完全一致,就不用传。

响应类型解包 ​

生成的客户端支持解包 { code, data, message } 这类响应包装。传入包装类型和实际数据所在的字段名即可:

typescript
// 定义响应包装类型
interface ApiResponse<T> {
  code: number;
  data: T;
  message: string;
}

// 使用泛型参数自动解包
const userService = new UserServiceClient<ApiResponse<any>, 'data'>(httpClient);

// 返回类型自动推断为 User,而非 ApiResponse<User>
const user = await userService.getUser({ userId: '123' });

三个泛型参数:

  • TRes - 响应包装类型。void(默认)表示不包装,返回原始响应类型。传入业务包装类型可自动解包。
  • TDataKey - 包装类型里实际数据所在的字段名。默认 'data',可设为 'payload'、'result'、'body' 等。
  • TExtras - RequestConfig.extras 的类型约束。默认无约束;传入具体类型(如 AxiosRequestConfig)可对框架专属选项做完整类型检查。

请求级配置 ​

每个方法都接受可选的 RequestConfig,按请求单独控制行为:

typescript
await userService.getUser(
  { userId: '123' },
  { silent: true, signal: controller.signal, headers: { 'X-Trace-Id': 'abc' } },
);
  • silent - 在请求级别静默错误(不弹 UI 提示)
  • signal - AbortController 信号,用于取消请求
  • headers - 自定义请求头
  • responseType - 覆盖响应处理方式(json、blob、text、arraybuffer)
  • extras - 框架专属配置,由你的适配层解析
  • metadata - 声明式请求元数据,供你的 HttpClient 实现据此做路由决策