客户端代码生成
自动生成类型安全的 API 客户端 SDK
概述
通过 OpenAPI Generator,我们可以为各种编程语言和框架自动生成客户端 SDK。这些 SDK 提供:
- 🔒 类型安全: 自动生成的类型定义
- 🚀 开箱即用: 包含认证、错误处理等功能
- 📝 文档完整: 自动生成的代码注释
- 🔄 保持同步: API 变更时重新生成即可
支持的客户端类型
Web 前端
| 生成器 | 适用场景 | 特性 |
|---|---|---|
typescript-axios | React/Vue/Angular | Promise-based, 拦截器支持 |
typescript-fetch | 现代浏览器 | 原生 Fetch API |
javascript | 传统项目 | ES5 兼容 |
typescript-angular | Angular 项目 | 服务注入,RxJS |
typescript-rxjs | 响应式编程 | Observable 支持 |
移动端
| 生成器 | 平台 | 特性 |
|---|---|---|
swift5 | iOS | Codable, async/await |
kotlin | Android | Coroutines, Retrofit |
dart | Flutter | Null safety |
objc | iOS (Legacy) | ARC 支持 |
后端/脚本
| 生成器 | 语言 | 特性 |
|---|---|---|
python | Python | requests, async 支持 |
go | Go | 标准库 HTTP |
ruby | Ruby | 多种 HTTP 库 |
php | PHP | PSR-7, Guzzle |
csharp | C# | HttpClient, async |
TypeScript 客户端生成
基础配置
bash
# 生成 TypeScript Axios 客户端
openapi-generator-cli generate \
-i api/openapi.yaml \
-g typescript-axios \
-o generated/typescript-client \
--additional-properties=npmName=@company/api-client,npmVersion=1.0.0,supportsES6=true高级配置文件
yaml
# typescript-client-config.yaml
generatorName: typescript-axios
outputDir: ./src/api/client
inputSpec: ./api/openapi.yaml
globalProperties:
apis: true
models: true
supportingFiles: true
modelDocs: false
apiDocs: false
additionalProperties:
npmName: "@company/api-client"
npmVersion: "1.0.0"
supportsES6: true
withInterfaces: true
withSeparateModelsAndApi: true
modelPropertyNaming: camelCase
enumPropertyNaming: UPPERCASE
useSingleRequestParameter: true
typeMappings:
DateTime: Date
Date: string
Long: number
importMappings:
Date: Date生成的代码示例
typescript
// generated/api/user-api.ts
import { Configuration } from './configuration';
import { User, CreateUserRequest, UpdateUserRequest } from './models';
import axios, { AxiosInstance } from 'axios';
export class UserApi {
private axios: AxiosInstance;
constructor(configuration?: Configuration) {
this.axios = axios.create({
baseURL: configuration?.basePath || 'https://api.example.com/v1',
headers: {
'Authorization': `Bearer ${configuration?.accessToken}`,
}
});
}
/**
* 获取用户列表
* @param page 页码
* @param size 每页大小
*/
async listUsers(page?: number, size?: number): Promise<User[]> {
const response = await this.axios.get<User[]>('/users', {
params: { page, size }
});
return response.data;
}
/**
* 创建用户
* @param createUserRequest 用户创建请求
*/
async createUser(createUserRequest: CreateUserRequest): Promise<User> {
const response = await this.axios.post<User>('/users', createUserRequest);
return response.data;
}
}使用生成的客户端
typescript
// app.ts
import { UserApi, Configuration } from '@company/api-client';
// 配置客户端
const config = new Configuration({
basePath: 'https://api.example.com/v1',
accessToken: 'your-jwt-token'
});
const userApi = new UserApi(config);
// 使用 API
async function fetchUsers() {
try {
const users = await userApi.listUsers(1, 20);
console.log('Users:', users);
} catch (error) {
console.error('Error fetching users:', error);
}
}
// 创建用户
async function createNewUser() {
const newUser = await userApi.createUser({
name: 'John Doe',
email: 'john@example.com',
password: 'secure-password'
});
console.log('Created user:', newUser);
}React 集成示例
自定义 Hook
typescript
// hooks/useApi.ts
import { useState, useEffect } from 'react';
import { Configuration, UserApi } from '@company/api-client';
const config = new Configuration({
basePath: process.env.REACT_APP_API_URL,
accessToken: localStorage.getItem('token') || undefined
});
export const useUserApi = () => {
return new UserApi(config);
};
// hooks/useUsers.ts
export const useUsers = (page: number = 1, size: number = 20) => {
const [users, setUsers] = useState<User[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
const userApi = useUserApi();
useEffect(() => {
const fetchUsers = async () => {
try {
setLoading(true);
const data = await userApi.listUsers(page, size);
setUsers(data);
} catch (err) {
setError(err as Error);
} finally {
setLoading(false);
}
};
fetchUsers();
}, [page, size]);
return { users, loading, error };
};React 组件
tsx
// components/UserList.tsx
import React from 'react';
import { useUsers } from '../hooks/useUsers';
export const UserList: React.FC = () => {
const { users, loading, error } = useUsers();
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<ul>
{users.map(user => (
<li key={user.id}>
{user.name} - {user.email}
</li>
))}
</ul>
);
};Python 客户端生成
生成配置
bash
openapi-generator-cli generate \
-i api/openapi.yaml \
-g python \
-o generated/python-client \
--additional-properties=packageName=company_api_client,projectName=company-api-client配置文件
yaml
# python-client-config.yaml
generatorName: python
outputDir: ./generated/python-client
inputSpec: ./api/openapi.yaml
additionalProperties:
packageName: company_api_client
projectName: company-api-client
packageVersion: 1.0.0
library: urllib3 # 或 asyncio, tornado, requests
generateSourceCodeOnly: false使用示例
python
# main.py
from company_api_client import Configuration, ApiClient, UserApi
from company_api_client.models import CreateUserRequest
# 配置
configuration = Configuration(
host="https://api.example.com/v1",
access_token="your-jwt-token"
)
# 创建 API 客户端
with ApiClient(configuration) as api_client:
user_api = UserApi(api_client)
# 获取用户列表
users = user_api.list_users(page=1, size=20)
for user in users:
print(f"User: {user.name} ({user.email})")
# 创建用户
new_user_request = CreateUserRequest(
name="Jane Doe",
email="jane@example.com",
password="secure-password"
)
new_user = user_api.create_user(new_user_request)
print(f"Created user: {new_user.id}")异步客户端
python
# async_example.py
import asyncio
from company_api_client.async_api import AsyncUserApi
from company_api_client import Configuration
async def main():
configuration = Configuration(
host="https://api.example.com/v1",
access_token="your-jwt-token"
)
async with AsyncUserApi(configuration) as api:
# 并发获取多个用户
tasks = [
api.get_user(user_id=f"user-{i}")
for i in range(1, 11)
]
users = await asyncio.gather(*tasks)
for user in users:
print(f"Fetched: {user.name}")
if __name__ == "__main__":
asyncio.run(main())移动端客户端
Swift (iOS)
bash
# 生成 Swift 客户端
openapi-generator-cli generate \
-i api/openapi.yaml \
-g swift5 \
-o generated/ios-client \
--additional-properties=projectName=CompanyAPIClient,responseAs=AsyncAwait使用示例:
swift
// UserService.swift
import CompanyAPIClient
class UserService {
let api = UserAPI()
func fetchUsers() async throws -> [User] {
let users = try await api.listUsers(page: 1, size: 20)
return users
}
func createUser(name: String, email: String) async throws -> User {
let request = CreateUserRequest(
name: name,
email: email,
password: "secure-password"
)
let user = try await api.createUser(createUserRequest: request)
return user
}
}Kotlin (Android)
bash
# 生成 Kotlin 客户端
openapi-generator-cli generate \
-i api/openapi.yaml \
-g kotlin \
-o generated/android-client \
--additional-properties=packageName=com.company.api,library=retrofit2,dateLibrary=java8使用示例:
kotlin
// UserRepository.kt
import com.company.api.apis.UserApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
class UserRepository {
private val api = UserApi()
suspend fun getUsers(page: Int = 1, size: Int = 20): List<User> {
return withContext(Dispatchers.IO) {
api.listUsers(page, size)
}
}
suspend fun createUser(name: String, email: String): User {
return withContext(Dispatchers.IO) {
val request = CreateUserRequest(
name = name,
email = email,
password = "secure-password"
)
api.createUser(request)
}
}
}客户端定制
自定义模板
创建自定义 Mustache 模板:
mustache
{{! custom-api.mustache }}
{{#operations}}
export class {{classname}}Custom extends {{classname}} {
constructor(configuration?: Configuration) {
super(configuration);
}
{{#operation}}
/**
* {{summary}}
* 自定义方法包装
*/
async {{nickname}}WithRetry({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{{dataType}}}{{#hasMore}}, {{/hasMore}}{{/allParams}}): Promise<{{{returnType}}}> {
let attempts = 0;
const maxAttempts = 3;
while (attempts < maxAttempts) {
try {
return await this.{{nickname}}({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}});
} catch (error) {
attempts++;
if (attempts >= maxAttempts) throw error;
await new Promise(resolve => setTimeout(resolve, 1000 * attempts));
}
}
throw new Error('Max retry attempts reached');
}
{{/operation}}
}
{{/operations}}使用自定义模板:
bash
openapi-generator-cli generate \
-i api/openapi.yaml \
-g typescript-axios \
-o generated/client \
-t ./templates \
--additional-properties=customApi=true拦截器和中间件
typescript
// interceptors.ts
import axios from 'axios';
// 请求拦截器
axios.interceptors.request.use(
config => {
// 添加时间戳
config.params = {
...config.params,
_t: Date.now()
};
// 添加追踪 ID
config.headers['X-Request-ID'] = generateUUID();
return config;
},
error => Promise.reject(error)
);
// 响应拦截器
axios.interceptors.response.use(
response => {
// 记录响应时间
const duration = Date.now() - response.config.metadata.startTime;
console.log(`API call took ${duration}ms`);
return response;
},
error => {
if (error.response?.status === 401) {
// 刷新令牌
return refreshToken().then(() => {
return axios.request(error.config);
});
}
return Promise.reject(error);
}
);测试生成的客户端
单元测试
typescript
// __tests__/userApi.test.ts
import { UserApi } from '../generated/api';
import MockAdapter from 'axios-mock-adapter';
import axios from 'axios';
describe('UserApi', () => {
let api: UserApi;
let mock: MockAdapter;
beforeEach(() => {
mock = new MockAdapter(axios);
api = new UserApi();
});
afterEach(() => {
mock.restore();
});
test('should fetch users', async () => {
const mockUsers = [
{ id: '1', name: 'John', email: 'john@example.com' }
];
mock.onGet('/users').reply(200, mockUsers);
const users = await api.listUsers();
expect(users).toEqual(mockUsers);
});
test('should handle errors', async () => {
mock.onGet('/users').reply(500);
await expect(api.listUsers()).rejects.toThrow();
});
});集成测试
python
# test_integration.py
import pytest
from company_api_client import UserApi, Configuration
@pytest.fixture
def api_client():
config = Configuration(
host="http://localhost:3000/api/v1"
)
return UserApi(configuration=config)
def test_user_lifecycle(api_client):
# 创建用户
user = api_client.create_user(
name="Test User",
email="test@example.com"
)
assert user.id is not None
# 获取用户
fetched_user = api_client.get_user(user.id)
assert fetched_user.name == "Test User"
# 更新用户
updated_user = api_client.update_user(
user.id,
name="Updated User"
)
assert updated_user.name == "Updated User"
# 删除用户
api_client.delete_user(user.id)
# 验证删除
with pytest.raises(ApiException) as exc:
api_client.get_user(user.id)
assert exc.value.status == 404发布和分发
NPM 发布
json
// package.json
{
"name": "@company/api-client",
"version": "1.0.0",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"files": [
"dist"
],
"scripts": {
"build": "tsc",
"prepare": "npm run build",
"prepublishOnly": "npm test && npm run build"
},
"publishConfig": {
"registry": "https://npm.company.com"
}
}发布流程:
bash
# 构建
npm run build
# 测试
npm test
# 发布
npm publish --access publicPyPI 发布
python
# setup.py
from setuptools import setup, find_packages
setup(
name="company-api-client",
version="1.0.0",
packages=find_packages(),
install_requires=[
"urllib3>=1.25.3",
"python-dateutil>=2.8.0",
"pydantic>=1.8.0"
],
python_requires=">=3.7",
author="API Team",
author_email="api@company.com",
description="Company API Client",
long_description=open("README.md").read(),
long_description_content_type="text/markdown",
url="https://github.com/company/api-client-python",
)最佳实践
1. 版本管理
typescript
// version-check.ts
const API_VERSION = '1.0.0';
const MIN_SUPPORTED_VERSION = '0.9.0';
export function checkApiVersion(serverVersion: string): boolean {
return compareVersions(serverVersion, MIN_SUPPORTED_VERSION) >= 0;
}2. 错误处理
typescript
// error-handler.ts
export class APIErrorHandler {
static handle(error: any): never {
if (error.response) {
// 服务器响应错误
const apiError = {
status: error.response.status,
message: error.response.data.message || 'Unknown error',
code: error.response.data.code,
timestamp: new Date().toISOString()
};
throw new APIError(apiError);
} else if (error.request) {
// 网络错误
throw new NetworkError('Network error occurred');
} else {
// 其他错误
throw new UnknownError(error.message);
}
}
}3. 性能优化
typescript
// cache.ts
export class APICache {
private cache = new Map<string, CacheEntry>();
async get<T>(
key: string,
fetcher: () => Promise<T>,
ttl: number = 60000
): Promise<T> {
const cached = this.cache.get(key);
if (cached && cached.expires > Date.now()) {
return cached.data as T;
}
const data = await fetcher();
this.cache.set(key, {
data,
expires: Date.now() + ttl
});
return data;
}
}总结
通过 OpenAPI Generator 生成客户端 SDK:
- 🚀 提高效率: 自动生成减少手工编码
- 🔒 类型安全: 编译时发现错误
- 📚 文档完整: 自动生成的注释和文档
- 🔄 保持同步: API 变更时快速更新
正确使用客户端生成器,可以大大提高前后端协作效率,减少集成错误。