Skip to content

服务端代码生成

基于 OpenAPI 规范自动生成服务端框架代码

概述

服务端代码生成帮助快速搭建符合 API 规范的后端服务框架。生成的代码包括:

  • 🎯 控制器接口: 定义 API 端点
  • 📦 数据模型: 请求/响应对象
  • 验证规则: 参数校验
  • 🔐 安全配置: 认证授权框架
  • 📝 API 文档: 自动集成

支持的服务端框架

Java 生态

生成器框架特性
springSpring Boot注解驱动, 依赖注入
java-vertxVert.x响应式, 高性能
jaxrs-jerseyJerseyJAX-RS 标准
java-micronautMicronaut云原生, GraalVM

Python 生态

生成器框架特性
python-fastapiFastAPI异步, 自动文档
python-flaskFlask轻量, 灵活
python-djangoDjango全功能, ORM
python-aiohttpaiohttp异步 HTTP

Node.js 生态

生成器框架特性
nodejs-express-serverExpress成熟, 中间件丰富
nodejs-koa-serverKoa现代, async/await
nodejs-fastifyFastify高性能, Schema验证

其他语言

生成器语言/框架特性
go-gin-serverGo/Gin高性能, 简洁
rust-axumRust/Axum类型安全, 高性能
aspnetcoreC#/.NET跨平台, 企业级
ruby-on-railsRuby/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 user
python
# 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 文档

让开发者专注于业务逻辑实现,而不是重复的框架代码编写。

SOLO Development Guide