- Swagger是一个规范和完整的框架,用于生成、描述、调用和可视化RESTful风格的Web服务
- 它使得前后端分离开发更加方便,有利于团队协作
- 起始与定位:Swagger最初由Tony Tam在2011年创建,旨在作为一个简单的API文档生成工具
- 核心功能:通过对JAX-RS和Jersey注解的支持,
Swagger 1.x
能够自动生成API文档,使得API文档的维护变得更加容易。在这个阶段,Swagger还没有完全成熟,主要支持基本的API描述和文档生成
- 重大变革:
Swagger 2.x
发生了重大变化,从单一的文档生成工具演变为一个完整的API开发和管理平台 - 引入了强大的注解支持,可以描述API的细节信息。eg:请求参数、返回类型等
- 定义了
RESTful API
的元数据。eg:API描述、标签等 - 引入了OpenAPI规范(原名Swagger规范),为API定义提供了更严格的标准和规则
- 被称为
Swagger 3.x
- 规范更名:在2017年,
Swagger 2.x
的规范被捐赠给Linux基金会,并正式更名为OpenAPI规范 - 发展与普及:OpenAPI规范不仅继承了
Swagger 2.x
的特性,还提供了更加全面和严格的API定义规范,并且扩展了对非RESTful API的支持。随着OpenAPI规范的普及,越来越多的API开发者开始使用Swagger/OpenAPI来开发、测试和文档化他们的RESTful API
- 工具与服务:OpenAPI规范采用JSON或YAML格式编写,并支持多种数据类型。基于OpenAPI规范,开发了许多工具和服务
- eg:
Swagger UI
、Swagger Codegen
、SwaggerHub
等,进一步扩展了Swagger的功能,使其成为了一个更加完整、强大和易于使用的API定义和管理平台
- Springdoc OpenAPI 1.x
- JDK支持:支持JDK 8及以上版本
- SpringBoot支持:适用于
Spring Boot 2.x
及更早版本
- Springdoc OpenAPI 2.x
- JDK支持:最新版本要求JDK 11及以上
- SpringBoot支持:专为
Spring Boot 3.x
设计
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
<version>1.6.2</version>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.7.0</version>
</dependency>
@Configuration
public class OpenApiConfig extends WebMvcConfigurerAdapter {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new LocalDateConverter(DatePattern.NORM_DATE_PATTERN));
registry.addConverter(new LocalDateTimeConverter(DatePattern.UTC_MS_PATTERN));
}
@Bean
public GroupedOpenApi userApi() {
return GroupedOpenApi.builder()
.group("UserApi")
.packagesToScan("com.ruoyi.open_api")
.addOpenApiMethodFilter(method -> method.isAnnotationPresent(Operation.class))
.build();
}
@Bean
public GroupedOpenApi courseApi() {
return GroupedOpenApi.builder()
.group("CourseApi")
.packagesToScan("com.ruoyi.course.controller")
.build();
}
@Bean
public OpenAPI openApi() {
return new OpenAPI()
.info(new Info()
.title("Swagger Petstore - OpenAPI 3.0")
.description("This is a sample Pet Store Server based on the OpenAPI 3.0 specification. You can find out more about Swagger at [https://swagger.io](https://swagger.io). In the third iteration of the pet store, we've switched to the design first approach! You can now help us improve the API whether it's by making changes to the definition itself or to the code. That way, with time, we can improve the API in general, and expose some of the new features in OAS3. _If you're looking for the Swagger 2.0/OAS 2.0 version of Petstore, then click [here](https://editor.swagger.io/?url=https://petstore.swagger.io/v2/swagger.yaml). Alternatively, you can load via the `Edit > Load Petstore OAS 2.0` menu option!_ Some useful links: - [The Pet Store repository](https://github.com/swagger-api/swagger-petstore) - [The source API definition for the Pet Store](https://github.com/swagger-api/swagger-petstore/blob/master/src/main/resources/openapi.yaml)")
.termsOfService("")
.version("1.0.11")
.license(new License()
.name("Apache 2.0")
.url("http://www.apache.org/licenses/LICENSE-2.0.html"))
.contact(new Contact()
.email("apiteam@swagger.io"))
)
.servers(Arrays.asList(
new Server().url("http://localhost:7071").description("本地环境"),
new Server().url("http://listao.cn:20015/prod-api").description("生产环境")
));
}
}
class LocalDateConverter implements Converter<String, LocalDate> {
private final DateTimeFormatter formatter;
public LocalDateConverter(String dateFormat) {
this.formatter = DateTimeFormatter.ofPattern(dateFormat);
}
@Override
public LocalDate convert(String source) {
if (source.isEmpty()) {
return null;
}
return LocalDate.parse(source, this.formatter);
}
}
class LocalDateTimeConverter implements Converter<String, LocalDateTime> {
private final DateTimeFormatter formatter;
public LocalDateTimeConverter(String dateFormat) {
this.formatter = DateTimeFormatter.ofPattern(dateFormat);
}
@Override
public LocalDateTime convert(String source) {
if (source.isEmpty()) {
return null;
}
return LocalDateTime.parse(source, this.formatter);
}
}
springdoc:
api-docs:
path: /api-docs
swagger-ui:
disable-swagger-default-url: true
group-configs:
- group: UserApi
packages-to-scan: com.ruoyi.open_api
paths-to-match: /api/user/*
- group: CourseApi
paths-to-match: /api/course/*
packages-to-scan: com.ruoyi.course.controller
- 直接带端口访问后端:http://localhost:7071/swagger-ui/index.html
server: {
port: 80,
host: true,
open: true,
proxy: {
'/dev-api': {
target: 'http:
changeOrigin: true,
rewrite: (p) => p.replace(/^\/dev-api/, '')
},
'/api-docs': {
target: 'http:
changeOrigin: true,
}
}
},
# RuoYi官网的配置
server {
listen 18082;
# 指定前端项目所在的位置
location / {
root /etc/nginx/ruoyi;
try_files $uri $uri/ /index.html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
location /prod-api/ {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http:
}
location /api-docs/ {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http:
}
}
- 删除example即向后端传递
空String
- 再取消
Send empty value
勾选,即null
- 插入、更新id处理
- 关闭
required
- 进行分组校验
- 接口描述:
新增不为空, 更新为空
@NotBlank(message = "id为空", groups = Validd.upd.class)
@Null(message = "id不为空", groups = Validd.ins.class)
@Schema(example = "10", description = "主键, 新增不为空, 更新为空",
minimum = "1", maximum = "33",
requiredMode = Schema.RequiredMode.NOT_REQUIRED,
nullable = true
)
@JsonProperty("id")
private String id;
application/json
:入参、出参,都进行控制
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
application/x-www-form-urlencoded
:入参进行控制,LocalDate, LocalDateTime
@Configuration
public class OpenApiConfig extends WebMvcConfigurerAdapter {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new LocalDateConverter(DatePattern.NORM_DATE_PATTERN));
registry.addConverter(new LocalDateTimeConverter(DatePattern.UTC_MS_PATTERN));
}
}
class LocalDateConverter implements Converter<String, LocalDate> {
private final DateTimeFormatter formatter;
public LocalDateConverter(String dateFormat) {
this.formatter = DateTimeFormatter.ofPattern(dateFormat);
}
@Override
public LocalDate convert(String source) {
if (source.isEmpty()) {
return null;
}
return LocalDate.parse(source, this.formatter);
}
}
class LocalDateTimeConverter implements Converter<String, LocalDateTime> {
private final DateTimeFormatter formatter;
public LocalDateTimeConverter(String dateFormat) {
this.formatter = DateTimeFormatter.ofPattern(dateFormat);
}
@Override
public LocalDateTime convert(String source) {
if (source.isEmpty()) {
return null;
}
return LocalDateTime.parse(source, this.formatter);
}
}
@Future(message = "日期必须为将来日期")
@Schema(example = "2099-09-09T06:32:52.415Z", description = "将来日期")
private LocalDateTime futureDate;
@Valid
@Schema(description = "嵌套对象", nullable = true)
private InObj inObj;
@Tag(name = "CRUD")
@RestController
@RequestMapping("/openApiCtl")
public class OpenApiCtl {
private final static Map<String, OpenApi> users = new LinkedHashMap<>();
{
users.put("1", new OpenApi("1", "name1"));
users.put("2", new OpenApi("2", "name2"));
}
@Operation(summary = "查询user集合")
@GetMapping("/list")
public R<List<OpenApi>> userList() {
List<OpenApi> userList = new ArrayList<>(users.values());
return R.ok(userList);
}
@Operation(summary = "userId查询user")
@GetMapping("/{userId}")
public R<OpenApi> getUser(@PathVariable Integer userId) {
return R.ok(users.get(userId));
}
@Operation(summary = "新增user", description = "inObj置null")
@PostMapping(value = "/save", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public R<OpenApi> save(@Parameter @Validated({Validd.ins.class, Default.class}) OpenApi user) {
users.put(user.getId(), user);
return R.ok(user);
}
@Operation(summary = "更新user", description = "id不为空,其他属性非null更新")
@PutMapping("/update")
public R<OpenApi> update(@RequestBody @Validated({Validd.upd.class, Default.class}) OpenApi user) {
users.remove(user.getId());
users.put(user.getId(), user);
return R.ok(user);
}
@Operation(summary = "删除user")
@DeleteMapping("/{userId}")
public R<String> delete(@PathVariable Integer userId) {
users.remove(userId);
return R.ok();
}
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class OpenApi {
@NotBlank(message = "id为空", groups = Validd.upd.class)
@Null(message = "id不为空", groups = Validd.ins.class)
@Schema(example = "10", description = "主键, 新增不为空, 更新为空",
minimum = "1", maximum = "33",
requiredMode = Schema.RequiredMode.NOT_REQUIRED,
nullable = true
)
@JsonProperty("id")
private String id;
@NotBlank(message = "username为空", groups = Validd.ins.class)
@Schema(example = "Danny", description = "user名称",
requiredMode = Schema.RequiredMode.REQUIRED,
minLength = 2, maxLength = 33
)
@JsonProperty("username")
private String username;
@Schema(example = "John", description = "名")
@JsonProperty("firstName")
private String firstName;
@Schema(example = "James", description = "姓")
@JsonProperty("lastName")
private String lastName;
@NotBlank(message = "邮箱不能为空", groups = Validd.ins.class)
@Pattern(regexp = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$", message = "邮箱格式不正确")
@Schema(example = "john@email.com", description = "邮箱")
@JsonProperty("email")
private String email;
@NotBlank(message = "密码不能为空", groups = Validd.ins.class)
@Size(min = 6, max = 16, message = "密码长度必须在6到16个字符之间")
@Schema(example = "******", description = "密码")
@JsonProperty("password")
private String password;
@NotBlank(message = "mobile不能为空", groups = Validd.ins.class)
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "无效的手机号码格式")
@Schema(example = "19910769053", description = "手机号")
@JsonProperty("mobile")
private String mobile;
@Schema(example = "1", description = "用户状态")
@JsonProperty("userStatus")
private Integer userStatus;
@URL(message = "url格式错误")
@Schema(example = "http://listao.site", description = "网站地址")
private String url;
@Digits(integer = 4, fraction = 2, message = "整数位数必须在4位以内小数位数必须在2位以内")
@Schema(description = "小数", example = "1.3")
private Double num;
@Past(message = "日期必须为过去日期")
@Schema(example = "2023-12-12", description = "过去日期")
private LocalDate pastDate;
@Future(message = "日期必须为将来日期")
@Schema(example = "2099-09-09T06:32:52.415Z", description = "将来日期")
private LocalDateTime futureDate;
@Valid
@Schema(description = "嵌套对象", nullable = true)
private InObj inObj;
public OpenApi(String id, String username) {
this.id = id;
this.username = username;
}
}
@Data
class InObj {
@NotBlank(message = "oxId不能为空")
@Schema(description = "oxId主键", example = "ooxxId")
private String id;
}