自定义模板
定制 OpenAPI Generator 的代码生成模板
模板引擎概述
OpenAPI Generator 使用 Mustache 模板引擎,通过模板定制可以:
- 🎨 自定义代码风格: 符合团队编码规范
- 🔧 添加特定功能: 日志、监控、缓存等
- 📦 集成框架特性: 依赖注入、AOP 等
- 🏢 企业标准化: 版权信息、安全要求
模板结构
模板文件组织
custom-templates/
├── api/ # API 相关模板
│ ├── api.mustache # 接口定义
│ ├── apiController.mustache # 控制器实现
│ └── apiService.mustache # 服务层
├── model/ # 数据模型模板
│ ├── model.mustache # 模型类
│ └── enum.mustache # 枚举类型
├── supporting/ # 支持文件模板
│ ├── pom.mustache # Maven 配置
│ ├── README.mustache # 项目说明
│ └── Dockerfile.mustache # Docker 配置
└── config.json # 模板配置模板变量
常用的 Mustache 变量:
mustache
{{package}} # 包名
{{classname}} # 类名
{{#operations}} # 操作列表
{{classname}} # API 类名
{{#operation}} # 单个操作
{{operationId}} # 操作 ID
{{httpMethod}} # HTTP 方法
{{path}} # 路径
{{summary}} # 摘要
{{notes}} # 详细说明
{{#allParams}} # 所有参数
{{paramName}} # 参数名
{{dataType}} # 数据类型
{{required}} # 是否必需
{{/allParams}}
{{/operation}}
{{/operations}}创建自定义模板
1. Java Spring Boot 模板
自定义控制器模板
mustache
{{! apiController.mustache }}
package {{package}};
{{#imports}}import {{import}};
{{/imports}}
import org.springframework.web.bind.annotation.*;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import io.micrometer.core.annotation.Timed;
import io.micrometer.core.annotation.Counted;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.CacheEvict;
/**
* {{#appName}}{{appName}} - {{/appName}}{{classname}} 控制器
* {{#appDescription}}{{appDescription}}{{/appDescription}}
*
* @author OpenAPI Generator
* @version {{appVersion}}
* @since {{generatedDate}}
*/
@Slf4j
@RestController
@RequestMapping("{{#contextPath}}{{contextPath}}{{/contextPath}}{{basePath}}")
@RequiredArgsConstructor
@Validated
@Tag(name = "{{baseName}}", description = "{{description}}")
public class {{classname}}Controller implements {{classname}} {
private final {{classname}}Service service;
private final MetricsService metricsService;
{{#operations}}
{{#operation}}
/**
* {{summary}}
{{#notes}}
* {{notes}}
{{/notes}}
*/
@Override
@{{httpMethod}}("{{path}}")
@Timed(value = "api.{{operationIdCamelCase}}.time", description = "{{summary}} 执行时间")
@Counted(value = "api.{{operationIdCamelCase}}.count", description = "{{summary}} 调用次数")
{{#vendorExtensions.x-cached}}
@Cacheable(value = "{{operationIdCamelCase}}", key = "#{{#allParams}}{{#isPathParam}}{{paramName}}{{/isPathParam}}{{/allParams}}")
{{/vendorExtensions.x-cached}}
public ResponseEntity<{{>returnTypes}}> {{operationId}}(
{{#allParams}}
{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{#hasMore}},{{/hasMore}}
{{/allParams}}
) {
// 记录请求
log.info("API 调用: {{operationId}} {{#allParams}}{{#isPathParam}}- {{paramName}}: {}{{/isPathParam}}{{/allParams}}"{{#allParams}}{{#isPathParam}}, {{paramName}}{{/isPathParam}}{{/allParams}});
// 开始计时
long startTime = System.currentTimeMillis();
try {
// 参数验证
{{#allParams}}
{{#required}}
Objects.requireNonNull({{paramName}}, "参数 {{paramName}} 不能为空");
{{/required}}
{{/allParams}}
// 调用服务层
{{#returnType}}{{returnType}} result = {{/returnType}}service.{{operationId}}({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}});
// 记录响应时间
long duration = System.currentTimeMillis() - startTime;
log.info("API 响应: {{operationId}} - 耗时: {}ms", duration);
metricsService.recordApiCall("{{operationId}}", duration, true);
{{#returnType}}
return ResponseEntity.ok()
.header("X-Response-Time", String.valueOf(duration))
.body(result);
{{/returnType}}
{{^returnType}}
return ResponseEntity.ok()
.header("X-Response-Time", String.valueOf(duration))
.build();
{{/returnType}}
} catch (NotFoundException e) {
log.warn("资源未找到: {}", e.getMessage());
metricsService.recordApiCall("{{operationId}}", System.currentTimeMillis() - startTime, false);
throw new ResponseStatusException(HttpStatus.NOT_FOUND, e.getMessage());
} catch (ValidationException e) {
log.warn("参数验证失败: {}", e.getMessage());
metricsService.recordApiCall("{{operationId}}", System.currentTimeMillis() - startTime, false);
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, e.getMessage());
} catch (Exception e) {
log.error("处理请求失败", e);
metricsService.recordApiCall("{{operationId}}", System.currentTimeMillis() - startTime, false);
throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "内部服务器错误");
}
}
{{#vendorExtensions.x-cached}}
@CacheEvict(value = "{{operationIdCamelCase}}", allEntries = true)
public void evict{{operationIdCamelCase}}Cache() {
log.info("清除缓存: {{operationIdCamelCase}}");
}
{{/vendorExtensions.x-cached}}
{{/operation}}
{{/operations}}
}自定义模型模板
mustache
{{! model.mustache }}
package {{package}};
{{#imports}}import {{import}};
{{/imports}}
import lombok.*;
import lombok.experimental.SuperBuilder;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import io.swagger.v3.oas.annotations.media.Schema;
import javax.validation.constraints.*;
import javax.validation.Valid;
import java.io.Serializable;
import java.time.OffsetDateTime;
import java.util.*;
/**
* {{#description}}{{description}}{{/description}}{{^description}}{{classname}}{{/description}}
*/
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = {{#parent}}true{{/parent}}{{^parent}}false{{/parent}})
@ToString(callSuper = {{#parent}}true{{/parent}}{{^parent}}false{{/parent}})
@Schema(description = "{{description}}")
{{#parent}}
public class {{classname}} extends {{parent}} implements Serializable {
{{/parent}}
{{^parent}}
public class {{classname}} implements Serializable {
{{/parent}}
private static final long serialVersionUID = 1L;
{{#vars}}
/**
* {{description}}
*/
@JsonProperty("{{baseName}}")
@Schema(
description = "{{description}}"{{#example}},
example = "{{example}}"{{/example}}{{#required}},
required = true{{/required}}
)
{{#required}}
@NotNull(message = "{{baseName}} 不能为空")
{{/required}}
{{#isString}}
{{#minLength}}
@Size(min = {{minLength}}{{#maxLength}}, max = {{maxLength}}{{/maxLength}}, message = "{{baseName}} 长度必须在 {{minLength}}{{#maxLength}} 到 {{maxLength}}{{/maxLength}} 之间")
{{/minLength}}
{{^minLength}}
{{#maxLength}}
@Size(max = {{maxLength}}, message = "{{baseName}} 长度不能超过 {{maxLength}}")
{{/maxLength}}
{{/minLength}}
{{#pattern}}
@Pattern(regexp = "{{pattern}}", message = "{{baseName}} 格式不正确")
{{/pattern}}
{{/isString}}
{{#isEmail}}
@Email(message = "{{baseName}} 必须是有效的邮箱地址")
{{/isEmail}}
{{#minimum}}
@Min(value = {{minimum}}, message = "{{baseName}} 不能小于 {{minimum}}")
{{/minimum}}
{{#maximum}}
@Max(value = {{maximum}}, message = "{{baseName}} 不能大于 {{maximum}}")
{{/maximum}}
{{#isContainer}}
@Valid
{{/isContainer}}
private {{{dataType}}} {{name}}{{#defaultValue}} = {{{defaultValue}}}{{/defaultValue}};
{{/vars}}
{{#hasEnums}}
{{#vars}}
{{#isEnum}}
/**
* {{description}}
*/
@JsonCreator
public enum {{datatypeWithEnum}} {
{{#allowableValues}}
{{#enumVars}}
@JsonProperty("{{value}}")
{{name}}("{{value}}"){{^-last}},{{/-last}}{{#-last}};{{/-last}}
{{/enumVars}}
{{/allowableValues}}
private final String value;
{{datatypeWithEnum}}(String value) {
this.value = value;
}
@JsonValue
public String getValue() {
return value;
}
@Override
public String toString() {
return String.valueOf(value);
}
public static {{datatypeWithEnum}} fromValue(String value) {
for ({{datatypeWithEnum}} e : {{datatypeWithEnum}}.values()) {
if (e.value.equals(value)) {
return e;
}
}
throw new IllegalArgumentException("Unexpected value '" + value + "'");
}
}
{{/isEnum}}
{{/vars}}
{{/hasEnums}}
/**
* 验证对象
*/
public boolean isValid() {
{{#requiredVars}}
if ({{name}} == null) {
return false;
}
{{/requiredVars}}
return true;
}
/**
* 转换为 Map
*/
public Map<String, Object> toMap() {
Map<String, Object> map = new HashMap<>();
{{#vars}}
map.put("{{baseName}}", {{name}});
{{/vars}}
return map;
}
}2. TypeScript 模板
自定义 API 客户端模板
mustache
{{! apiClient.mustache }}
/* tslint:disable */
/* eslint-disable */
/**
* {{appName}}
* {{appDescription}}
*
* OpenAPI spec version: {{appVersion}}
* {{#contact}}Contact: {{contactEmail}}{{/contact}}
*
* NOTE: This file is auto generated by OpenAPI Generator
* Do not edit this file manually.
*/
{{#imports}}
import { {{classname}} } from '{{filename}}';
{{/imports}}
import { Configuration } from './configuration';
import { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
import axios from 'axios';
export interface {{classname}}Interface {
{{#operations}}
{{#operation}}
/**
* {{summary}}
{{#notes}}
* {{¬es}}
{{/notes}}
*/
{{nickname}}({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{{dataType}}}{{#hasMore}}, {{/hasMore}}{{/allParams}}): Promise<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}>;
{{/operation}}
{{/operations}}
}
export class {{classname}} implements {{classname}}Interface {
protected axios: AxiosInstance;
protected basePath: string;
protected configuration: Configuration;
// 请求拦截器
private requestInterceptor?: number;
private responseInterceptor?: number;
// 缓存
private cache = new Map<string, { data: any; expires: number }>();
constructor(configuration?: Configuration, basePath?: string, axiosInstance?: AxiosInstance) {
this.configuration = configuration || new Configuration();
this.basePath = basePath || this.configuration.basePath || '{{basePath}}';
this.axios = axiosInstance || axios.create();
this.setupInterceptors();
}
/**
* 设置拦截器
*/
private setupInterceptors(): void {
// 请求拦截器
this.requestInterceptor = this.axios.interceptors.request.use(
(config) => {
// 添加认证信息
if (this.configuration.accessToken) {
config.headers.Authorization = `Bearer ${this.configuration.accessToken}`;
}
// 添加请求 ID
config.headers['X-Request-ID'] = this.generateRequestId();
// 添加时间戳
config.params = {
...config.params,
_t: Date.now()
};
console.log(`[API] ${config.method?.toUpperCase()} ${config.url}`);
return config;
},
(error) => {
console.error('[API] Request error:', error);
return Promise.reject(error);
}
);
// 响应拦截器
this.responseInterceptor = this.axios.interceptors.response.use(
(response) => {
const duration = Date.now() - (response.config as any).startTime;
console.log(`[API] Response ${response.status} - ${duration}ms`);
return response;
},
async (error) => {
if (error.response?.status === 401 && this.configuration.onTokenExpired) {
// 尝试刷新令牌
const newToken = await this.configuration.onTokenExpired();
if (newToken) {
this.configuration.accessToken = newToken;
error.config.headers.Authorization = `Bearer ${newToken}`;
return this.axios.request(error.config);
}
}
console.error('[API] Response error:', error.response?.status, error.message);
return Promise.reject(this.handleError(error));
}
);
}
/**
* 生成请求 ID
*/
private generateRequestId(): string {
return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
}
/**
* 错误处理
*/
private handleError(error: any): Error {
if (error.response) {
const { status, data } = error.response;
const message = data?.message || error.message;
const apiError = new Error(message) as any;
apiError.status = status;
apiError.code = data?.code;
apiError.details = data?.details;
return apiError;
}
return error;
}
/**
* 缓存管理
*/
private getCached<T>(key: string): T | null {
const cached = this.cache.get(key);
if (cached && cached.expires > Date.now()) {
console.log(`[API] Cache hit: ${key}`);
return cached.data;
}
this.cache.delete(key);
return null;
}
private setCached<T>(key: string, data: T, ttl: number = 60000): void {
this.cache.set(key, {
data,
expires: Date.now() + ttl
});
console.log(`[API] Cache set: ${key} (TTL: ${ttl}ms)`);
}
/**
* 清理资源
*/
public dispose(): void {
if (this.requestInterceptor !== undefined) {
this.axios.interceptors.request.eject(this.requestInterceptor);
}
if (this.responseInterceptor !== undefined) {
this.axios.interceptors.response.eject(this.responseInterceptor);
}
this.cache.clear();
}
{{#operations}}
{{#operation}}
/**
* {{summary}}
{{#notes}}
* {{¬es}}
{{/notes}}
*/
public async {{nickname}}({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{{dataType}}}{{#hasMore}}, {{/hasMore}}{{/allParams}}): Promise<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}> {
{{#vendorExtensions.x-cached}}
// 检查缓存
const cacheKey = `{{nickname}}-${JSON.stringify({{#allParams}}{ {{paramName}} }{{#hasMore}}, {{/hasMore}}{{/allParams}})}`;
const cached = this.getCached<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}>(cacheKey);
if (cached !== null) {
return cached;
}
{{/vendorExtensions.x-cached}}
const localVarPath = `{{{path}}}`{{#pathParams}}
.replace('{' + '{{baseName}}' + '}', encodeURIComponent(String({{paramName}}))){{/pathParams}};
const localVarUrlObj = new URL(localVarPath, this.basePath);
const localVarRequestOptions: AxiosRequestConfig = {
method: '{{httpMethod}}',
(startTime as any): Date.now()
};
const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any;
{{#hasFormParams}}
const localVarFormParams = new FormData();
{{/hasFormParams}}
{{#queryParams}}
{{#required}}
localVarQueryParameter['{{baseName}}'] = {{paramName}};
{{/required}}
{{^required}}
if ({{paramName}} !== undefined) {
localVarQueryParameter['{{baseName}}'] = {{paramName}};
}
{{/required}}
{{/queryParams}}
{{#headerParams}}
{{#required}}
localVarHeaderParameter['{{baseName}}'] = String({{paramName}});
{{/required}}
{{^required}}
if ({{paramName}} !== undefined && {{paramName}} !== null) {
localVarHeaderParameter['{{baseName}}'] = String({{paramName}});
}
{{/required}}
{{/headerParams}}
{{#hasBodyParam}}
{{#bodyParam}}
localVarHeaderParameter['Content-Type'] = 'application/json';
{{/bodyParam}}
{{/hasBodyParam}}
localVarUrlObj.search = new URLSearchParams(localVarQueryParameter).toString();
localVarRequestOptions.headers = { ...localVarHeaderParameter };
{{#hasBodyParam}}
{{#bodyParam}}
localVarRequestOptions.data = {{paramName}};
{{/bodyParam}}
{{/hasBodyParam}}
try {
const response = await this.axios.request<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}>(localVarRequestOptions);
{{#vendorExtensions.x-cached}}
// 缓存结果
this.setCached(cacheKey, response.data, {{vendorExtensions.x-cache-ttl}}{{^vendorExtensions.x-cache-ttl}}60000{{/vendorExtensions.x-cache-ttl}});
{{/vendorExtensions.x-cached}}
return response.data;
} catch (error) {
throw this.handleError(error);
}
}
{{/operation}}
{{/operations}}
}3. Python 模板
自定义服务层模板
mustache
{{! service.mustache }}
# -*- coding: utf-8 -*-
"""
{{appName}} - {{classname}} Service
{{appDescription}}
Generated by OpenAPI Generator
"""
{{#imports}}
{{import}}
{{/imports}}
import logging
from typing import List, Optional, Dict, Any
from datetime import datetime
import asyncio
from functools import lru_cache, wraps
import time
from prometheus_client import Counter, Histogram, Gauge
logger = logging.getLogger(__name__)
# Prometheus 指标
api_requests_total = Counter('api_requests_total', 'Total API requests', ['method', 'endpoint', 'status'])
api_request_duration = Histogram('api_request_duration_seconds', 'API request duration', ['method', 'endpoint'])
api_active_requests = Gauge('api_active_requests', 'Active API requests', ['method', 'endpoint'])
def monitor_performance(method: str, endpoint: str):
"""性能监控装饰器"""
def decorator(func):
@wraps(func)
async def async_wrapper(*args, **kwargs):
start_time = time.time()
api_active_requests.labels(method=method, endpoint=endpoint).inc()
try:
result = await func(*args, **kwargs)
api_requests_total.labels(method=method, endpoint=endpoint, status='success').inc()
return result
except Exception as e:
api_requests_total.labels(method=method, endpoint=endpoint, status='error').inc()
logger.error(f"Error in {method} {endpoint}: {str(e)}", exc_info=True)
raise
finally:
duration = time.time() - start_time
api_request_duration.labels(method=method, endpoint=endpoint).observe(duration)
api_active_requests.labels(method=method, endpoint=endpoint).dec()
logger.info(f"{method} {endpoint} completed in {duration:.3f}s")
@wraps(func)
def sync_wrapper(*args, **kwargs):
start_time = time.time()
api_active_requests.labels(method=method, endpoint=endpoint).inc()
try:
result = func(*args, **kwargs)
api_requests_total.labels(method=method, endpoint=endpoint, status='success').inc()
return result
except Exception as e:
api_requests_total.labels(method=method, endpoint=endpoint, status='error').inc()
logger.error(f"Error in {method} {endpoint}: {str(e)}", exc_info=True)
raise
finally:
duration = time.time() - start_time
api_request_duration.labels(method=method, endpoint=endpoint).observe(duration)
api_active_requests.labels(method=method, endpoint=endpoint).dec()
logger.info(f"{method} {endpoint} completed in {duration:.3f}s")
return async_wrapper if asyncio.iscoroutinefunction(func) else sync_wrapper
return decorator
class {{classname}}Service:
"""{{description}}"""
def __init__(self, repository=None, cache_client=None, config=None):
self.repository = repository
self.cache_client = cache_client
self.config = config or {}
self._initialize()
def _initialize(self):
"""初始化服务"""
logger.info(f"Initializing {{classname}}Service")
# 可以在这里初始化连接池、缓存等
{{#operations}}
{{#operation}}
@monitor_performance("{{httpMethod}}", "{{path}}")
{{#vendorExtensions.x-async}}async {{/vendorExtensions.x-async}}def {{operationIdSnakeCase}}(
self,
{{#allParams}}
{{paramName}}: {{^required}}Optional[{{/required}}{{dataType}}{{^required}}] = None{{/required}},
{{/allParams}}
) -> {{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}None{{/returnType}}:
"""
{{summary}}
{{#notes}}
{{notes}}
{{/notes}}
Args:
{{#allParams}}
{{paramName}}: {{description}}
{{/allParams}}
Returns:
{{#returnType}}{{returnType}}: {{/returnType}}{{returnTypeDescription}}
Raises:
ValidationError: 参数验证失败
NotFoundError: 资源不存在
ApiError: API 调用失败
"""
logger.debug(f"{{operationIdSnakeCase}} called with: {{#allParams}}{{paramName}}={{{{paramName}}}, {{/allParams}}")
# 参数验证
{{#allParams}}
{{#required}}
if {{paramName}} is None:
raise ValidationError("参数 {{paramName}} 不能为空")
{{/required}}
{{#hasValidation}}
self._validate_{{paramName}}({{paramName}})
{{/hasValidation}}
{{/allParams}}
{{#vendorExtensions.x-cached}}
# 检查缓存
cache_key = f"{{operationIdSnakeCase}}:{{#allParams}}{{#isPathParam}}{{{{paramName}}}{{/isPathParam}}{{/allParams}}"
cached_result = {{#vendorExtensions.x-async}}await {{/vendorExtensions.x-async}}self._get_from_cache(cache_key)
if cached_result is not None:
logger.debug(f"Cache hit for {cache_key}")
return cached_result
{{/vendorExtensions.x-cached}}
try:
# 业务逻辑实现
{{#vendorExtensions.x-async}}
result = await self._{{operationIdSnakeCase}}_impl({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}})
{{/vendorExtensions.x-async}}
{{^vendorExtensions.x-async}}
result = self._{{operationIdSnakeCase}}_impl({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}})
{{/vendorExtensions.x-async}}
{{#vendorExtensions.x-cached}}
# 缓存结果
{{#vendorExtensions.x-async}}await {{/vendorExtensions.x-async}}self._set_cache(cache_key, result, ttl={{vendorExtensions.x-cache-ttl}}{{^vendorExtensions.x-cache-ttl}}3600{{/vendorExtensions.x-cache-ttl}})
{{/vendorExtensions.x-cached}}
return result
except Exception as e:
logger.error(f"Error in {{operationIdSnakeCase}}: {str(e)}", exc_info=True)
raise
def _{{operationIdSnakeCase}}_impl(self, {{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}):
"""{{summary}} - 实现"""
# TODO: 实现业务逻辑
raise NotImplementedError("{{operationIdSnakeCase}} 尚未实现")
{{#allParams}}
{{#hasValidation}}
def _validate_{{paramName}}(self, value: {{dataType}}) -> None:
"""验证 {{paramName}}"""
{{#minLength}}
if len(value) < {{minLength}}:
raise ValidationError("{{paramName}} 长度不能小于 {{minLength}}")
{{/minLength}}
{{#maxLength}}
if len(value) > {{maxLength}}:
raise ValidationError("{{paramName}} 长度不能大于 {{maxLength}}")
{{/maxLength}}
{{#minimum}}
if value < {{minimum}}:
raise ValidationError("{{paramName}} 不能小于 {{minimum}}")
{{/minimum}}
{{#maximum}}
if value > {{maximum}}:
raise ValidationError("{{paramName}} 不能大于 {{maximum}}")
{{/maximum}}
{{#pattern}}
import re
if not re.match(r"{{pattern}}", value):
raise ValidationError("{{paramName}} 格式不正确")
{{/pattern}}
{{/hasValidation}}
{{/allParams}}
{{/operation}}
{{/operations}}
# 缓存辅助方法
{{#vendorExtensions.x-async}}async {{/vendorExtensions.x-async}}def _get_from_cache(self, key: str) -> Optional[Any]:
"""从缓存获取数据"""
if not self.cache_client:
return None
try:
return {{#vendorExtensions.x-async}}await {{/vendorExtensions.x-async}}self.cache_client.get(key)
except Exception as e:
logger.warning(f"Cache get error: {e}")
return None
{{#vendorExtensions.x-async}}async {{/vendorExtensions.x-async}}def _set_cache(self, key: str, value: Any, ttl: int = 3600) -> None:
"""设置缓存"""
if not self.cache_client:
return
try:
{{#vendorExtensions.x-async}}await {{/vendorExtensions.x-async}}self.cache_client.set(key, value, ttl=ttl)
except Exception as e:
logger.warning(f"Cache set error: {e}")使用自定义模板
1. 命令行使用
bash
# 使用自定义模板目录
openapi-generator-cli generate \
-i api/openapi.yaml \
-g spring \
-o generated \
-t ./custom-templates
# 指定特定模板文件
openapi-generator-cli generate \
-i api/openapi.yaml \
-g spring \
-o generated \
--template-dir ./custom-templates \
--import-mappings DateTime=java.time.OffsetDateTime2. 配置文件使用
yaml
# generator-config.yaml
generatorName: spring
outputDir: ./generated
inputSpec: ./api/openapi.yaml
templateDir: ./custom-templates
# 模板变量
additionalProperties:
appName: "用户管理系统"
appDescription: "提供用户管理相关功能"
appVersion: "1.0.0"
# 自定义扩展属性
enableMetrics: true
enableCaching: true
enableAsync: true
# 自定义文件生成
files:
# 额外生成的文件
- templateFile: "README.mustache"
outputFile: "README.md"
- templateFile: "Dockerfile.mustache"
outputFile: "Dockerfile"
- templateFile: "docker-compose.mustache"
outputFile: "docker-compose.yml"3. 编程方式使用
java
// CustomGenerator.java
import org.openapitools.codegen.*;
import org.openapitools.codegen.languages.SpringCodegen;
public class CustomSpringCodegen extends SpringCodegen {
@Override
public void processOpts() {
super.processOpts();
// 添加自定义模板
supportingFiles.add(new SupportingFile(
"service.mustache",
sourceFolder + "/" + apiPackage().replace(".", "/"),
"Service.java"
));
// 添加自定义属性
additionalProperties.put("enableMetrics", true);
additionalProperties.put("currentYear", Year.now().getValue());
}
@Override
public String toApiName(String name) {
// 自定义 API 命名规则
return super.toApiName(name) + "Custom";
}
}模板开发技巧
1. 调试模板
bash
# 生成调试信息
openapi-generator-cli generate \
-i api/openapi.yaml \
-g spring \
-o generated \
-t ./custom-templates \
--global-property debugModels,debugOperations
# 查看模板变量
openapi-generator-cli config-help -g spring2. 条件渲染
mustache
{{! 条件渲染示例 }}
{{#required}}
// 必填字段
@NotNull(message = "{{paramName}} 不能为空")
{{/required}}
{{^required}}
// 可选字段
@Nullable
{{/required}}
{{#hasValidation}}
// 包含验证规则
{{#minLength}}@Size(min = {{minLength}}){{/minLength}}
{{#pattern}}@Pattern(regexp = "{{pattern}}"){{/pattern}}
{{/hasValidation}}3. 循环和嵌套
mustache
{{! 循环示例 }}
{{#operations}}
public interface {{classname}}Service {
{{#operation}}
{{returnType}} {{operationId}}({{#allParams}}{{dataType}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}});
{{/operation}}
}
{{/operations}}
{{! 嵌套对象 }}
{{#models}}
{{#model}}
public class {{classname}} {
{{#vars}}
private {{dataType}} {{name}};
{{/vars}}
}
{{/model}}
{{/models}}4. 自定义辅助函数
mustache
{{! 使用 Lambda 表达式 }}
{{#lambda.uppercase}}{{operationId}}{{/lambda.uppercase}}
{{! 使用自定义标签 }}
{{>customHeader}}
{{! 包含其他模板 }}
{{>model/pojo}}模板最佳实践
1. 保持模板简洁
mustache
{{! 好的做法:使用部分模板 }}
{{>imports}}
{{>classAnnotations}}
public class {{classname}} {
{{>fields}}
{{>methods}}
}
{{! 避免:在一个模板中包含太多逻辑 }}2. 复用通用部分
mustache
{{! partials/header.mustache }}
/*
* {{appName}}
* {{appDescription}}
*
* API Version: {{appVersion}}
* Generated: {{generatedDate}}
*/3. 版本控制
custom-templates/
├── v1/ # 版本 1 模板
├── v2/ # 版本 2 模板
└── current/ # 当前使用的模板(符号链接)总结
自定义模板让我们能够:
- 🎯 统一代码风格: 符合团队规范
- 🚀 增强功能: 添加监控、缓存、日志等
- 📦 集成框架: 无缝集成企业框架
- 🔧 提高效率: 减少重复代码编写
通过合理使用自定义模板,可以让生成的代码更好地适应项目需求。