|
|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
引言
在当今快速发展的软件开发行业中,API(应用程序编程接口)已经成为连接不同系统和服务的桥梁。随着微服务架构的普及,API的数量和复杂性不断增加,如何有效地管理和文档化这些API成为开发团队面临的重要挑战。Swagger(现已更名为OpenAPI)作为一个强大的API文档工具,能够帮助开发者自动生成、描述、调用和可视化RESTful风格的Web服务,极大地提升了API开发和维护的效率。
本文将详细介绍如何配置Swagger并添加请求示例,帮助您打造专业、易读的API文档,从而提升整个开发团队的工作效率。我们将从基础概念入手,逐步深入到高级配置和实战案例,确保您能够全面掌握Swagger的使用技巧。
Swagger基础
什么是Swagger
Swagger是一套围绕OpenAPI规范构建的开源工具,可以帮助您设计、构建、记录和使用RESTful Web服务。它主要包括以下几个部分:
• Swagger Editor:基于浏览器的编辑器,可以在其中编写OpenAPI规范。
• Swagger UI:将OpenAPI规范呈现为交互式API文档。
• Swagger Codegen:根据OpenAPI规范生成服务器存根和客户端库。
• Swagger Core:与Java等编程语言集成的库,用于创建OpenAPI规范。
OpenAPI规范
OpenAPI规范(OAS)是Linux基金会下的一个项目,它定义了一个标准的、与语言无关的接口描述RESTful API。OpenAPI文档使团队能够在不查看实际源代码的情况下理解和调用API。
Swagger的优势
使用Swagger具有以下优势:
1. 自动化文档生成:减少手动编写和维护文档的工作量。
2. 交互式API探索:开发者可以直接在文档中测试API端点。
3. 客户端代码生成:可以自动生成多种编程语言的客户端代码。
4. API设计优先:可以在编码前设计API,促进团队协作。
5. 提高开发效率:减少前后端沟通成本,加速开发流程。
环境搭建
在Spring Boot项目中集成Swagger
对于Java开发者来说,将Swagger集成到Spring Boot项目中最常用的库是Springfox。以下是集成步骤:
首先,在您的pom.xml文件中添加Springfox依赖:
- <dependency>
- <groupId>io.springfox</groupId>
- <artifactId>springfox-swagger2</artifactId>
- <version>3.0.0</version>
- </dependency>
- <dependency>
- <groupId>io.springfox</groupId>
- <artifactId>springfox-swagger-ui</artifactId>
- <version>3.0.0</version>
- </dependency>
复制代码
如果您使用的是Spring Boot 2.6.x或更高版本,还需要添加以下配置以解决路径匹配问题:
- @Configuration
- public class WebConfig implements WebMvcConfigurer {
- @Override
- public void configurePathMatch(PathMatchConfigurer configurer) {
- configurer.addPathPrefix("/api", Pattern.compile("/api"));
- }
- }
复制代码
创建一个配置类来启用Swagger:
- @Configuration
- @EnableSwagger2
- public class SwaggerConfig {
-
- @Bean
- public Docket api() {
- return new Docket(DocumentationType.SWAGGER_2)
- .select()
- .apis(RequestHandlerSelectors.basePackage("com.example.controller"))
- .paths(PathSelectors.any())
- .build();
- }
- }
复制代码
启动应用程序后,您可以通过访问以下URL来查看Swagger UI:
- http://localhost:8080/swagger-ui.html
复制代码
在其他框架中的集成
在Node.js的Express框架中,您可以使用swagger-ui-express和swagger-jsdoc来集成Swagger:
- const express = require('express');
- const swaggerUi = require('swagger-ui-express');
- const swaggerJsdoc = require('swagger-jsdoc');
- const app = express();
- const options = {
- definition: {
- openapi: '3.0.0',
- info: {
- title: 'My API',
- version: '1.0.0',
- },
- },
- apis: ['./routes/*.js'], // 包含API注解的文件路径
- };
- const specs = swaggerJsdoc(options);
- app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(specs));
- // ... 其他路由和中间件
- app.listen(3000, () => {
- console.log('Server is running on port 3000');
- });
复制代码
在Python的Flask框架中,您可以使用flask-swagger-ui:
- from flask import Flask
- from flask_swagger_ui import get_swaggerui_blueprint
- app = Flask(__name__)
- SWAGGER_URL = '/api/docs' # Swagger UI的URL
- API_URL = '/static/swagger.json' # API文档文件的URL
- swaggerui_blueprint = get_swaggerui_blueprint(
- SWAGGER_URL,
- API_URL,
- config={
- 'app_name': "My API"
- }
- )
- app.register_blueprint(swaggerui_blueprint, url_prefix=SWAGGER_URL)
- if __name__ == '__main__':
- app.run(debug=True)
复制代码
基础配置
Docket配置详解
在Spring Boot中,Docket是Swagger的核心配置类,它允许您自定义Swagger的行为。以下是一些常用的配置选项:
- @Configuration
- @EnableSwagger2
- public class SwaggerConfig {
-
- @Bean
- public Docket api() {
- return new Docket(DocumentationType.SWAGGER_2)
- .apiInfo(apiInfo()) // API基本信息
- .select()
- .apis(RequestHandlerSelectors.basePackage("com.example.controller")) // 控制器包路径
- .paths(PathSelectors.any()) // 所有路径
- .build()
- .produces(Collections.singleton("application/json")) // 生成的内容类型
- .consumes(Collections.singleton("application/json")) // 接受的内容类型
- .protocols(Collections.singleton("http")) // 支持的协议
- .securitySchemes(Collections.singletonList(apiKey())) // 安全方案
- .securityContexts(Collections.singletonList(securityContext())); // 安全上下文
- }
-
- private ApiInfo apiInfo() {
- return new ApiInfoBuilder()
- .title("示例API文档")
- .description("这是一个示例API的文档")
- .version("1.0.0")
- .contact(new Contact("开发者姓名", "https://example.com", "developer@example.com"))
- .license("Apache License Version 2.0")
- .licenseUrl("https://www.apache.org/licenses/LICENSE-2.0")
- .build();
- }
-
- private ApiKey apiKey() {
- return new ApiKey("JWT", "Authorization", "header");
- }
-
- private SecurityContext securityContext() {
- return SecurityContext.builder()
- .securityReferences(defaultAuth())
- .forPaths(PathSelectors.regex("/api/.*"))
- .build();
- }
-
- private List<SecurityReference> defaultAuth() {
- AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
- return Collections.singletonList(new SecurityReference("JWT", new AuthorizationScope[]{authorizationScope}));
- }
- }
复制代码
全局参数配置
您可以为所有API请求添加全局参数,例如认证头:
- @Bean
- public Docket api() {
- return new Docket(DocumentationType.SWAGGER_2)
- .globalOperationParameters(
- Collections.singletonList(
- new ParameterBuilder()
- .name("Authorization")
- .description("认证令牌")
- .modelRef(new ModelRef("string"))
- .parameterType("header")
- .required(true)
- .build()
- )
- )
- // ... 其他配置
- .build();
- }
复制代码
全局响应配置
您可以为所有API响应添加全局响应消息:
- @Bean
- public Docket api() {
- return new Docket(DocumentationType.SWAGGER_2)
- .globalResponseMessage(RequestMethod.GET,
- Collections.singletonList(
- new ResponseMessageBuilder()
- .code(500)
- .message("服务器错误")
- .responseModel(new ModelRef("Error"))
- .build()
- )
- )
- // ... 其他配置
- .build();
- }
复制代码
注解详解
Swagger提供了一系列注解,帮助您丰富API文档的内容。下面详细介绍常用注解及其使用方法。
API级别注解
用于标记Controller类作为Swagger文档的资源。
- @Api(tags = "用户管理", description = "提供用户的增删改查功能")
- @RestController
- @RequestMapping("/api/users")
- public class UserController {
- // ...
- }
复制代码
用于对API进行排序,这在有多个Controller时特别有用。
- @ApiSort(1)
- @Api(tags = "用户管理")
- @RestController
- @RequestMapping("/api/users")
- public class UserController {
- // ...
- }
复制代码
方法级别注解
用于描述一个方法或API操作。
- @ApiOperation(
- value = "获取用户列表",
- notes = "获取系统中所有用户的列表,支持分页",
- response = User.class,
- responseContainer = "List",
- tags = {"用户管理"}
- )
- @GetMapping
- public ResponseEntity<List<User>> getAllUsers() {
- // ...
- }
复制代码
用于描述方法的参数。
- @ApiImplicitParams({
- @ApiImplicitParam(
- name = "page",
- value = "页码",
- required = true,
- dataType = "int",
- paramType = "query",
- defaultValue = "1"
- ),
- @ApiImplicitParam(
- name = "size",
- value = "每页大小",
- required = true,
- dataType = "int",
- paramType = "query",
- defaultValue = "10"
- )
- })
- @GetMapping
- public ResponseEntity<List<User>> getUsersByPage(@RequestParam int page, @RequestParam int size) {
- // ...
- }
复制代码
用于描述方法可能的响应。
- @ApiResponses(value = {
- @ApiResponse(code = 200, message = "成功获取用户", response = User.class),
- @ApiResponse(code = 401, message = "未授权"),
- @ApiResponse(code = 403, message = "禁止访问"),
- @ApiResponse(code = 404, message = "用户不存在"),
- @ApiResponse(code = 500, message = "服务器错误")
- })
- @GetMapping("/{id}")
- public ResponseEntity<User> getUserById(@PathVariable Long id) {
- // ...
- }
复制代码
模型注解
用于描述模型(通常是实体类)。
- @ApiModel(description = "用户详细信息")
- public class User {
- // ...
- }
复制代码
用于描述模型属性。
- public class User {
- @ApiModelProperty(value = "用户ID", example = "1", required = true)
- private Long id;
-
- @ApiModelProperty(value = "用户名", example = "john_doe", required = true)
- private String username;
-
- @ApiModelProperty(value = "电子邮箱", example = "john@example.com", required = true)
- private String email;
-
- @ApiModelProperty(value = "密码", example = "password123", required = true, accessMode = ApiModelProperty.AccessMode.WRITE_ONLY)
- private String password;
-
- @ApiModelProperty(value = "创建时间", example = "2023-01-01T00:00:00Z", readOnly = true)
- private Date createdAt;
-
- // getters and setters
- }
复制代码
请求和响应注解
当使用@RequestBody时,可以结合@ApiParam提供更详细的描述。
- @ApiOperation(value = "创建新用户")
- @PostMapping
- public ResponseEntity<User> createUser(
- @ApiParam(value = "用户对象", required = true) @Valid @RequestBody UserDto userDto) {
- // ...
- }
复制代码
用于忽略某个方法或参数,使其不出现在API文档中。
- @ApiIgnore
- @GetMapping("/internal")
- public String internalMethod() {
- return "This is an internal method";
- }
复制代码
请求示例配置
请求示例是API文档中非常重要的一部分,它可以帮助开发者快速了解如何正确地调用API。下面介绍如何在Swagger中配置请求示例。
使用@ApiModelProperty添加示例值
最简单的方式是在@ApiModelProperty注解中直接提供example属性:
- public class UserDto {
- @ApiModelProperty(value = "用户名", example = "john_doe", required = true)
- private String username;
-
- @ApiModelProperty(value = "电子邮箱", example = "john@example.com", required = true)
- private String email;
-
- @ApiModelProperty(value = "年龄", example = "30")
- private Integer age;
-
- // getters and setters
- }
复制代码
使用@Example和@ExampleProperty
对于更复杂的示例,可以使用@Example和@ExampleProperty注解:
- @ApiOperation(value = "创建用户", notes = "创建一个新用户")
- @ApiResponses(value = {
- @ApiResponse(code = 201, message = "用户创建成功",
- examples = @Example(value = {
- @ExampleProperty(mediaType = "application/json", value = "{"id": 1, "username": "john_doe", "email": "john@example.com"}")
- })
- )
- })
- @PostMapping
- public ResponseEntity<User> createUser(@Valid @RequestBody UserDto userDto) {
- // ...
- }
复制代码
在@ApiOperation中添加示例
您可以在@ApiOperation注解中直接添加示例:
- @ApiOperation(
- value = "更新用户",
- notes = "更新指定ID的用户信息",
- examples = @Example(value = {
- @ExampleProperty(mediaType = "application/json", value = "{"username": "new_username", "email": "new_email@example.com"}")
- })
- )
- @PutMapping("/{id}")
- public ResponseEntity<User> updateUser(
- @ApiParam(value = "用户ID", example = "1") @PathVariable Long id,
- @Valid @RequestBody UserDto userDto) {
- // ...
- }
复制代码
使用@RequestPart和@ExampleProperty处理文件上传
对于文件上传接口,可以使用@RequestPart和@ExampleProperty:
- @ApiOperation(value = "上传用户头像")
- @ApiResponses(value = {
- @ApiResponse(code = 200, message = "上传成功",
- examples = @Example(value = {
- @ExampleProperty(mediaType = "application/json", value = "{"url": "https://example.com/avatar.jpg"}")
- })
- )
- })
- @PostMapping("/{id}/avatar")
- public ResponseEntity<Map<String, String>> uploadAvatar(
- @ApiParam(value = "用户ID", example = "1") @PathVariable Long id,
- @ApiParam(value = "头像文件", required = true) @RequestPart("file") MultipartFile file) {
- // ...
- }
复制代码
使用Swagger配置类添加全局示例
您可以在Swagger配置类中添加全局示例:
- @Bean
- public Docket api() {
- return new Docket(DocumentationType.SWAGGER_2)
- .globalRequestParameters(Collections.singletonList(
- new ParameterBuilder()
- .name("Authorization")
- .description("认证令牌")
- .modelRef(new ModelRef("string"))
- .parameterType("header")
- .required(true)
- .example("Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...")
- .build()
- ))
- // ... 其他配置
- .build();
- }
复制代码
使用@Operation和@RequestBody(OpenAPI 3.0)
如果您使用的是OpenAPI 3.0,可以使用@Operation和@RequestBody注解:
- @Operation(
- summary = "创建用户",
- description = "创建一个新用户",
- requestBody = @RequestBody(
- description = "用户对象",
- required = true,
- content = @Content(
- mediaType = "application/json",
- schema = @Schema(implementation = UserDto.class),
- examples = {
- @ExampleObject(
- name = "示例1",
- description = "一个普通的用户示例",
- value = "{"username": "john_doe", "email": "john@example.com", "age": 30}"
- ),
- @ExampleObject(
- name = "示例2",
- description = "一个包含所有字段的用户示例",
- value = "{"username": "jane_doe", "email": "jane@example.com", "age": 25, "phone": "1234567890"}"
- )
- }
- )
- )
- )
- @PostMapping
- public ResponseEntity<User> createUser(@Valid @RequestBody UserDto userDto) {
- // ...
- }
复制代码
动态生成请求示例
有时您可能需要根据不同的条件动态生成请求示例。这时,您可以创建一个自定义的示例提供器:
- public class UserExampleProvider {
-
- public static User getExampleUser() {
- User user = new User();
- user.setId(1L);
- user.setUsername("john_doe");
- user.setEmail("john@example.com");
- user.setAge(30);
- user.setCreatedAt(new Date());
- return user;
- }
-
- public static List<User> getExampleUserList() {
- List<User> users = new ArrayList<>();
- users.add(getExampleUser());
-
- User user2 = new User();
- user2.setId(2L);
- user2.setUsername("jane_doe");
- user2.setEmail("jane@example.com");
- user2.setAge(25);
- user2.setCreatedAt(new Date());
- users.add(user2);
-
- return users;
- }
- }
复制代码
然后在控制器中使用这些示例:
- @ApiOperation(
- value = "获取用户列表",
- notes = "获取系统中所有用户的列表",
- response = User.class,
- responseContainer = "List"
- )
- @GetMapping
- public ResponseEntity<List<User>> getAllUsers() {
- // 实际实现...
- // 这里只是一个示例,展示如何使用示例提供器
- return ResponseEntity.ok(UserExampleProvider.getExampleUserList());
- }
复制代码
高级功能
API分组
在大型项目中,您可能需要将API文档分组,以便更好地组织和管理。Swagger提供了分组功能,允许您根据不同的标准(如模块、版本等)对API进行分组。
- @Configuration
- @EnableSwagger2
- public class SwaggerConfig {
-
- @Bean
- public Docket publicApi() {
- return new Docket(DocumentationType.SWAGGER_2)
- .groupName("public-api")
- .select()
- .apis(RequestHandlerSelectors.basePackage("com.example.controller.public"))
- .paths(PathSelectors.any())
- .build()
- .apiInfo(publicApiInfo());
- }
-
- @Bean
- public Docket adminApi() {
- return new Docket(DocumentationType.SWAGGER_2)
- .groupName("admin-api")
- .select()
- .apis(RequestHandlerSelectors.basePackage("com.example.controller.admin"))
- .paths(PathSelectors.any())
- .build()
- .apiInfo(adminApiInfo())
- .securitySchemes(Collections.singletonList(apiKey()))
- .securityContexts(Collections.singletonList(securityContext()));
- }
-
- private ApiInfo publicApiInfo() {
- return new ApiInfoBuilder()
- .title("公共API文档")
- .description "系统公共接口文档")
- .version("1.0.0")
- .build();
- }
-
- private ApiInfo adminApiInfo() {
- return new ApiInfoBuilder()
- .title("管理API文档")
- .description("系统管理接口文档")
- .version("1.0.0")
- .build();
- }
-
- // 其他方法...
- }
复制代码
自定义Swagger UI
您可以通过自定义配置来改变Swagger UI的外观和行为。创建一个WebMvcConfigurer来添加自定义资源:
- @Configuration
- public class WebMvcConfig implements WebMvcConfigurer {
-
- @Override
- public void addResourceHandlers(ResourceHandlerRegistry registry) {
- registry.addResourceHandler("swagger-ui.html")
- .addResourceLocations("classpath:/META-INF/resources/");
-
- registry.addResourceHandler("/webjars/**")
- .addResourceLocations("classpath:/META-INF/resources/webjars/");
- }
- }
复制代码
然后,您可以创建一个自定义的Swagger UI页面:
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="UTF-8">
- <title>自定义Swagger UI</title>
- <link rel="stylesheet" type="text/css" href="./webjars/swagger-ui/dist/swagger-ui.css" />
- <link rel="stylesheet" type="text/css" href="./css/custom-swagger.css" />
- <style>
- html {
- box-sizing: border-box;
- overflow: -moz-scrollbars-vertical;
- overflow-y: scroll;
- }
- *, *:before, *:after {
- box-sizing: inherit;
- }
- body {
- margin: 0;
- background: #fafafa;
- }
- </style>
- </head>
- <body>
- <div id="swagger-ui"></div>
- <script src="./webjars/swagger-ui/dist/swagger-ui-bundle.js"></script>
- <script src="./webjars/swagger-ui/dist/swagger-ui-standalone-preset.js"></script>
- <script>
- window.onload = function() {
- const ui = SwaggerUIBundle({
- url: "/v2/api-docs",
- dom_id: '#swagger-ui',
- deepLinking: true,
- presets: [
- SwaggerUIBundle.presets.apis,
- SwaggerUIStandalonePreset
- ],
- plugins: [
- SwaggerUIBundle.plugins.DownloadUrl
- ],
- layout: "StandaloneLayout",
- validatorUrl: null,
- supportedSubmitMethods: ['get', 'post', 'put', 'delete', 'patch'],
- docExpansion: "none",
- defaultModelsExpandDepth: -1,
- displayRequestDuration: true,
- filter: true,
- persistAuthorization: true
- });
- };
- </script>
- </body>
- </html>
复制代码
集成Spring Security
如果您的应用使用了Spring Security,您需要配置Swagger UI可以访问的端点:
- @Configuration
- @EnableWebSecurity
- public class SecurityConfig extends WebSecurityConfigurerAdapter {
-
- @Override
- protected void configure(HttpSecurity http) throws Exception {
- http
- .authorizeRequests()
- .antMatchers("/v2/api-docs", "/configuration/**", "/swagger*/**", "/webjars/**").permitAll()
- .anyRequest().authenticated()
- .and()
- .csrf().disable();
- }
- }
复制代码
多环境配置
您可能希望在不同的环境中使用不同的Swagger配置。可以通过Spring Profile实现:
- @Configuration
- @Profile({"dev", "test"})
- @EnableSwagger2
- public class SwaggerConfig {
-
- @Bean
- public Docket api() {
- return new Docket(DocumentationType.SWAGGER_2)
- .select()
- .apis(RequestHandlerSelectors.any())
- .paths(PathSelectors.any())
- .build();
- }
- }
复制代码
然后在application.properties中指定活动Profile:
- spring.profiles.active=dev
复制代码
API版本控制
随着API的演进,您可能需要支持多个版本的API。Swagger可以帮助您实现这一点:
- @Configuration
- @EnableSwagger2
- public class SwaggerConfig {
-
- @Bean
- public Docket apiV1() {
- return new Docket(DocumentationType.SWAGGER_2)
- .groupName("v1")
- .select()
- .apis(RequestHandlerSelectors.withMethodAnnotation(ApiV1.class))
- .build()
- .apiInfo(apiV1Info());
- }
-
- @Bean
- public Docket apiV2() {
- return new Docket(DocumentationType.SWAGGER_2)
- .groupName("v2")
- .select()
- .apis(RequestHandlerSelectors.withMethodAnnotation(ApiV2.class))
- .build()
- .apiInfo(apiV2Info());
- }
-
- private ApiInfo apiV1Info() {
- return new ApiInfoBuilder()
- .title("API V1文档")
- .version("1.0.0")
- .build();
- }
-
- private ApiInfo apiV2Info() {
- return new ApiInfoBuilder()
- .title("API V2文档")
- .version("2.0.0")
- .build();
- }
- }
复制代码
然后创建自定义注解来标记不同版本的API:
- @Target({ElementType.METHOD, ElementType.TYPE})
- @Retention(RetentionPolicy.RUNTIME)
- public @interface ApiV1 {
- }
- @Target({ElementType.METHOD, ElementType.TYPE})
- @Retention(RetentionPolicy.RUNTIME)
- public @interface ApiV2 {
- }
复制代码
在控制器中使用这些注解:
- @ApiV1
- @GetMapping("/v1/users")
- public ResponseEntity<List<User>> getUsersV1() {
- // V1实现
- }
- @ApiV2
- @GetMapping("/v2/users")
- public ResponseEntity<List<UserDto>> getUsersV2() {
- // V2实现
- }
复制代码
最佳实践
1. 保持文档与代码同步
Swagger最大的优势之一是能够保持文档与代码同步。为了充分利用这一点:
• 使用注解来描述API,而不是单独维护文档文件。
• 在API变更时,同时更新相关的Swagger注解。
• 将API文档检查纳入CI/CD流程,确保文档始终是最新的。
2. 提供清晰且有意义的描述
为API、参数和模型提供清晰且有意义的描述:
- @ApiOperation(
- value = "创建新用户",
- notes = "创建一个新用户账户。用户名必须是唯一的,电子邮件地址必须是有效的格式。密码必须至少包含8个字符,包括至少一个大写字母、一个小写字母和一个数字。"
- )
- @PostMapping
- public ResponseEntity<User> createUser(@Valid @RequestBody UserDto userDto) {
- // ...
- }
复制代码
3. 使用适当的HTTP状态码
使用标准HTTP状态码来表示操作结果:
- @ApiResponses(value = {
- @ApiResponse(code = 201, message = "用户创建成功", response = User.class),
- @ApiResponse(code = 400, message = "无效的输入数据"),
- @ApiResponse(code = 409, message = "用户名或电子邮件已存在"),
- @ApiResponse(code = 500, message = "服务器错误")
- })
- @PostMapping
- public ResponseEntity<User> createUser(@Valid @RequestBody UserDto userDto) {
- // ...
- }
复制代码
4. 提供真实且有意义的示例
提供真实且有意义的请求和响应示例,而不是无意义的占位符:
- public class UserDto {
- @ApiModelProperty(
- value = "用户名",
- example = "john_doe",
- required = true,
- notes = "用户名必须是唯一的,只能包含字母、数字和下划线,长度在3到20个字符之间。"
- )
- private String username;
-
- @ApiModelProperty(
- value = "电子邮箱",
- example = "john@example.com",
- required = true,
- notes = "必须是有效的电子邮件地址格式。"
- )
- private String email;
-
- // ...
- }
复制代码
5. 使用DTO而不是实体类
在API中使用DTO(数据传输对象)而不是直接使用实体类,这样可以:
• 隐藏不想暴露的字段(如密码哈希)。
• 提供更适合API需求的字段结构。
• 避免因实体类变更而影响API兼容性。
- @ApiModel(description = "用户数据传输对象")
- public class UserDto {
- @ApiModelProperty(value = "用户ID", example = "1", readOnly = true)
- private Long id;
-
- @ApiModelProperty(value = "用户名", example = "john_doe", required = true)
- private String username;
-
- @ApiModelProperty(value = "电子邮箱", example = "john@example.com", required = true)
- private String email;
-
- // 不包含密码等敏感字段
-
- // getters and setters
- }
复制代码
6. 合理使用分组和标签
对于大型项目,使用分组和标签来组织API文档:
- @Api(tags = {"用户管理", "核心功能"})
- @RestController
- @RequestMapping("/api/users")
- public class UserController {
- // ...
- }
复制代码
7. 添加认证信息
如果API需要认证,确保在文档中明确说明:
- @Bean
- public Docket api() {
- return new Docket(DocumentationType.SWAGGER_2)
- .securitySchemes(Collections.singletonList(apiKey()))
- .securityContexts(Collections.singletonList(securityContext()))
- // ... 其他配置
- .build();
- }
- private ApiKey apiKey() {
- return new ApiKey("JWT", "Authorization", "header");
- }
- private SecurityContext securityContext() {
- return SecurityContext.builder()
- .securityReferences(defaultAuth())
- .forPaths(PathSelectors.regex("/api/.*"))
- .build();
- }
- private List<SecurityReference> defaultAuth() {
- AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
- return Collections.singletonList(new SecurityReference("JWT", new AuthorizationScope[]{authorizationScope}));
- }
复制代码
8. 在生产环境中禁用Swagger
出于安全考虑,应该在生产环境中禁用Swagger:
- @Configuration
- @Profile({"dev", "test"})
- @EnableSwagger2
- public class SwaggerConfig {
- // ...
- }
复制代码
或者使用条件配置:
- @Bean
- @ConditionalOnProperty(name = "swagger.enabled", havingValue = "true")
- public Docket api() {
- return new Docket(DocumentationType.SWAGGER_2)
- // ...
- .build();
- }
复制代码
然后在application.properties中:
- # 开发和测试环境
- swagger.enabled=true
- # 生产环境
- # swagger.enabled=false
复制代码
9. 使用外部化配置
对于复杂的Swagger配置,考虑使用外部化配置:
- @Configuration
- @EnableSwagger2
- @ConfigurationProperties(prefix = "swagger")
- public class SwaggerConfig {
- private String title;
- private String description;
- private String version;
- private Contact contact;
-
- @Bean
- public Docket api() {
- return new Docket(DocumentationType.SWAGGER_2)
- .apiInfo(new ApiInfoBuilder()
- .title(title)
- .description(description)
- .version(version)
- .contact(contact)
- .build())
- // ... 其他配置
- .build();
- }
-
- // getters and setters
- }
复制代码
然后在application.yml中:
- swagger:
- title: "我的API文档"
- description: "这是一个示例API的文档"
- version: "1.0.0"
- contact:
- name: "开发者姓名"
- email: "developer@example.com"
- url: "https://example.com"
复制代码
10. 定期审查和更新文档
定期审查和更新API文档,确保其准确性和完整性:
• 将文档审查纳入开发流程。
• 在API变更时,评估是否需要更新文档。
• 收集用户反馈,持续改进文档质量。
实战案例
让我们通过一个完整的示例来展示如何使用Swagger创建专业、易读的API文档。我们将创建一个简单的博客系统API,包括用户管理、文章管理和评论管理功能。
项目结构
- src/main/java/com/example/blog/
- ├── config/
- │ └── SwaggerConfig.java
- ├── controller/
- │ ├── AuthController.java
- │ ├── PostController.java
- │ ├── CommentController.java
- │ └── UserController.java
- ├── dto/
- │ ├── request/
- │ │ ├── CreateUserRequest.java
- │ │ ├── CreatePostRequest.java
- │ │ ├── CreateCommentRequest.java
- │ │ └── LoginRequest.java
- │ └── response/
- │ ├── UserResponse.java
- │ ├── PostResponse.java
- │ └── CommentResponse.java
- ├── model/
- │ ├── User.java
- │ ├── Post.java
- │ └── Comment.java
- ├── repository/
- │ ├── UserRepository.java
- │ ├── PostRepository.java
- │ └── CommentRepository.java
- ├── security/
- │ ├── JwtTokenProvider.java
- │ └── JwtAuthenticationFilter.java
- └── BlogApplication.java
复制代码
依赖配置
首先,在pom.xml中添加必要的依赖:
- <dependencies>
- <!-- Spring Boot Starter Web -->
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-web</artifactId>
- </dependency>
-
- <!-- Spring Boot Starter Data JPA -->
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-data-jpa</artifactId>
- </dependency>
-
- <!-- Spring Boot Starter Security -->
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-security</artifactId>
- </dependency>
-
- <!-- Spring Boot Starter Validation -->
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-validation</artifactId>
- </dependency>
-
- <!-- H2 Database -->
- <dependency>
- <groupId>com.h2database</groupId>
- <artifactId>h2</artifactId>
- <scope>runtime</scope>
- </dependency>
-
- <!-- JWT -->
- <dependency>
- <groupId>io.jsonwebtoken</groupId>
- <artifactId>jjwt-api</artifactId>
- <version>0.11.5</version>
- </dependency>
- <dependency>
- <groupId>io.jsonwebtoken</groupId>
- <artifactId>jjwt-impl</artifactId>
- <version>0.11.5</version>
- <scope>runtime</scope>
- </dependency>
- <dependency>
- <groupId>io.jsonwebtoken</groupId>
- <artifactId>jjwt-jackson</artifactId>
- <version>0.11.5</version>
- <scope>runtime</scope>
- </dependency>
-
- <!-- Swagger -->
- <dependency>
- <groupId>io.springfox</groupId>
- <artifactId>springfox-swagger2</artifactId>
- <version>3.0.0</version>
- </dependency>
- <dependency>
- <groupId>io.springfox</groupId>
- <artifactId>springfox-swagger-ui</artifactId>
- <version>3.0.0</version>
- </dependency>
-
- <!-- Lombok -->
- <dependency>
- <groupId>org.projectlombok</groupId>
- <artifactId>lombok</artifactId>
- <optional>true</optional>
- </dependency>
- </dependencies>
复制代码
Swagger配置
创建Swagger配置类:
- package com.example.blog.config;
- import com.example.blog.security.JwtTokenProvider;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import springfox.documentation.builders.ApiInfoBuilder;
- import springfox.documentation.builders.PathSelectors;
- import springfox.documentation.builders.RequestHandlerSelectors;
- import springfox.documentation.service.*;
- import springfox.documentation.spi.DocumentationType;
- import springfox.documentation.spi.service.contexts.SecurityContext;
- import springfox.documentation.spring.web.plugins.Docket;
- import springfox.documentation.swagger2.annotations.EnableSwagger2;
- import java.util.Arrays;
- import java.util.Collections;
- import java.util.List;
- @Configuration
- @EnableSwagger2
- public class SwaggerConfig {
- @Bean
- public Docket api() {
- return new Docket(DocumentationType.SWAGGER_2)
- .select()
- .apis(RequestHandlerSelectors.basePackage("com.example.blog.controller"))
- .paths(PathSelectors.any())
- .build()
- .apiInfo(apiInfo())
- .securityContexts(Collections.singletonList(securityContext()))
- .securitySchemes(Collections.singletonList(apiKey()))
- .produces(Collections.singleton("application/json"))
- .consumes(Collections.singleton("application/json"));
- }
- private ApiInfo apiInfo() {
- return new ApiInfoBuilder()
- .title("博客系统API文档")
- .description("这是一个博客系统的RESTful API文档,提供了用户管理、文章管理和评论管理等功能。")
- .version("1.0.0")
- .contact(new Contact("博客开发团队", "https://blog.example.com", "dev@blog.example.com"))
- .license("MIT License")
- .licenseUrl("https://opensource.org/licenses/MIT")
- .build();
- }
- private ApiKey apiKey() {
- return new ApiKey("JWT", "Authorization", "header");
- }
- private SecurityContext securityContext() {
- return SecurityContext.builder()
- .securityReferences(defaultAuth())
- .forPaths(PathSelectors.regex("/api/.*"))
- .build();
- }
- private List<SecurityReference> defaultAuth() {
- AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
- return Collections.singletonList(new SecurityReference("JWT", new AuthorizationScope[]{authorizationScope}));
- }
- }
复制代码
模型类
定义用户模型:
- package com.example.blog.model;
- import lombok.AllArgsConstructor;
- import lombok.Data;
- import lombok.NoArgsConstructor;
- import javax.persistence.*;
- import javax.validation.constraints.Email;
- import javax.validation.constraints.NotBlank;
- import javax.validation.constraints.Size;
- import java.time.LocalDateTime;
- import java.util.HashSet;
- import java.util.Set;
- @Entity
- @Table(name = "users")
- @Data
- @NoArgsConstructor
- @AllArgsConstructor
- public class User {
- @Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
- private Long id;
- @NotBlank
- @Size(min = 3, max = 20)
- @Column(unique = true)
- private String username;
- @NotBlank
- @Size(max = 50)
- @Email
- @Column(unique = true)
- private String email;
- @NotBlank
- @Size(min = 6, max = 100)
- private String password;
- @Column(name = "created_at")
- private LocalDateTime createdAt;
- @Column(name = "updated_at")
- private LocalDateTime updatedAt;
- @ElementCollection(fetch = FetchType.EAGER)
- @Enumerated(EnumType.STRING)
- private Set<Role> roles = new HashSet<>();
- public User(String username, String email, String password) {
- this.username = username;
- this.email = email;
- this.password = password;
- this.createdAt = LocalDateTime.now();
- this.updatedAt = LocalDateTime.now();
- this.roles.add(Role.ROLE_USER);
- }
- public enum Role {
- ROLE_USER,
- ROLE_ADMIN
- }
- }
复制代码
定义文章模型:
- package com.example.blog.model;
- import lombok.AllArgsConstructor;
- import lombok.Data;
- import lombok.NoArgsConstructor;
- import javax.persistence.*;
- import javax.validation.constraints.NotBlank;
- import javax.validation.constraints.Size;
- import java.time.LocalDateTime;
- import java.util.HashSet;
- import java.util.Set;
- @Entity
- @Table(name = "posts")
- @Data
- @NoArgsConstructor
- @AllArgsConstructor
- public class Post {
- @Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
- private Long id;
- @NotBlank
- @Size(min = 1, max = 100)
- private String title;
- @NotBlank
- @Size(min = 1, max = 5000)
- private String content;
- @Column(name = "created_at")
- private LocalDateTime createdAt;
- @Column(name = "updated_at")
- private LocalDateTime updatedAt;
- @ManyToOne(fetch = FetchType.LAZY)
- @JoinColumn(name = "author_id")
- private User author;
- @OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true)
- private Set<Comment> comments = new HashSet<>();
- public Post(String title, String content, User author) {
- this.title = title;
- this.content = content;
- this.author = author;
- this.createdAt = LocalDateTime.now();
- this.updatedAt = LocalDateTime.now();
- }
- }
复制代码
定义评论模型:
- package com.example.blog.model;
- import lombok.AllArgsConstructor;
- import lombok.Data;
- import lombok.NoArgsConstructor;
- import javax.persistence.*;
- import javax.validation.constraints.NotBlank;
- import javax.validation.constraints.Size;
- import java.time.LocalDateTime;
- @Entity
- @Table(name = "comments")
- @Data
- @NoArgsConstructor
- @AllArgsConstructor
- public class Comment {
- @Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
- private Long id;
- @NotBlank
- @Size(min = 1, max = 1000)
- private String text;
- @Column(name = "created_at")
- private LocalDateTime createdAt;
- @Column(name = "updated_at")
- private LocalDateTime updatedAt;
- @ManyToOne(fetch = FetchType.LAZY)
- @JoinColumn(name = "post_id")
- private Post post;
- @ManyToOne(fetch = FetchType.LAZY)
- @JoinColumn(name = "author_id")
- private User author;
- public Comment(String text, Post post, User author) {
- this.text = text;
- this.post = post;
- this.author = author;
- this.createdAt = LocalDateTime.now();
- this.updatedAt = LocalDateTime.now();
- }
- }
复制代码
DTO类
定义用户相关的DTO:
- package com.example.blog.dto.request;
- import io.swagger.annotations.ApiModel;
- import io.swagger.annotations.ApiModelProperty;
- import lombok.Data;
- import javax.validation.constraints.Email;
- import javax.validation.constraints.NotBlank;
- import javax.validation.constraints.Size;
- @ApiModel(description = "创建用户请求")
- @Data
- public class CreateUserRequest {
- @ApiModelProperty(value = "用户名", example = "john_doe", required = true, notes = "用户名必须是唯一的,只能包含字母、数字和下划线,长度在3到20个字符之间。")
- @NotBlank
- @Size(min = 3, max = 20)
- private String username;
- @ApiModelProperty(value = "电子邮箱", example = "john@example.com", required = true, notes = "必须是有效的电子邮件地址格式。")
- @NotBlank
- @Size(max = 50)
- @Email
- private String email;
- @ApiModelProperty(value = "密码", example = "password123", required = true, notes = "密码必须至少包含6个字符。")
- @NotBlank
- @Size(min = 6, max = 100)
- private String password;
- }
复制代码- package com.example.blog.dto.response;
- import io.swagger.annotations.ApiModel;
- import io.swagger.annotations.ApiModelProperty;
- import lombok.Data;
- import java.time.LocalDateTime;
- import java.util.Set;
- @ApiModel(description = "用户响应")
- @Data
- public class UserResponse {
- @ApiModelProperty(value = "用户ID", example = "1", readOnly = true)
- private Long id;
- @ApiModelProperty(value = "用户名", example = "john_doe")
- private String username;
- @ApiModelProperty(value = "电子邮箱", example = "john@example.com")
- private String email;
- @ApiModelProperty(value = "创建时间", example = "2023-01-01T00:00:00", readOnly = true)
- private LocalDateTime createdAt;
- @ApiModelProperty(value = "更新时间", example = "2023-01-01T00:00:00", readOnly = true)
- private LocalDateTime updatedAt;
- @ApiModelProperty(value = "用户角色", example = "["ROLE_USER"]")
- private Set<String> roles;
- }
复制代码
定义文章相关的DTO:
- package com.example.blog.dto.request;
- import io.swagger.annotations.ApiModel;
- import io.swagger.annotations.ApiModelProperty;
- import lombok.Data;
- import javax.validation.constraints.NotBlank;
- import javax.validation.constraints.Size;
- @ApiModel(description = "创建文章请求")
- @Data
- public class CreatePostRequest {
- @ApiModelProperty(value = "文章标题", example = "如何使用Swagger创建API文档", required = true, notes = "标题长度在1到100个字符之间。")
- @NotBlank
- @Size(min = 1, max = 100)
- private String title;
- @ApiModelProperty(value = "文章内容", example = "Swagger是一个强大的API文档工具...", required = true, notes = "内容长度在1到5000个字符之间。")
- @NotBlank
- @Size(min = 1, max = 5000)
- private String content;
- }
复制代码- package com.example.blog.dto.response;
- import io.swagger.annotations.ApiModel;
- import io.swagger.annotations.ApiModelProperty;
- import lombok.Data;
- import java.time.LocalDateTime;
- @ApiModel(description = "文章响应")
- @Data
- public class PostResponse {
- @ApiModelProperty(value = "文章ID", example = "1", readOnly = true)
- private Long id;
- @ApiModelProperty(value = "文章标题", example = "如何使用Swagger创建API文档")
- private String title;
- @ApiModelProperty(value = "文章内容", example = "Swagger是一个强大的API文档工具...")
- private String content;
- @ApiModelProperty(value = "创建时间", example = "2023-01-01T00:00:00", readOnly = true)
- private LocalDateTime createdAt;
- @ApiModelProperty(value = "更新时间", example = "2023-01-01T00:00:00", readOnly = true)
- private LocalDateTime updatedAt;
- @ApiModelProperty(value = "作者信息", readOnly = true)
- private UserResponse author;
- }
复制代码
定义评论相关的DTO:
- package com.example.blog.dto.request;
- import io.swagger.annotations.ApiModel;
- import io.swagger.annotations.ApiModelProperty;
- import lombok.Data;
- import javax.validation.constraints.NotBlank;
- import javax.validation.constraints.Size;
- @ApiModel(description = "创建评论请求")
- @Data
- public class CreateCommentRequest {
- @ApiModelProperty(value = "评论内容", example = "这篇文章写得很好!", required = true, notes = "评论内容长度在1到1000个字符之间。")
- @NotBlank
- @Size(min = 1, max = 1000)
- private String text;
- }
复制代码- package com.example.blog.dto.response;
- import io.swagger.annotations.ApiModel;
- import io.swagger.annotations.ApiModelProperty;
- import lombok.Data;
- import java.time.LocalDateTime;
- @ApiModel(description = "评论响应")
- @Data
- public class CommentResponse {
- @ApiModelProperty(value = "评论ID", example = "1", readOnly = true)
- private Long id;
- @ApiModelProperty(value = "评论内容", example = "这篇文章写得很好!")
- private String text;
- @ApiModelProperty(value = "创建时间", example = "2023-01-01T00:00:00", readOnly = true)
- private LocalDateTime createdAt;
- @ApiModelProperty(value = "更新时间", example = "2023-01-01T00:00:00", readOnly = true)
- private LocalDateTime updatedAt;
- @ApiModelProperty(value = "作者信息", readOnly = true)
- private UserResponse author;
- }
复制代码
定义登录请求DTO:
- package com.example.blog.dto.request;
- import io.swagger.annotations.ApiModel;
- import io.swagger.annotations.ApiModelProperty;
- import lombok.Data;
- import javax.validation.constraints.NotBlank;
- @ApiModel(description = "登录请求")
- @Data
- public class LoginRequest {
- @ApiModelProperty(value = "用户名或邮箱", example = "john_doe", required = true)
- @NotBlank
- private String usernameOrEmail;
- @ApiModelProperty(value = "密码", example = "password123", required = true)
- @NotBlank
- private String password;
- }
复制代码
控制器类
定义认证控制器:
- package com.example.blog.controller;
- import com.example.blog.dto.request.LoginRequest;
- import com.example.blog.security.JwtAuthenticationResponse;
- import com.example.blog.security.JwtTokenProvider;
- import io.swagger.annotations.Api;
- import io.swagger.annotations.ApiOperation;
- import io.swagger.annotations.ApiResponse;
- import io.swagger.annotations.ApiResponses;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.http.ResponseEntity;
- import org.springframework.security.authentication.AuthenticationManager;
- import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
- import org.springframework.security.core.Authentication;
- import org.springframework.security.core.context.SecurityContextHolder;
- import org.springframework.web.bind.annotation.PostMapping;
- import org.springframework.web.bind.annotation.RequestBody;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RestController;
- import javax.validation.Valid;
- @Api(tags = "认证管理")
- @RestController
- @RequestMapping("/api/auth")
- public class AuthController {
- @Autowired
- private AuthenticationManager authenticationManager;
- @Autowired
- private JwtTokenProvider tokenProvider;
- @ApiOperation(value = "用户登录", notes = "使用用户名/邮箱和密码进行登录,成功后返回JWT令牌。")
- @ApiResponses(value = {
- @ApiResponse(code = 200, message = "登录成功", response = JwtAuthenticationResponse.class),
- @ApiResponse(code = 400, message = "无效的输入数据"),
- @ApiResponse(code = 401, message = "认证失败")
- })
- @PostMapping("/signin")
- public ResponseEntity<JwtAuthenticationResponse> authenticateUser(@Valid @RequestBody LoginRequest loginRequest) {
- Authentication authentication = authenticationManager.authenticate(
- new UsernamePasswordAuthenticationToken(
- loginRequest.getUsernameOrEmail(),
- loginRequest.getPassword()
- )
- );
- SecurityContextHolder.getContext().setAuthentication(authentication);
- String jwt = tokenProvider.generateToken(authentication);
- return ResponseEntity.ok(new JwtAuthenticationResponse(jwt));
- }
- }
复制代码
定义用户控制器:
- package com.example.blog.controller;
- import com.example.blog.dto.request.CreateUserRequest;
- import com.example.blog.dto.response.UserResponse;
- import com.example.blog.model.User;
- import com.example.blog.service.UserService;
- import io.swagger.annotations.Api;
- import io.swagger.annotations.ApiOperation;
- import io.swagger.annotations.ApiParam;
- import io.swagger.annotations.ApiResponse;
- import io.swagger.annotations.ApiResponses;
- import org.modelmapper.ModelMapper;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.http.HttpStatus;
- import org.springframework.http.ResponseEntity;
- import org.springframework.security.access.prepost.PreAuthorize;
- import org.springframework.web.bind.annotation.*;
- import javax.validation.Valid;
- import java.util.List;
- import java.util.stream.Collectors;
- @Api(tags = "用户管理")
- @RestController
- @RequestMapping("/api/users")
- public class UserController {
- @Autowired
- private UserService userService;
- @Autowired
- private ModelMapper modelMapper;
- @ApiOperation(value = "创建新用户", notes = "创建一个新的用户账户。")
- @ApiResponses(value = {
- @ApiResponse(code = 201, message = "用户创建成功", response = UserResponse.class),
- @ApiResponse(code = 400, message = "无效的输入数据"),
- @ApiResponse(code = 409, message = "用户名或邮箱已存在")
- })
- @PostMapping
- public ResponseEntity<UserResponse> createUser(@Valid @RequestBody CreateUserRequest createUserRequest) {
- User user = modelMapper.map(createUserRequest, User.class);
- User createdUser = userService.createUser(user);
- UserResponse userResponse = modelMapper.map(createdUser, UserResponse.class);
- return new ResponseEntity<>(userResponse, HttpStatus.CREATED);
- }
- @ApiOperation(value = "获取所有用户", notes = "获取系统中所有用户的列表。需要管理员权限。")
- @ApiResponses(value = {
- @ApiResponse(code = 200, message = "成功获取用户列表", response = UserResponse.class, responseContainer = "List"),
- @ApiResponse(code = 403, message = "没有权限访问")
- })
- @PreAuthorize("hasRole('ADMIN')")
- @GetMapping
- public ResponseEntity<List<UserResponse>> getAllUsers() {
- List<User> users = userService.getAllUsers();
- List<UserResponse> userResponses = users.stream()
- .map(user -> modelMapper.map(user, UserResponse.class))
- .collect(Collectors.toList());
- return ResponseEntity.ok(userResponses);
- }
- @ApiOperation(value = "根据ID获取用户", notes = "根据用户ID获取用户信息。")
- @ApiResponses(value = {
- @ApiResponse(code = 200, message = "成功获取用户", response = UserResponse.class),
- @ApiResponse(code = 404, message = "用户不存在")
- })
- @GetMapping("/{id}")
- public ResponseEntity<UserResponse> getUserById(
- @ApiParam(value = "用户ID", example = "1", required = true) @PathVariable Long id) {
- User user = userService.getUserById(id);
- UserResponse userResponse = modelMapper.map(user, UserResponse.class);
- return ResponseEntity.ok(userResponse);
- }
- }
复制代码
定义文章控制器:
- package com.example.blog.controller;
- import com.example.blog.dto.request.CreatePostRequest;
- import com.example.blog.dto.response.PostResponse;
- import com.example.blog.model.Post;
- import com.example.blog.model.User;
- import com.example.blog.service.PostService;
- import com.example.blog.service.UserService;
- import io.swagger.annotations.Api;
- import io.swagger.annotations.ApiOperation;
- import io.swagger.annotations.ApiParam;
- import io.swagger.annotations.ApiResponse;
- import io.swagger.annotations.ApiResponses;
- import org.modelmapper.ModelMapper;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.data.domain.Page;
- import org.springframework.data.domain.Pageable;
- import org.springframework.http.HttpStatus;
- import org.springframework.http.ResponseEntity;
- import org.springframework.security.core.Authentication;
- import org.springframework.web.bind.annotation.*;
- import javax.validation.Valid;
- import java.util.List;
- import java.util.stream.Collectors;
- @Api(tags = "文章管理")
- @RestController
- @RequestMapping("/api/posts")
- public class PostController {
- @Autowired
- private PostService postService;
- @Autowired
- private UserService userService;
- @Autowired
- private ModelMapper modelMapper;
- @ApiOperation(value = "创建新文章", notes = "创建一篇新的文章。需要用户登录。")
- @ApiResponses(value = {
- @ApiResponse(code = 201, message = "文章创建成功", response = PostResponse.class),
- @ApiResponse(code = 400, message = "无效的输入数据"),
- @ApiResponse(code = 401, message = "未登录")
- })
- @PostMapping
- public ResponseEntity<PostResponse> createPost(
- @Valid @RequestBody CreatePostRequest createPostRequest,
- Authentication authentication) {
- User author = userService.getUserByUsername(authentication.getName());
- Post post = modelMapper.map(createPostRequest, Post.class);
- post.setAuthor(author);
- Post createdPost = postService.createPost(post);
- PostResponse postResponse = modelMapper.map(createdPost, PostResponse.class);
- return new ResponseEntity<>(postResponse, HttpStatus.CREATED);
- }
- @ApiOperation(value = "获取所有文章", notes = "获取系统中所有文章的列表,支持分页。")
- @ApiResponses(value = {
- @ApiResponse(code = 200, message = "成功获取文章列表", response = PostResponse.class, responseContainer = "Page")
- })
- @GetMapping
- public ResponseEntity<Page<PostResponse>> getAllPosts(Pageable pageable) {
- Page<Post> posts = postService.getAllPosts(pageable);
- Page<PostResponse> postResponses = posts.map(post -> modelMapper.map(post, PostResponse.class));
- return ResponseEntity.ok(postResponses);
- }
- @ApiOperation(value = "根据ID获取文章", notes = "根据文章ID获取文章信息。")
- @ApiResponses(value = {
- @ApiResponse(code = 200, message = "成功获取文章", response = PostResponse.class),
- @ApiResponse(code = 404, message = "文章不存在")
- })
- @GetMapping("/{id}")
- public ResponseEntity<PostResponse> getPostById(
- @ApiParam(value = "文章ID", example = "1", required = true) @PathVariable Long id) {
- Post post = postService.getPostById(id);
- PostResponse postResponse = modelMapper.map(post, PostResponse.class);
- return ResponseEntity.ok(postResponse);
- }
- @ApiOperation(value = "更新文章", notes = "更新指定的文章。只有文章作者或管理员可以更新文章。")
- @ApiResponses(value = {
- @ApiResponse(code = 200, message = "文章更新成功", response = PostResponse.class),
- @ApiResponse(code = 400, message = "无效的输入数据"),
- @ApiResponse(code = 401, message = "未登录"),
- @ApiResponse(code = 403, message = "没有权限更新此文章"),
- @ApiResponse(code = 404, message = "文章不存在")
- })
- @PutMapping("/{id}")
- public ResponseEntity<PostResponse> updatePost(
- @ApiParam(value = "文章ID", example = "1", required = true) @PathVariable Long id,
- @Valid @RequestBody CreatePostRequest createPostRequest,
- Authentication authentication) {
- User currentUser = userService.getUserByUsername(authentication.getName());
- Post existingPost = postService.getPostById(id);
-
- // 检查用户是否有权限更新文章
- if (!existingPost.getAuthor().getId().equals(currentUser.getId()) &&
- !currentUser.getRoles().contains(User.Role.ROLE_ADMIN)) {
- return new ResponseEntity<>(HttpStatus.FORBIDDEN);
- }
-
- Post post = modelMapper.map(createPostRequest, Post.class);
- post.setId(id);
- post.setAuthor(existingPost.getAuthor());
- post.setCreatedAt(existingPost.getCreatedAt());
- Post updatedPost = postService.updatePost(post);
- PostResponse postResponse = modelMapper.map(updatedPost, PostResponse.class);
- return ResponseEntity.ok(postResponse);
- }
- @ApiOperation(value = "删除文章", notes = "删除指定的文章。只有文章作者或管理员可以删除文章。")
- @ApiResponses(value = {
- @ApiResponse(code = 204, message = "文章删除成功"),
- @ApiResponse(code = 401, message = "未登录"),
- @ApiResponse(code = 403, message = "没有权限删除此文章"),
- @ApiResponse(code = 404, message = "文章不存在")
- })
- @DeleteMapping("/{id}")
- public ResponseEntity<Void> deletePost(
- @ApiParam(value = "文章ID", example = "1", required = true) @PathVariable Long id,
- Authentication authentication) {
- User currentUser = userService.getUserByUsername(authentication.getName());
- Post post = postService.getPostById(id);
-
- // 检查用户是否有权限删除文章
- if (!post.getAuthor().getId().equals(currentUser.getId()) &&
- !currentUser.getRoles().contains(User.Role.ROLE_ADMIN)) {
- return new ResponseEntity<>(HttpStatus.FORBIDDEN);
- }
-
- postService.deletePost(id);
- return new ResponseEntity<>(HttpStatus.NO_CONTENT);
- }
- }
复制代码
定义评论控制器:
- package com.example.blog.controller;
- import com.example.blog.dto.request.CreateCommentRequest;
- import com.example.blog.dto.response.CommentResponse;
- import com.example.blog.model.Comment;
- import com.example.blog.model.Post;
- import com.example.blog.model.User;
- import com.example.blog.service.CommentService;
- import com.example.blog.service.PostService;
- import com.example.blog.service.UserService;
- import io.swagger.annotations.Api;
- import io.swagger.annotations.ApiOperation;
- import io.swagger.annotations.ApiParam;
- import io.swagger.annotations.ApiResponse;
- import io.swagger.annotations.ApiResponses;
- import org.modelmapper.ModelMapper;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.http.HttpStatus;
- import org.springframework.http.ResponseEntity;
- import org.springframework.security.core.Authentication;
- import org.springframework.web.bind.annotation.*;
- import javax.validation.Valid;
- import java.util.List;
- import java.util.stream.Collectors;
- @Api(tags = "评论管理")
- @RestController
- @RequestMapping("/api/posts/{postId}/comments")
- public class CommentController {
- @Autowired
- private CommentService commentService;
- @Autowired
- private PostService postService;
- @Autowired
- private UserService userService;
- @Autowired
- private ModelMapper modelMapper;
- @ApiOperation(value = "创建新评论", notes = "为指定文章创建一条新评论。需要用户登录。")
- @ApiResponses(value = {
- @ApiResponse(code = 201, message = "评论创建成功", response = CommentResponse.class),
- @ApiResponse(code = 400, message = "无效的输入数据"),
- @ApiResponse(code = 401, message = "未登录"),
- @ApiResponse(code = 404, message = "文章不存在")
- })
- @PostMapping
- public ResponseEntity<CommentResponse> createComment(
- @ApiParam(value = "文章ID", example = "1", required = true) @PathVariable Long postId,
- @Valid @RequestBody CreateCommentRequest createCommentRequest,
- Authentication authentication) {
- Post post = postService.getPostById(postId);
- User author = userService.getUserByUsername(authentication.getName());
- Comment comment = modelMapper.map(createCommentRequest, Comment.class);
- comment.setPost(post);
- comment.setAuthor(author);
- Comment createdComment = commentService.createComment(comment);
- CommentResponse commentResponse = modelMapper.map(createdComment, CommentResponse.class);
- return new ResponseEntity<>(commentResponse, HttpStatus.CREATED);
- }
- @ApiOperation(value = "获取文章的所有评论", notes = "获取指定文章的所有评论列表。")
- @ApiResponses(value = {
- @ApiResponse(code = 200, message = "成功获取评论列表", response = CommentResponse.class, responseContainer = "List"),
- @ApiResponse(code = 404, message = "文章不存在")
- })
- @GetMapping
- public ResponseEntity<List<CommentResponse>> getCommentsByPostId(
- @ApiParam(value = "文章ID", example = "1", required = true) @PathVariable Long postId) {
- Post post = postService.getPostById(postId);
- List<Comment> comments = commentService.getCommentsByPostId(postId);
- List<CommentResponse> commentResponses = comments.stream()
- .map(comment -> modelMapper.map(comment, CommentResponse.class))
- .collect(Collectors.toList());
- return ResponseEntity.ok(commentResponses);
- }
- @ApiOperation(value = "删除评论", notes = "删除指定的评论。只有评论作者、文章作者或管理员可以删除评论。")
- @ApiResponses(value = {
- @ApiResponse(code = 204, message = "评论删除成功"),
- @ApiResponse(code = 401, message = "未登录"),
- @ApiResponse(code = 403, message = "没有权限删除此评论"),
- @ApiResponse(code = 404, message = "评论不存在")
- })
- @DeleteMapping("/{commentId}")
- public ResponseEntity<Void> deleteComment(
- @ApiParam(value = "文章ID", example = "1", required = true) @PathVariable Long postId,
- @ApiParam(value = "评论ID", example = "1", required = true) @PathVariable Long commentId,
- Authentication authentication) {
- User currentUser = userService.getUserByUsername(authentication.getName());
- Comment comment = commentService.getCommentById(commentId);
-
- // 检查用户是否有权限删除评论
- if (!comment.getAuthor().getId().equals(currentUser.getId()) &&
- !comment.getPost().getAuthor().getId().equals(currentUser.getId()) &&
- !currentUser.getRoles().contains(User.Role.ROLE_ADMIN)) {
- return new ResponseEntity<>(HttpStatus.FORBIDDEN);
- }
-
- commentService.deleteComment(commentId);
- return new ResponseEntity<>(HttpStatus.NO_CONTENT);
- }
- }
复制代码
运行和测试
启动应用程序后,访问以下URL查看Swagger UI:
- http://localhost:8080/swagger-ui.html
复制代码
您将看到一个专业、易读的API文档,包括:
1. 认证管理
2. 用户管理
3. 文章管理
4. 评论管理
每个API都有详细的描述、参数说明、请求示例和响应示例。您可以直接在Swagger UI中测试API,只需点击”Try it out”按钮,填入参数,然后点击”Execute”。
效果展示
通过以上配置,我们实现了一个功能完整、文档完善的博客系统API。Swagger UI将显示:
1. 清晰的API分组:按照功能模块将API分为认证管理、用户管理、文章管理和评论管理四个部分。
2. 详细的API描述:每个API都有清晰的功能描述和使用说明。
3. 完整的参数说明:包括参数类型、是否必需、示例值等。
4. 丰富的请求示例:为每个API提供了符合规范的请求示例。
5. 详细的响应说明:包括不同HTTP状态码的含义和对应的响应结构。
6. 认证集成:支持JWT认证,可以在Swagger UI中直接输入令牌进行测试。
这样的API文档不仅对前端开发者友好,也便于后端开发者进行测试和维护,大大提高了开发效率。
总结
本文详细介绍了如何使用Swagger配置请求示例,打造专业、易读的API文档,从而提升开发效率。我们从Swagger的基础概念入手,逐步深入到环境搭建、基础配置、注解详解、请求示例配置、高级功能、最佳实践和实战案例,全面覆盖了Swagger的使用技巧。
通过本文的学习,您应该能够:
1. 在项目中集成Swagger并配置基本参数。
2. 使用Swagger注解丰富API文档的内容。
3. 添加请求和响应示例,提高API文档的实用性。
4. 利用Swagger的高级功能,如API分组、自定义UI等。
5. 遵循最佳实践,创建高质量的API文档。
6. 通过实战案例,掌握Swagger在实际项目中的应用。
Swagger作为一款强大的API文档工具,不仅能够自动生成文档,还能提供交互式的API测试功能,大大简化了API开发和测试的流程。通过合理使用Swagger,您可以创建专业、易读的API文档,提高团队协作效率,加速项目开发进程。
随着API经济的不断发展,API文档的重要性将日益凸显。掌握Swagger等API文档工具,将成为现代软件开发者的必备技能。希望本文能够帮助您更好地使用Swagger,打造出高质量的API文档,提升开发效率和用户体验。 |
|