服务端代码生成
基于 OpenAPI 规范自动生成服务端框架代码
概述
服务端代码生成帮助快速搭建符合 API 规范的后端服务框架。生成的代码包括:
- 🎯 控制器接口: 定义 API 端点
- 📦 数据模型: 请求/响应对象
- ✅ 验证规则: 参数校验
- 🔐 安全配置: 认证授权框架
- 📝 API 文档: 自动集成
支持的服务端框架
Java 生态
| 生成器 | 框架 | 特性 |
|---|---|---|
spring | Spring Boot | 注解驱动, 依赖注入 |
java-vertx | Vert.x | 响应式, 高性能 |
jaxrs-jersey | Jersey | JAX-RS 标准 |
java-micronaut | Micronaut | 云原生, GraalVM |
Python 生态
| 生成器 | 框架 | 特性 |
|---|---|---|
python-fastapi | FastAPI | 异步, 自动文档 |
python-flask | Flask | 轻量, 灵活 |
python-django | Django | 全功能, ORM |
python-aiohttp | aiohttp | 异步 HTTP |
Node.js 生态
| 生成器 | 框架 | 特性 |
|---|---|---|
nodejs-express-server | Express | 成熟, 中间件丰富 |
nodejs-koa-server | Koa | 现代, async/await |
nodejs-fastify | Fastify | 高性能, Schema验证 |
其他语言
| 生成器 | 语言/框架 | 特性 |
|---|---|---|
go-gin-server | Go/Gin | 高性能, 简洁 |
rust-axum | Rust/Axum | 类型安全, 高性能 |
aspnetcore | C#/.NET | 跨平台, 企业级 |
ruby-on-rails | Ruby/Rails | 约定优于配置 |
Spring Boot 服务端生成
基础生成
bash
openapi-generator-cli generate \
-i api/openapi.yaml \
-g spring \
-o generated/spring-server \
--additional-properties=\
basePackage=com.company.api,\
configPackage=com.company.api.config,\
modelPackage=com.company.api.model,\
apiPackage=com.company.api.controller,\
useTags=true,\
interfaceOnly=true配置文件
yaml
# spring-server-config.yaml
generatorName: spring
outputDir: ./generated/spring-server
inputSpec: ./api/openapi.yaml
additionalProperties:
# 包结构
basePackage: com.company.api
configPackage: com.company.api.config
modelPackage: com.company.api.model
apiPackage: com.company.api.controller
invokerPackage: com.company.api
# 项目配置
groupId: com.company
artifactId: user-api-server
artifactVersion: 1.0.0
title: User API Server
# 功能选项
useTags: true # 使用标签分组
interfaceOnly: false # 生成完整实现
delegatePattern: true # 使用委托模式
useBeanValidation: true # Bean Validation
performBeanValidation: true # 运行时验证
java8: true # Java 8+ 特性
dateLibrary: java8 # 使用 Java 8 时间 API
reactive: false # WebFlux 响应式
# Spring 特性
useSpringBoot3: true # Spring Boot 3
documentationProvider: springdoc # OpenAPI 文档
# 数据库
generateSupportingFiles: true
supportingFilesToGenerate: "ApiUtil.java,pom.xml,application.yml"生成的代码结构
generated/spring-server/
├── pom.xml
├── src/main/java/com/company/api/
│ ├── ApiApplication.java # 主应用类
│ ├── controller/
│ │ ├── UserApi.java # 接口定义
│ │ └── UserApiController.java # 控制器实现
│ ├── model/
│ │ ├── User.java # 用户模型
│ │ ├── CreateUserRequest.java # 请求模型
│ │ └── ErrorResponse.java # 错误响应
│ ├── config/
│ │ ├── OpenApiConfig.java # OpenAPI 配置
│ │ └── SecurityConfig.java # 安全配置
│ └── service/
│ └── UserService.java # 服务接口
└── src/main/resources/
└── application.yml # 应用配置生成的接口代码
java
// UserApi.java - 生成的接口
package com.company.api.controller;
import com.company.api.model.*;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import javax.validation.constraints.*;
import java.util.List;
@Tag(name = "users", description = "用户管理接口")
@Validated
public interface UserApi {
@Operation(
summary = "获取用户列表",
description = "获取系统中所有用户的分页列表",
responses = {
@ApiResponse(responseCode = "200", description = "成功获取用户列表"),
@ApiResponse(responseCode = "401", description = "未授权"),
@ApiResponse(responseCode = "500", description = "服务器错误")
}
)
@GetMapping("/users")
ResponseEntity<List<User>> listUsers(
@Parameter(description = "页码")
@RequestParam(value = "page", required = false, defaultValue = "1")
@Min(1) Integer page,
@Parameter(description = "每页大小")
@RequestParam(value = "size", required = false, defaultValue = "20")
@Min(1) @Max(100) Integer size
);
@Operation(
summary = "创建用户",
description = "创建一个新用户",
responses = {
@ApiResponse(responseCode = "201", description = "用户创建成功"),
@ApiResponse(responseCode = "400", description = "请求参数错误"),
@ApiResponse(responseCode = "409", description = "用户已存在")
}
)
@PostMapping("/users")
ResponseEntity<User> createUser(
@Parameter(description = "用户创建请求", required = true)
@Valid @RequestBody CreateUserRequest createUserRequest
);
@Operation(
summary = "获取用户详情",
description = "根据用户ID获取用户详细信息",
responses = {
@ApiResponse(responseCode = "200", description = "成功获取用户信息"),
@ApiResponse(responseCode = "404", description = "用户不存在")
}
)
@GetMapping("/users/{userId}")
ResponseEntity<User> getUserById(
@Parameter(description = "用户ID", required = true)
@PathVariable("userId") String userId
);
}实现控制器
java
// UserApiController.java - 控制器实现
package com.company.api.controller;
import com.company.api.model.*;
import com.company.api.service.UserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@Slf4j
@RestController
@RequiredArgsConstructor
public class UserApiController implements UserApi {
private final UserService userService;
@Override
public ResponseEntity<List<User>> listUsers(Integer page, Integer size) {
log.info("获取用户列表: page={}, size={}", page, size);
try {
List<User> users = userService.findUsers(page, size);
return ResponseEntity.ok(users);
} catch (Exception e) {
log.error("获取用户列表失败", e);
throw new ApiException("获取用户列表失败", e);
}
}
@Override
public ResponseEntity<User> createUser(CreateUserRequest request) {
log.info("创建用户: {}", request.getEmail());
try {
// 验证邮箱是否已存在
if (userService.existsByEmail(request.getEmail())) {
throw new ConflictException("邮箱已被注册");
}
User user = userService.createUser(request);
return ResponseEntity
.status(HttpStatus.CREATED)
.header("Location", "/users/" + user.getId())
.body(user);
} catch (ConflictException e) {
throw e;
} catch (Exception e) {
log.error("创建用户失败", e);
throw new ApiException("创建用户失败", e);
}
}
@Override
public ResponseEntity<User> getUserById(String userId) {
log.info("获取用户详情: userId={}", userId);
return userService.findById(userId)
.map(ResponseEntity::ok)
.orElseThrow(() -> new NotFoundException("用户不存在"));
}
}数据模型
java
// User.java - 生成的模型类
package com.company.api.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import lombok.Builder;
import javax.validation.constraints.*;
import java.time.OffsetDateTime;
import java.util.List;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "用户信息")
public class User {
@JsonProperty("id")
@Schema(description = "用户唯一标识", example = "123e4567-e89b-12d3-a456-426614174000")
private String id;
@JsonProperty("name")
@NotNull
@Size(min = 2, max = 50)
@Schema(description = "用户姓名", required = true, example = "张三")
private String name;
@JsonProperty("email")
@NotNull
@Email
@Schema(description = "电子邮箱", required = true, example = "zhangsan@example.com")
private String email;
@JsonProperty("phone")
@Pattern(regexp = "^1[3-9]\\d{9}$")
@Schema(description = "手机号码", example = "13800138000")
private String phone;
@JsonProperty("status")
@Schema(description = "用户状态", example = "ACTIVE")
private StatusEnum status = StatusEnum.ACTIVE;
@JsonProperty("roles")
@Schema(description = "用户角色")
private List<String> roles;
@JsonProperty("createdAt")
@Schema(description = "创建时间", example = "2024-01-01T00:00:00Z")
private OffsetDateTime createdAt;
public enum StatusEnum {
ACTIVE("active"),
INACTIVE("inactive"),
SUSPENDED("suspended");
private String value;
StatusEnum(String value) {
this.value = value;
}
@JsonValue
public String getValue() {
return value;
}
}
}FastAPI 服务端生成
生成配置
bash
openapi-generator-cli generate \
-i api/openapi.yaml \
-g python-fastapi \
-o generated/fastapi-server \
--additional-properties=packageName=company_api,serverPort=8000生成的代码
python
# main.py - 生成的主应用
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from company_api.apis.users_api import router as users_router
from company_api.models import *
app = FastAPI(
title="User API",
version="1.0.0",
description="用户管理 API"
)
# CORS 配置
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# 注册路由
app.include_router(users_router, prefix="/api/v1", tags=["users"])
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)python
# apis/users_api.py - 生成的路由
from typing import List, Optional
from fastapi import APIRouter, HTTPException, Query, Path, Body
from fastapi.responses import JSONResponse
from ..models import User, CreateUserRequest, ErrorResponse
from ..services.user_service import UserService
router = APIRouter()
user_service = UserService()
@router.get(
"/users",
response_model=List[User],
summary="获取用户列表",
description="获取系统中所有用户的分页列表"
)
async def list_users(
page: Optional[int] = Query(1, ge=1, description="页码"),
size: Optional[int] = Query(20, ge=1, le=100, description="每页大小")
) -> List[User]:
"""获取用户列表"""
try:
users = await user_service.get_users(page=page, size=size)
return users
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@router.post(
"/users",
response_model=User,
status_code=201,
summary="创建用户",
description="创建一个新用户"
)
async def create_user(
create_user_request: CreateUserRequest = Body(..., description="用户创建请求")
) -> User:
"""创建新用户"""
try:
# 检查邮箱是否已存在
if await user_service.email_exists(create_user_request.email):
raise HTTPException(status_code=409, detail="邮箱已被注册")
user = await user_service.create_user(create_user_request)
return user
except HTTPException:
raise
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@router.get(
"/users/{user_id}",
response_model=User,
summary="获取用户详情",
description="根据用户ID获取用户详细信息"
)
async def get_user_by_id(
user_id: str = Path(..., description="用户ID")
) -> User:
"""获取用户详情"""
user = await user_service.get_user_by_id(user_id)
if not user:
raise HTTPException(status_code=404, detail="用户不存在")
return userpython
# models/user.py - 生成的数据模型
from typing import Optional, List
from datetime import datetime
from enum import Enum
from pydantic import BaseModel, Field, EmailStr, constr
class StatusEnum(str, Enum):
active = "active"
inactive = "inactive"
suspended = "suspended"
class User(BaseModel):
id: str = Field(..., description="用户唯一标识", example="123e4567-e89b-12d3-a456-426614174000")
name: constr(min_length=2, max_length=50) = Field(..., description="用户姓名", example="张三")
email: EmailStr = Field(..., description="电子邮箱", example="zhangsan@example.com")
phone: Optional[constr(regex=r'^1[3-9]\d{9}$')] = Field(None, description="手机号码", example="13800138000")
status: StatusEnum = Field(StatusEnum.active, description="用户状态")
roles: List[str] = Field(default_factory=list, description="用户角色")
created_at: datetime = Field(..., description="创建时间", example="2024-01-01T00:00:00Z")
class Config:
orm_mode = True
class CreateUserRequest(BaseModel):
name: constr(min_length=2, max_length=50) = Field(..., description="用户姓名", example="张三")
email: EmailStr = Field(..., description="电子邮箱", example="zhangsan@example.com")
password: constr(min_length=8, max_length=128) = Field(..., description="密码")
phone: Optional[constr(regex=r'^1[3-9]\d{9}$')] = Field(None, description="手机号码", example="13800138000")Express.js 服务端生成
生成配置
bash
openapi-generator-cli generate \
-i api/openapi.yaml \
-g nodejs-express-server \
-o generated/express-server生成的代码
javascript
// index.js - 主应用
const express = require('express');
const cors = require('cors');
const bodyParser = require('body-parser');
const { OpenApiValidator } = require('express-openapi-validator');
const usersRouter = require('./routes/users');
const { errorHandler } = require('./middleware/errorHandler');
const app = express();
const port = process.env.PORT || 3000;
// 中间件
app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
// OpenAPI 验证
app.use(
OpenApiValidator.middleware({
apiSpec: './api/openapi.yaml',
validateRequests: true,
validateResponses: true,
})
);
// 路由
app.use('/api/v1', usersRouter);
// 错误处理
app.use(errorHandler);
// 启动服务器
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});javascript
// routes/users.js - 路由实现
const express = require('express');
const router = express.Router();
const userService = require('../services/userService');
/**
* GET /users
* 获取用户列表
*/
router.get('/users', async (req, res, next) => {
try {
const { page = 1, size = 20 } = req.query;
const users = await userService.getUsers({ page, size });
res.json(users);
} catch (error) {
next(error);
}
});
/**
* POST /users
* 创建用户
*/
router.post('/users', async (req, res, next) => {
try {
const user = await userService.createUser(req.body);
res.status(201)
.location(`/users/${user.id}`)
.json(user);
} catch (error) {
if (error.code === 'EMAIL_EXISTS') {
res.status(409).json({ error: '邮箱已被注册' });
} else {
next(error);
}
}
});
/**
* GET /users/:userId
* 获取用户详情
*/
router.get('/users/:userId', async (req, res, next) => {
try {
const user = await userService.getUserById(req.params.userId);
if (!user) {
res.status(404).json({ error: '用户不存在' });
} else {
res.json(user);
}
} catch (error) {
next(error);
}
});
module.exports = router;服务端测试
单元测试生成
配置测试生成:
yaml
# 添加测试生成选项
additionalProperties:
generateTests: true
testFramework: junit5 # 或 pytest, jest生成的测试代码:
java
// UserApiControllerTest.java
@SpringBootTest
@AutoConfigureMockMvc
class UserApiControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Test
@DisplayName("应该成功获取用户列表")
void shouldGetUserList() throws Exception {
// Given
List<User> users = Arrays.asList(
User.builder().id("1").name("张三").email("zhang@example.com").build(),
User.builder().id("2").name("李四").email("li@example.com").build()
);
when(userService.findUsers(1, 20)).thenReturn(users);
// When & Then
mockMvc.perform(get("/users")
.param("page", "1")
.param("size", "20"))
.andExpect(status().isOk())
.andExpect(jsonPath("$", hasSize(2)))
.andExpect(jsonPath("$[0].name").value("张三"))
.andExpect(jsonPath("$[1].name").value("李四"));
}
@Test
@DisplayName("创建用户时邮箱已存在应返回409")
void shouldReturn409WhenEmailExists() throws Exception {
// Given
when(userService.existsByEmail("existing@example.com")).thenReturn(true);
CreateUserRequest request = CreateUserRequest.builder()
.name("测试用户")
.email("existing@example.com")
.password("password123")
.build();
// When & Then
mockMvc.perform(post("/users")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request)))
.andExpect(status().isConflict())
.andExpect(jsonPath("$.code").value("EMAIL_EXISTS"));
}
}集成测试
python
# test_integration.py
import pytest
from fastapi.testclient import TestClient
from main import app
client = TestClient(app)
def test_user_lifecycle():
# 创建用户
response = client.post("/api/v1/users", json={
"name": "测试用户",
"email": "test@example.com",
"password": "password123"
})
assert response.status_code == 201
user_id = response.json()["id"]
# 获取用户
response = client.get(f"/api/v1/users/{user_id}")
assert response.status_code == 200
assert response.json()["name"] == "测试用户"
# 获取用户列表
response = client.get("/api/v1/users")
assert response.status_code == 200
assert any(u["id"] == user_id for u in response.json())自定义代码生成
自定义模板
创建自定义 Mustache 模板:
mustache
{{! custom-controller.mustache }}
package {{package}};
{{#imports}}import {{import}};
{{/imports}}
@RestController
@RequestMapping("{{basePath}}")
@Slf4j
@RequiredArgsConstructor
public class {{classname}} implements {{classname}}Api {
private final {{classname}}Service service;
{{#operations}}
{{#operation}}
@Override
public ResponseEntity<{{>returnTypes}}> {{operationId}}({{#allParams}}{{>queryParams}}{{>pathParams}}{{>bodyParams}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) {
log.info("API调用: {{operationId}}");
try {
{{#returnType}}{{returnType}} result = {{/returnType}}service.{{operationId}}({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}});
{{#returnType}}return ResponseEntity.ok(result);{{/returnType}}
{{^returnType}}return ResponseEntity.ok().build();{{/returnType}}
} catch (NotFoundException e) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, e.getMessage());
} catch (ConflictException e) {
throw new ResponseStatusException(HttpStatus.CONFLICT, e.getMessage());
} catch (Exception e) {
log.error("处理请求失败", e);
throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "内部服务器错误");
}
}
{{/operation}}
{{/operations}}
}代码后处理
javascript
// post-process.js
const fs = require('fs');
const path = require('path');
// 添加自定义注释
function addCustomHeaders(dir) {
const files = fs.readdirSync(dir);
files.forEach(file => {
if (file.endsWith('.java')) {
const filePath = path.join(dir, file);
let content = fs.readFileSync(filePath, 'utf8');
// 添加版权信息
content = `/*
* Copyright (c) 2024 Company Name
* Generated by OpenAPI Generator
*/
${content}`;
fs.writeFileSync(filePath, content);
}
});
}
// 执行后处理
addCustomHeaders('./generated/src/main/java');部署和运行
Docker 化
生成的 Dockerfile:
dockerfile
# Spring Boot 应用
FROM openjdk:17-jdk-slim AS build
WORKDIR /app
COPY pom.xml .
COPY src ./src
RUN ./mvnw clean package -DskipTests
FROM openjdk:17-jre-slim
WORKDIR /app
COPY --from=build /app/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]Kubernetes 部署
yaml
# k8s-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-api
spec:
replicas: 3
selector:
matchLabels:
app: user-api
template:
metadata:
labels:
app: user-api
spec:
containers:
- name: user-api
image: company/user-api:latest
ports:
- containerPort: 8080
env:
- name: SPRING_PROFILES_ACTIVE
value: "production"
livenessProbe:
httpGet:
path: /actuator/health
port: 8080
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080最佳实践
1. 分层架构
保持生成的代码和业务逻辑分离:
src/
├── generated/ # 生成的代码(不要修改)
│ ├── api/
│ └── model/
├── main/
│ ├── controller/ # 控制器实现
│ ├── service/ # 业务逻辑
│ ├── repository/ # 数据访问
│ └── domain/ # 领域模型2. 接口与实现分离
java
// 生成的接口
public interface UserApi {
ResponseEntity<User> getUser(String id);
}
// 自定义实现
@RestController
public class UserController implements UserApi {
// 实现业务逻辑
}3. 错误处理
python
# 全局错误处理器
@app.exception_handler(ValidationError)
async def validation_exception_handler(request, exc):
return JSONResponse(
status_code=422,
content={
"code": "VALIDATION_ERROR",
"message": "请求参数验证失败",
"details": exc.errors()
}
)总结
服务端代码生成通过:
- 🚀 快速搭建: 自动生成框架代码
- ✅ 规范一致: 确保实现符合 API 规范
- 🔒 类型安全: 编译时类型检查
- 📝 文档集成: 自动生成 API 文档
让开发者专注于业务逻辑实现,而不是重复的框架代码编写。