openapi_docs
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSpring Boot OpenAPI Documentation with SpringDoc
基于SpringDoc的Spring Boot OpenAPI文档方案
Implement comprehensive REST API documentation using SpringDoc OpenAPI 3.0 and Swagger UI in Spring Boot 3.x applications.
在Spring Boot 3.x应用中使用SpringDoc OpenAPI 3.0和Swagger UI实现完善的REST API文档。
When to Use
适用场景
Use this skill when you need to:
- Set up SpringDoc OpenAPI in Spring Boot 3.x projects
- Generate OpenAPI 3.0 specifications for REST APIs
- Configure and customize Swagger UI
- Add detailed API documentation with annotations
- Document request/response models with validation
- Implement API security documentation (JWT, OAuth2, Basic Auth)
- Document pageable and sortable endpoints
- Add examples and schemas to API endpoints
- Customize OpenAPI definitions programmatically
- Generate API documentation for WebMvc or WebFlux applications
- Support multiple API groups and versions
- Document error responses and exception handlers
- Add JSR-303 Bean Validation to API documentation
- Support Kotlin-based Spring Boot APIs
当你需要完成以下操作时可使用该方案:
- 在Spring Boot 3.x项目中搭建SpringDoc OpenAPI
- 为REST API生成OpenAPI 3.0规范文档
- 配置和自定义Swagger UI
- 通过注解添加详细的API文档说明
- 为请求/响应模型添加校验规则文档
- 实现API安全相关文档(JWT、OAuth2、基础认证)
- 为支持分页、排序的端点编写文档
- 为API端点添加示例和Schema定义
- 通过编程方式自定义OpenAPI定义
- 为WebMvc或WebFlux应用生成API文档
- 支持多API分组和多版本管理
- 为错误响应和异常处理器编写文档
- 在API文档中集成JSR-303 Bean Validation规则
- 支持基于Kotlin开发的Spring Boot API
Setup Dependencies
依赖安装
Add Maven Dependencies
添加Maven依赖
xml
<!-- Standard WebMVC support -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.8.13</version> // Use latest stable version
</dependency>
<!-- Optional: therapi-runtime-javadoc for JavaDoc support -->
<dependency>
<groupId>com.github.therapi</groupId>
<artifactId>therapi-runtime-javadoc</artifactId>
<version>0.15.0</version> // Use latest stable version
<scope>provided</scope>
</dependency>
<!-- WebFlux support -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webflux-ui</artifactId>
<version>2.8.13</version> // Use latest stable version
</dependency>xml
<!-- 标准WebMVC支持 -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.8.13</version> // Use latest stable version
</dependency>
<!-- 可选:therapi-runtime-javadoc 用于支持JavaDoc导入 -->
<dependency>
<groupId>com.github.therapi</groupId>
<artifactId>therapi-runtime-javadoc</artifactId>
<version>0.15.0</version> // Use latest stable version
<scope>provided</scope>
</dependency>
<!-- WebFlux支持 -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webflux-ui</artifactId>
<version>2.8.13</version> // Use latest stable version
</dependency>Add Gradle Dependencies
添加Gradle依赖
gradle
// Standard WebMVC support
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.13'
// Optional: therapi-runtime-javadoc for JavaDoc support
implementation 'com.github.therapi:therapi-runtime-javadoc:0.15.0'
// WebFlux support
implementation 'org.springdoc:springdoc-openapi-starter-webflux-ui:2.8.13'gradle
// 标准WebMVC支持
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.13'
// 可选:therapi-runtime-javadoc 用于支持JavaDoc导入
implementation 'com.github.therapi:therapi-runtime-javadoc:0.15.0'
// WebFlux支持
implementation 'org.springdoc:springdoc-openapi-starter-webflux-ui:2.8.13'Configure SpringDoc
SpringDoc配置
Basic Configuration
基础配置
properties
undefinedproperties
undefinedapplication.properties
application.properties
springdoc.api-docs.path=/api-docs
springdoc.swagger-ui.path=/swagger-ui-custom.html
springdoc.swagger-ui.operationsSorter=method
springdoc.swagger-ui.tagsSorter=alpha
springdoc.swagger-ui.enabled=true
springdoc.api-docs.enabled=true
springdoc.packages-to-scan=com.example.controller
springdoc.paths-to-match=/api/**
```yamlspringdoc.api-docs.path=/api-docs
springdoc.swagger-ui.path=/swagger-ui-custom.html
springdoc.swagger-ui.operationsSorter=method
springdoc.swagger-ui.tagsSorter=alpha
springdoc.swagger-ui.enabled=true
springdoc.api-docs.enabled=true
springdoc.packages-to-scan=com.example.controller
springdoc.paths-to-match=/api/**
```yamlapplication.yml
application.yml
springdoc:
api-docs:
path: /api-docs
enabled: true
swagger-ui:
path: /swagger-ui.html
enabled: true
operationsSorter: method
tagsSorter: alpha
tryItOutEnabled: true
packages-to-scan: com.example.controller
paths-to-match: /api/**
undefinedspringdoc:
api-docs:
path: /api-docs
enabled: true
swagger-ui:
path: /swagger-ui.html
enabled: true
operationsSorter: method
tagsSorter: alpha
tryItOutEnabled: true
packages-to-scan: com.example.controller
paths-to-match: /api/**
undefinedAccess Endpoints
访问端点
After configuration:
- OpenAPI JSON:
http://localhost:8080/v3/api-docs - OpenAPI YAML:
http://localhost:8080/v3/api-docs.yaml - Swagger UI:
http://localhost:8080/swagger-ui/index.html
配置完成后可以访问以下地址:
- OpenAPI JSON文档:
http://localhost:8080/v3/api-docs - OpenAPI YAML文档:
http://localhost:8080/v3/api-docs.yaml - Swagger UI界面:
http://localhost:8080/swagger-ui/index.html
Document Controllers
控制器文档编写
Basic Controller Documentation
基础控制器文档
java
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.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/books")
@Tag(name = "Book", description = "Book management APIs")
public class BookController {
@Operation(
summary = "Retrieve a book by ID",
description = "Get a Book object by specifying its ID. The response includes id, title, author and description."
)
@ApiResponses(value = {
@ApiResponse(
responseCode = "200",
description = "Successfully retrieved book",
content = @Content(schema = @Schema(implementation = Book.class))
),
@ApiResponse(
responseCode = "404",
description = "Book not found"
)
})
@GetMapping("/{id}")
public Book findById(
@Parameter(description = "ID of book to retrieve", required = true)
@PathVariable Long id
) {
return repository.findById(id)
.orElseThrow(() -> new BookNotFoundException());
}
}java
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.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/books")
@Tag(name = "Book", description = "Book management APIs")
public class BookController {
@Operation(
summary = "Retrieve a book by ID",
description = "Get a Book object by specifying its ID. The response includes id, title, author and description."
)
@ApiResponses(value = {
@ApiResponse(
responseCode = "200",
description = "Successfully retrieved book",
content = @Content(schema = @Schema(implementation = Book.class))
),
@ApiResponse(
responseCode = "404",
description = "Book not found"
)
})
@GetMapping("/{id}")
public Book findById(
@Parameter(description = "ID of book to retrieve", required = true)
@PathVariable Long id
) {
return repository.findById(id)
.orElseThrow(() -> new BookNotFoundException());
}
}Document Request Bodies
请求体文档编写
java
import io.swagger.v3.oas.annotations.parameters.RequestBody;
import io.swagger.v3.oas.annotations.media.ExampleObject;
@Operation(summary = "Create a new book")
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Book createBook(
@RequestBody(
description = "Book to create",
required = true,
content = @Content(
schema = @Schema(implementation = Book.class),
examples = @ExampleObject(
value = """
{
"title": "Clean Code",
"author": "Robert C. Martin",
"isbn": "978-0132350884",
"description": "A handbook of agile software craftsmanship"
}
"""
)
)
)
Book book
) {
return repository.save(book);
}java
import io.swagger.v3.oas.annotations.parameters.RequestBody;
import io.swagger.v3.oas.annotations.media.ExampleObject;
@Operation(summary = "Create a new book")
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Book createBook(
@RequestBody(
description = "Book to create",
required = true,
content = @Content(
schema = @Schema(implementation = Book.class),
examples = @ExampleObject(
value = """
{
"title": "Clean Code",
"author": "Robert C. Martin",
"isbn": "978-0132350884",
"description": "A handbook of agile software craftsmanship"
}
"""
)
)
)
Book book
) {
return repository.save(book);
}Document Models
模型文档编写
Entity with Validation
带校验规则的实体类
java
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.*;
@Entity
@Schema(description = "Book entity representing a published book")
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Schema(description = "Unique identifier", example = "1", accessMode = Schema.AccessMode.READ_ONLY)
private Long id;
@NotBlank(message = "Title is required")
@Size(min = 1, max = 200)
@Schema(description = "Book title", example = "Clean Code", required = true, maxLength = 200)
private String title;
@Pattern(regexp = "^(?:ISBN(?:-1[03])?:? )?(?=[0-9X]{10}$|(?=(?:[0-9]+[- ]){3})[- 0-9X]{13}$|97[89][0-9]{10}$|(?=(?:[0-9]+[- ]){4})[- 0-9]{17}$)(?:97[89][- ]?)?[0-9]{1,5}[- ]?[0-9]+[- ]?[0-9]+[- ]?[0-9X]$")
@Schema(description = "ISBN number", example = "978-0132350884")
private String isbn;
// Additional fields, constructors, getters, setters
}java
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.*;
@Entity
@Schema(description = "Book entity representing a published book")
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Schema(description = "Unique identifier", example = "1", accessMode = Schema.AccessMode.READ_ONLY)
private Long id;
@NotBlank(message = "Title is required")
@Size(min = 1, max = 200)
@Schema(description = "Book title", example = "Clean Code", required = true, maxLength = 200)
private String title;
@Pattern(regexp = "^(?:ISBN(?:-1[03])?:? )?(?=[0-9X]{10}$|(?=(?:[0-9]+[- ]){3})[- 0-9X]{13}$|97[89][0-9]{10}$|(?=(?:[0-9]+[- ]){4})[- 0-9]{17}$)(?:97[89][- ]?)?[0-9]{1,5}[- ]?[0-9]+[- ]?[0-9]+[- ]?[0-9X]$")
@Schema(description = "ISBN number", example = "978-0132350884")
private String isbn;
// Additional fields, constructors, getters, setters
}Hidden Fields
隐藏字段
java
@Schema(hidden = true)
private String internalField;
@JsonIgnore
@Schema(accessMode = Schema.AccessMode.READ_ONLY)
private LocalDateTime createdAt;java
@Schema(hidden = true)
private String internalField;
@JsonIgnore
@Schema(accessMode = Schema.AccessMode.READ_ONLY)
private LocalDateTime createdAt;Document Security
安全相关文档
JWT Bearer Authentication
JWT Bearer认证配置
java
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.security.SecurityScheme;
@Configuration
public class OpenAPISecurityConfig {
@Bean
public OpenAPI customOpenAPI() {
return new OpenAPI()
.components(new Components()
.addSecuritySchemes("bearer-jwt", new SecurityScheme()
.type(SecurityScheme.Type.HTTP)
.scheme("bearer")
.bearerFormat("JWT")
.description("JWT authentication")
)
);
}
}
// Apply security requirement
@RestController
@RequestMapping("/api/books")
@SecurityRequirement(name = "bearer-jwt")
public class BookController {
// All endpoints require JWT authentication
}java
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.security.SecurityScheme;
@Configuration
public class OpenAPISecurityConfig {
@Bean
public OpenAPI customOpenAPI() {
return new OpenAPI()
.components(new Components()
.addSecuritySchemes("bearer-jwt", new SecurityScheme()
.type(SecurityScheme.Type.HTTP)
.scheme("bearer")
.bearerFormat("JWT")
.description("JWT authentication")
)
);
}
}
// 应用安全规则
@RestController
@RequestMapping("/api/books")
@SecurityRequirement(name = "bearer-jwt")
public class BookController {
// 所有端点都需要JWT认证
}OAuth2 Configuration
OAuth2配置
java
import io.swagger.v3.oas.models.security.OAuthFlow;
import io.swagger.v3.oas.models.security.OAuthFlows;
import io.swagger.v3.oas.models.security.Scopes;
@Bean
public OpenAPI customOpenAPI() {
return new OpenAPI()
.components(new Components()
.addSecuritySchemes("oauth2", new SecurityScheme()
.type(SecurityScheme.Type.OAUTH2)
.flows(new OAuthFlows()
.authorizationCode(new OAuthFlow()
.authorizationUrl("https://auth.example.com/oauth/authorize")
.tokenUrl("https://auth.example.com/oauth/token")
.scopes(new Scopes()
.addString("read", "Read access")
.addString("write", "Write access")
)
)
)
)
);
}java
import io.swagger.v3.oas.models.security.OAuthFlow;
import io.swagger.v3.oas.models.security.OAuthFlows;
import io.swagger.v3.oas.models.security.Scopes;
@Bean
public OpenAPI customOpenAPI() {
return new OpenAPI()
.components(new Components()
.addSecuritySchemes("oauth2", new SecurityScheme()
.type(SecurityScheme.Type.OAUTH2)
.flows(new OAuthFlows()
.authorizationCode(new OAuthFlow()
.authorizationUrl("https://auth.example.com/oauth/authorize")
.tokenUrl("https://auth.example.com/oauth/token")
.scopes(new Scopes()
.addString("read", "Read access")
.addString("write", "Write access")
)
)
)
)
);
}Document Pagination
分页功能文档
Spring Data Pageable Support
Spring Data Pageable支持
java
import org.springdoc.core.annotations.ParameterObject;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
@Operation(summary = "Get paginated list of books")
@GetMapping("/paginated")
public Page<Book> findAllPaginated(
@ParameterObject Pageable pageable
) {
return repository.findAll(pageable);
}java
import org.springdoc.core.annotations.ParameterObject;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
@Operation(summary = "Get paginated list of books")
@GetMapping("/paginated")
public Page<Book> findAllPaginated(
@ParameterObject Pageable pageable
) {
return repository.findAll(pageable);
}Advanced Configuration
高级配置
Multiple API Groups
多API分组
java
import org.springdoc.core.models.GroupedOpenApi;
@Bean
public GroupedOpenApi publicApi() {
return GroupedOpenApi.builder()
.group("public")
.pathsToMatch("/api/public/**")
.build();
}
@Bean
public GroupedOpenApi adminApi() {
return GroupedOpenApi.builder()
.group("admin")
.pathsToMatch("/api/admin/**")
.build();
}java
import org.springdoc.core.models.GroupedOpenApi;
@Bean
public GroupedOpenApi publicApi() {
return GroupedOpenApi.builder()
.group("public")
.pathsToMatch("/api/public/**")
.build();
}
@Bean
public GroupedOpenApi adminApi() {
return GroupedOpenApi.builder()
.group("admin")
.pathsToMatch("/api/admin/**")
.build();
}Custom Operation Customizer
自定义操作定制器
java
import org.springdoc.core.customizers.OperationCustomizer;
@Bean
public OperationCustomizer customizeOperation() {
return (operation, handlerMethod) -> {
operation.addExtension("x-custom-field", "custom-value");
return operation;
};
}java
import org.springdoc.core.customizers.OperationCustomizer;
@Bean
public OperationCustomizer customizeOperation() {
return (operation, handlerMethod) -> {
operation.addExtension("x-custom-field", "custom-value");
return operation;
};
}Hide Endpoints
隐藏端点
java
@Operation(hidden = true)
@GetMapping("/internal")
public String internalEndpoint() {
return "Hidden from docs";
}
// Hide entire controller
@Hidden
@RestController
public class InternalController {
// All endpoints hidden
}java
@Operation(hidden = true)
@GetMapping("/internal")
public String internalEndpoint() {
return "Hidden from docs";
}
// 隐藏整个控制器
@Hidden
@RestController
public class InternalController {
// 所有端点都会被隐藏
}Document Exception Responses
异常响应文档
Global Exception Handler
全局异常处理器
java
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BookNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
@Operation(hidden = true)
public ErrorResponse handleBookNotFound(BookNotFoundException ex) {
return new ErrorResponse("BOOK_NOT_FOUND", ex.getMessage());
}
@ExceptionHandler(ValidationException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@Operation(hidden = true)
public ErrorResponse handleValidation(ValidationException ex) {
return new ErrorResponse("VALIDATION_ERROR", ex.getMessage());
}
}
@Schema(description = "Error response")
public record ErrorResponse(
@Schema(description = "Error code", example = "BOOK_NOT_FOUND")
String code,
@Schema(description = "Error message", example = "Book with ID 123 not found")
String message,
@Schema(description = "Timestamp", example = "2024-01-15T10:30:00Z")
LocalDateTime timestamp
) {}java
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BookNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
@Operation(hidden = true)
public ErrorResponse handleBookNotFound(BookNotFoundException ex) {
return new ErrorResponse("BOOK_NOT_FOUND", ex.getMessage());
}
@ExceptionHandler(ValidationException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@Operation(hidden = true)
public ErrorResponse handleValidation(ValidationException ex) {
return new ErrorResponse("VALIDATION_ERROR", ex.getMessage());
}
}
@Schema(description = "Error response")
public record ErrorResponse(
@Schema(description = "Error code", example = "BOOK_NOT_FOUND")
String code,
@Schema(description = "Error message", example = "Book with ID 123 not found")
String message,
@Schema(description = "Timestamp", example = "2024-01-15T10:30:00Z")
LocalDateTime timestamp
) {}Build Integration
构建集成
Maven Plugin
Maven插件
xml
<plugin>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-maven-plugin</artifactId>
<version>1.4</version>
<executions>
<execution>
<phase>integration-test</phase>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
<configuration>
<apiDocsUrl>http://localhost:8080/v3/api-docs</apiDocsUrl>
<outputFileName>openapi.json</outputFileName>
<outputDir>${project.build.directory}</outputDir>
</configuration>
</plugin>xml
<plugin>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-maven-plugin</artifactId>
<version>1.4</version>
<executions>
<execution>
<phase>integration-test</phase>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
<configuration>
<apiDocsUrl>http://localhost:8080/v3/api-docs</apiDocsUrl>
<outputFileName>openapi.json</outputFileName>
<outputDir>${project.build.directory}</outputDir>
</configuration>
</plugin>Gradle Plugin
Gradle插件
gradle
plugins {
id 'org.springdoc.openapi-gradle-plugin' version '1.9.0'
}
openApi {
apiDocsUrl = "http://localhost:8080/v3/api-docs"
outputDir = file("$buildDir/docs")
outputFileName = "openapi.json"
}gradle
plugins {
id 'org.springdoc.openapi-gradle-plugin' version '1.9.0'
}
openApi {
apiDocsUrl = "http://localhost:8080/v3/api-docs"
outputDir = file("$buildDir/docs")
outputFileName = "openapi.json"
}Examples
示例
Complete REST Controller Example
完整REST控制器示例
java
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springdoc.core.annotations.ParameterObject;
import org.springframework.web.bind.annotation.*;
import jakarta.validation.Valid;
@RestController
@RequestMapping("/api/books")
@Tag(name = "Book", description = "Book management APIs")
@SecurityRequirement(name = "bearer-jwt")
public class BookController {
private final BookService bookService;
public BookController(BookService bookService) {
this.bookService = bookService;
}
@Operation(summary = "Get all books")
@ApiResponses(value = {
@ApiResponse(
responseCode = "200",
description = "Found all books",
content = @Content(
mediaType = "application/json",
array = @ArraySchema(schema = @Schema(implementation = Book.class))
)
)
})
@GetMapping
public List<Book> getAllBooks() {
return bookService.getAllBooks();
}
@Operation(summary = "Get paginated books")
@GetMapping("/paginated")
public Page<Book> getBooksPaginated(@ParameterObject Pageable pageable) {
return bookService.getBooksPaginated(pageable);
}
@Operation(summary = "Get book by ID")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Book found"),
@ApiResponse(responseCode = "404", description = "Book not found")
})
@GetMapping("/{id}")
public Book getBookById(@PathVariable Long id) {
return bookService.getBookById(id);
}
@Operation(summary = "Create new book")
@ApiResponses(value = {
@ApiResponse(responseCode = "201", description = "Book created successfully"),
@ApiResponse(responseCode = "400", description = "Invalid input")
})
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Book createBook(@Valid @RequestBody Book book) {
return bookService.createBook(book);
}
@Operation(summary = "Update book")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Book updated"),
@ApiResponse(responseCode = "404", description = "Book not found")
})
@PutMapping("/{id}")
public Book updateBook(@PathVariable Long id, @Valid @RequestBody Book book) {
return bookService.updateBook(id, book);
}
@Operation(summary = "Delete book")
@ApiResponses(value = {
@ApiResponse(responseCode = "204", description = "Book deleted"),
@ApiResponse(responseCode = "404", description = "Book not found")
})
@DeleteMapping("/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void deleteBook(@PathVariable Long id) {
bookService.deleteBook(id);
}
}java
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springdoc.core.annotations.ParameterObject;
import org.springframework.web.bind.annotation.*;
import jakarta.validation.Valid;
@RestController
@RequestMapping("/api/books")
@Tag(name = "Book", description = "Book management APIs")
@SecurityRequirement(name = "bearer-jwt")
public class BookController {
private final BookService bookService;
public BookController(BookService bookService) {
this.bookService = bookService;
}
@Operation(summary = "Get all books")
@ApiResponses(value = {
@ApiResponse(
responseCode = "200",
description = "Found all books",
content = @Content(
mediaType = "application/json",
array = @ArraySchema(schema = @Schema(implementation = Book.class))
)
)
})
@GetMapping
public List<Book> getAllBooks() {
return bookService.getAllBooks();
}
@Operation(summary = "Get paginated books")
@GetMapping("/paginated")
public Page<Book> getBooksPaginated(@ParameterObject Pageable pageable) {
return bookService.getBooksPaginated(pageable);
}
@Operation(summary = "Get book by ID")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Book found"),
@ApiResponse(responseCode = "404", description = "Book not found")
})
@GetMapping("/{id}")
public Book getBookById(@PathVariable Long id) {
return bookService.getBookById(id);
}
@Operation(summary = "Create new book")
@ApiResponses(value = {
@ApiResponse(responseCode = "201", description = "Book created successfully"),
@ApiResponse(responseCode = "400", description = "Invalid input")
})
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Book createBook(@Valid @RequestBody Book book) {
return bookService.createBook(book);
}
@Operation(summary = "Update book")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Book updated"),
@ApiResponse(responseCode = "404", description = "Book not found")
})
@PutMapping("/{id}")
public Book updateBook(@PathVariable Long id, @Valid @RequestBody Book book) {
return bookService.updateBook(id, book);
}
@Operation(summary = "Delete book")
@ApiResponses(value = {
@ApiResponse(responseCode = "204", description = "Book deleted"),
@ApiResponse(responseCode = "404", description = "Book not found")
})
@DeleteMapping("/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void deleteBook(@PathVariable Long id) {
bookService.deleteBook(id);
}
}Best Practices
最佳实践
-
Use descriptive operation summaries and descriptions
- Summary: Short, clear statement (< 120 chars)
- Description: Detailed explanation with use cases
-
Document all response codes
- Include success (2xx), client errors (4xx), server errors (5xx)
- Provide meaningful descriptions for each
-
Add examples to request/response bodies
- Use for realistic examples
@ExampleObject - Include edge cases when relevant
- Use
-
Leverage JSR-303 validation annotations
- SpringDoc auto-generates constraints from validation annotations
- Reduces duplication between code and documentation
-
Usefor complex parameters
@ParameterObject- Especially useful for Pageable, custom filter objects
- Keeps controller methods clean
-
Group related endpoints with @Tag
- Organize API by domain entities or features
- Use consistent tag names across controllers
-
Document security requirements
- Apply where authentication needed
@SecurityRequirement - Configure security schemes globally in OpenAPI bean
- Apply
-
Hide internal/admin endpoints appropriately
- Use or create separate API groups
@Hidden - Prevent exposing internal implementation details
- Use
-
Customize Swagger UI for better UX
- Enable filtering, sorting, try-it-out features
- Set appropriate default behaviors
-
Version your API documentation
- Include version in OpenAPI Info
- Consider multiple API groups for versioned APIs
-
使用清晰的操作摘要和描述
- 摘要:简短明确的说明(不超过120个字符)
- 描述:包含使用场景的详细解释
-
为所有响应码编写文档
- 包含成功响应(2xx)、客户端错误(4xx)、服务端错误(5xx)
- 为每个响应码提供有意义的说明
-
为请求/响应体添加示例
- 使用提供真实可用的示例
@ExampleObject - 相关场景下补充边界 case 示例
- 使用
-
复用JSR-303校验注解
- SpringDoc会自动从校验注解生成约束规则
- 减少代码和文档之间的重复定义
-
复杂参数使用注解
@ParameterObject- 尤其适合Pageable、自定义过滤对象等场景
- 保持控制器方法代码整洁
-
使用对相关端点分组
@Tag- 按领域实体或功能模块组织API
- 跨控制器使用统一的标签名称
-
文档化安全要求
- 需要认证的接口添加注解
@SecurityRequirement - 在OpenAPI Bean中全局配置安全方案
- 需要认证的接口添加
-
合理隐藏内部/管理端端点
- 使用注解或者创建独立的API分组
@Hidden - 避免暴露内部实现细节
- 使用
-
自定义Swagger UI提升用户体验
- 开启过滤、排序、调试功能
- 设置合适的默认行为
-
为API文档添加版本管理
- 在OpenAPI Info中包含版本信息
- 多版本API可考虑使用多分组管理
Common Annotations Reference
常用注解参考
Core Annotations
核心注解
- : Group operations under a tag
@Tag - : Describe a single API operation
@Operation - /
@ApiResponse: Document response codes@ApiResponses - : Document a single parameter
@Parameter - : Document request body (OpenAPI version)
@RequestBody - : Document model schema
@Schema - : Apply security to operations
@SecurityRequirement - : Hide from documentation
@Hidden - : Document complex objects as parameters
@ParameterObject
- : 为接口操作分组
@Tag - : 描述单个API操作
@Operation - /
@ApiResponse: 文档化响应码@ApiResponses - : 描述单个参数
@Parameter - : 描述请求体(OpenAPI版本注解)
@RequestBody - : 描述模型Schema
@Schema - : 为操作应用安全规则
@SecurityRequirement - : 从文档中隐藏对应内容
@Hidden - : 将复杂对象转为参数描述
@ParameterObject
Validation Annotations (Auto-documented)
自动文档化的校验注解
- ,
@NotNull,@NotBlank: Required fields@NotEmpty - : String/collection length constraints
@Size(min, max) - ,
@Min: Numeric range constraints@Max - : Regex validation
@Pattern - : Email validation
@Email - ,
@DecimalMin: Decimal constraints@DecimalMax - ,
@Positive,@PositiveOrZero,@Negative@NegativeOrZero
- ,
@NotNull,@NotBlank: 必填字段@NotEmpty - : 字符串/集合长度约束
@Size(min, max) - ,
@Min: 数值范围约束@Max - : 正则校验
@Pattern - : 邮箱格式校验
@Email - ,
@DecimalMin: 小数范围约束@DecimalMax - ,
@Positive,@PositiveOrZero,@Negative: 正负数值约束@NegativeOrZero
Troubleshooting
问题排查
For common issues and solutions, refer to the troubleshooting guide in @references/troubleshooting.md
常见问题和解决方案可参考@references/troubleshooting.md中的排查指南
Related Skills
相关技能
- - REST API design standards
spring-boot-rest-api-standards - - Dependency injection patterns
spring-boot-dependency-injection - - Testing REST controllers
unit-test-controller-layer - - Production monitoring and management
spring-boot-actuator
- - REST API设计规范
spring-boot-rest-api-standards - - 依赖注入模式
spring-boot-dependency-injection - - REST控制器测试
unit-test-controller-layer - - 生产环境监控与管理
spring-boot-actuator
References
参考资料
- Comprehensive SpringDoc documentation
- Common issues and solutions
- SpringDoc Official Documentation
- OpenAPI 3.0 Specification OpenAPI Docs v1.1 - Enhanced
- 完整SpringDoc文档
- 常见问题与解决方案
- SpringDoc官方文档
- OpenAPI 3.0规范 OpenAPI Docs v1.1 - Enhanced
🔄 Workflow
🔄 工作流
Kaynak: SpringDoc Official Documentation
来源: SpringDoc官方文档
Aşama 1: Configuration
阶段1:配置
- Dependency: (v2.x for Spring Boot 3) ekle.
springdoc-openapi-starter-webmvc-ui - Properties: ve
springdoc.api-docs.pathdeğerlerini sabitle (custom path kullanıyorsan).swagger-ui.path - Platform: WebMVC vs WebFlux ayrımına dikkat et (dependency farklı).
- 依赖: 添加(Spring Boot 3对应v2.x版本)。
springdoc-openapi-starter-webmvc-ui - 配置项: 固定和
springdoc.api-docs.path的值(如果使用自定义路径)。swagger-ui.path - 平台适配: 注意区分WebMVC和WebFlux,二者依赖不同。
Aşama 2: Documentation Layer
阶段2:文档层开发
- Controller: ile grupla,
@Tagile her endpoint'i açıkla.@Operation - Models: DTO'ları ile tanımla, validation anotasyonlarını (
@Schema) ekle (otomatik yansır).@NotNull - Security: Global security scheme (JWT/OAuth2) tanımla ve endpoint'lere ekle.
@SecurityRequirement
- 控制器: 用分组,用
@Tag说明每个端点的作用。@Operation - 模型: 用定义DTO,添加校验注解(如
@Schema),规则会自动同步到文档。@NotNull - 安全配置: 定义全局安全方案(JWT/OAuth2),为对应端点添加注解。
@SecurityRequirement
Aşama 3: Enhancement
阶段3:优化增强
- Examples: kullanarak request/response body'ler için gerçekçi örnekler ver.
@ExampleObject - Error Handling: Global Exception Handler'daki hata response formatlarını ile dokümante et.
@ApiResponse - Generation: CI/CD pipeline'ında ile
springdoc-openapi-maven-pluginüret.openapi.json
- 示例补充: 使用为请求/响应体提供真实可用的示例。
@ExampleObject - 错误处理: 通过文档化全局异常处理器的错误响应格式。
@ApiResponse - 自动生成: 在CI/CD流水线中通过生成
springdoc-openapi-maven-plugin文件。openapi.json
Kontrol Noktaları
检查点
| Aşama | Doğrulama |
|---|---|
| 1 | |
| 2 | Swagger UI'da "Try it out" butonu çalışıyor mu (CORS/Auth sorunu var mı)? |
| 3 | Enum değerleri ve required alanlar dokümanda doğru görünüyor mu? |
| 阶段 | 验证项 |
|---|---|
| 1 | |
| 2 | Swagger UI的「Try it out」按钮是否正常工作?是否存在CORS/权限问题? |
| 3 | 枚举值和必填字段在文档中是否展示正确? |