Using OpenAPI and Swagger UI
本指南介绍了您的 Quarkus 应用程序如何通过 OpenAPI 规范显示其 API 说明,以及您如何通过名为 Swagger UI 的用户友好 UI 对其进行测试。
- Prerequisites
- Architecture
- Solution
- Creating the Maven project
- Expose a REST Resource
- Expose OpenAPI Specifications
- Providing Application Level OpenAPI Annotations
- Enhancing the OpenAPI Schema with Filters
- Loading OpenAPI Schema From Static Files
- Changing the OpenAPI version
- Auto-generation of Operation Id
- Use Swagger UI for development
- Configuration Reference
Prerequisites
如要完成本指南,您需要:
-
Roughly 15 minutes
-
An IDE
-
安装了 JDK 17+,已正确配置
JAVA_HOME
-
Apache Maven ${proposed-maven-version}
-
如果你想使用 Quarkus CLI, 则可以选择使用
-
如果你想构建一个本机可执行文件(或如果你使用本机容器构建,则使用 Docker),则可以选择安装 Mandrel 或 GraalVM 以及 configured appropriately
Solution
我们建议你按照后续章节中的说明,逐步创建应用程序。但是,你可以直接跳到已完成的示例。
克隆 Git 存储库: git clone $${quickstarts-base-url}.git
,或下载 $${quickstarts-base-url}/archive/main.zip[存档]。
此解决方案位于 openapi-swaggerui-quickstart
directory。
Creating the Maven project
首先,我们需要一个新项目。使用以下命令创建一个新项目:
quarkus create app {create-app-group-id}:{create-app-artifact-id} \
--no-code
cd {create-app-artifact-id}
要创建一个 Gradle 项目,添加 --gradle
或 --gradle-kotlin-dsl
选项。
有关如何安装和使用 Quarkus CLI 的详细信息,请参见 Quarkus CLI 指南。
mvn {quarkus-platform-groupid}:quarkus-maven-plugin:{quarkus-version}:create \
-DprojectGroupId={create-app-group-id} \
-DprojectArtifactId={create-app-artifact-id} \
-DnoCode
cd {create-app-artifact-id}
要创建一个 Gradle 项目,添加 -DbuildTool=gradle
或 -DbuildTool=gradle-kotlin-dsl
选项。
适用于 Windows 用户:
-
如果使用 cmd,(不要使用反斜杠
\
,并将所有内容放在同一行上) -
如果使用 Powershell,将
-D
参数用双引号引起来,例如"-DprojectArtifactId={create-app-artifact-id}"
Expose a REST Resource
我们将创建 Fruit
bean 和 FruitResource
REST 资源(如果您想了解有关如何使用 Quarkus 构建 REST API 的更多详细信息,请随时查阅 Writing JSON REST services guide)。
package org.acme.openapi.swaggerui;
public class Fruit {
public String name;
public String description;
public Fruit() {
}
public Fruit(String name, String description) {
this.name = name;
this.description = description;
}
}
package org.acme.openapi.swaggerui;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.Path;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Set;
@Path("/fruits")
public class FruitResource {
private Set<Fruit> fruits = Collections.newSetFromMap(Collections.synchronizedMap(new LinkedHashMap<>()));
public FruitResource() {
fruits.add(new Fruit("Apple", "Winter fruit"));
fruits.add(new Fruit("Pineapple", "Tropical fruit"));
}
@GET
public Set<Fruit> list() {
return fruits;
}
@POST
public Set<Fruit> add(Fruit fruit) {
fruits.add(fruit);
return fruits;
}
@DELETE
public Set<Fruit> delete(Fruit fruit) {
fruits.removeIf(existingFruit -> existingFruit.name.contentEquals(fruit.name));
return fruits;
}
}
您还可以创建一个测试:
package org.acme.openapi.swaggerui;
import io.quarkus.test.junit.QuarkusTest;
import org.junit.jupiter.api.Test;
import jakarta.ws.rs.core.MediaType;
import static io.restassured.RestAssured.given;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.containsInAnyOrder;
@QuarkusTest
public class FruitResourceTest {
@Test
public void testList() {
given()
.when().get("/fruits")
.then()
.statusCode(200)
.body("$.size()", is(2),
"name", containsInAnyOrder("Apple", "Pineapple"),
"description", containsInAnyOrder("Winter fruit", "Tropical fruit"));
}
@Test
public void testAdd() {
given()
.body("{\"name\": \"Pear\", \"description\": \"Winter fruit\"}")
.header("Content-Type", MediaType.APPLICATION_JSON)
.when()
.post("/fruits")
.then()
.statusCode(200)
.body("$.size()", is(3),
"name", containsInAnyOrder("Apple", "Pineapple", "Pear"),
"description", containsInAnyOrder("Winter fruit", "Tropical fruit", "Winter fruit"));
given()
.body("{\"name\": \"Pear\", \"description\": \"Winter fruit\"}")
.header("Content-Type", MediaType.APPLICATION_JSON)
.when()
.delete("/fruits")
.then()
.statusCode(200)
.body("$.size()", is(2),
"name", containsInAnyOrder("Apple", "Pineapple"),
"description", containsInAnyOrder("Winter fruit", "Tropical fruit"));
}
}
Expose OpenAPI Specifications
Quarkus 提供符合 MicroProfile OpenAPI 规范的 Smallrye OpenAPI 扩展,以便生成您的 API OpenAPI v3 specification。
您只需要将 openapi
扩展添加到您的 Quarkus 应用程序:
quarkus extension add {add-extension-extensions}
./mvnw quarkus:add-extension -Dextensions='{add-extension-extensions}'
./gradlew addExtension --extensions='{add-extension-extensions}'
这会将以下内容添加到构建文件中:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-openapi</artifactId>
</dependency>
implementation("io.quarkus:quarkus-smallrye-openapi")
现在,我们准备运行我们的应用程序:
quarkus dev
./mvnw quarkus:dev
./gradlew --console=plain quarkusDev
应用程序启动后,您可以向默认 /q/openapi
终结点发出请求:
$ curl http://localhost:8080/q/openapi
openapi: 3.0.3
info:
title: Generated API
version: "1.0"
paths:
/fruits:
get:
responses:
200:
description: OK
content:
application/json: {}
post:
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Fruit'
responses:
200:
description: OK
content:
application/json: {}
delete:
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Fruit'
responses:
200:
description: OK
content:
application/json: {}
components:
schemas:
Fruit:
properties:
description:
type: string
name:
type: string
如果您不喜欢默认终结点位置
|
你可以使用 `format`查询参数申请 JSON 格式的 OpenAPI。例如:
|
按 CTRL+C
停止应用程序。
Providing Application Level OpenAPI Annotations
有一些 MicroProfile OpenAPI 注解描述了全局 API 信息,例如以下这些:
-
API Title
-
API Description
-
Version
-
Contact Information
-
License
所有这些信息(以及更多信息)都可以通过在 Jakarta REST Application`类中使用相应的 OpenAPI 注解包含在你的 Java 代码中。由于 Quarkus 中不需要 Jakarta REST `Application`类,你可能会需要创建一个。它可以简单地是一个扩展 `jakarta.ws.rs.core.Application`的空类。此空类随后可以使用各种 OpenAPI 注解(例如 `@OpenAPIDefinition
)进行注解。例如:
@OpenAPIDefinition(
tags = {
@Tag(name="widget", description="Widget operations."),
@Tag(name="gasket", description="Operations related to gaskets")
},
info = @Info(
title="Example API",
version = "1.0.1",
contact = @Contact(
name = "Example API Support",
url = "http://exampleurl.com/contact",
email = "techsupport@example.com"),
license = @License(
name = "Apache 2.0",
url = "https://www.apache.org/licenses/LICENSE-2.0.html"))
)
public class ExampleApiApplication extends Application {
}
另一个选择是使用配置添加此全局 API 信息,这是 SmallRye 提供的一项功能,不属于规范的一部分。通过这种方式,你无需使用 Jakarta REST `Application`类,并且可以针对每个环境对 API 采用不同的命名。
例如,将以下内容添加到 `application.properties`中:
quarkus.smallrye-openapi.info-title=Example API
%dev.quarkus.smallrye-openapi.info-title=Example API (development)
%test.quarkus.smallrye-openapi.info-title=Example API (test)
quarkus.smallrye-openapi.info-version=1.0.1
quarkus.smallrye-openapi.info-description=Just an example service
quarkus.smallrye-openapi.info-terms-of-service=Your terms here
quarkus.smallrye-openapi.info-contact-email=techsupport@example.com
quarkus.smallrye-openapi.info-contact-name=Example API Support
quarkus.smallrye-openapi.info-contact-url=http://exampleurl.com/contact
quarkus.smallrye-openapi.info-license-name=Apache 2.0
quarkus.smallrye-openapi.info-license-url=https://www.apache.org/licenses/LICENSE-2.0.html
这将提供与上述 `@OpenAPIDefinition`示例类似的信息。
Enhancing the OpenAPI Schema with Filters
你可以使用一个或多个过滤器更改生成的 OpenAPI 模式。这些过滤器可以在构建时或运行时运行。
/**
* Filter to add custom elements
*/
@OpenApiFilter(OpenApiFilter.RunStage.BUILD) (1)
public class MyBuildTimeFilter implements OASFilter { (2)
private IndexView view;
public MyBuildTimeFilter(IndexView view) { (3)
this.view = view;
}
@Override
public void filterOpenAPI(OpenAPI openAPI) { (4)
Collection<ClassInfo> knownClasses = this.view.getKnownClasses();
Info info = OASFactory.createInfo();
info.setDescription("Created from Annotated Buildtime filter with " + knownClasses.size() + " known indexed classes");
openAPI.setInfo(info);
}
}
1 | 使用 `@OpenApiFilter`和运行阶段 (BUILD、RUN、BOTH) 标注方法 |
2 | 实现 OASFilter 并覆盖任何方法 |
3 | 对于构建阶段过滤器,索引可以传入(可选) |
4 | 获取生成的 `OpenAPI`模式,并根据需要进行优化 |
请记住,在模式上设置字段将覆盖所生成的模式,你可能会想要获取和添加到(即修改)。还要注意,生成的值可能是空值,因此你必须进行检查。
Loading OpenAPI Schema From Static Files
除了从注解扫描中动态创建 OpenAPI 模式外,Quarkus 还支持提供静态 OpenAPI 文档。要提供的静态文件必须是符合 OpenAPI specification的有效文档。符合 OpenAPI 规范的 OpenAPI 文档本身就是一个有效的 JSON 对象,可以用 `yaml`或 `json`格式表示。
为了在操作中看到这一点,我们会在 `META-INF/openapi.yaml`中为 `/fruits`端点放置 OpenAPI 文档。如果你喜欢的话,Quarkus 也支持备用 OpenAPI document paths。
openapi: 3.0.1
info:
title: Static OpenAPI document of fruits resource
description: Fruit resources Open API documentation
version: "1.0"
servers:
- url: http://localhost:8080/q/openapi
description: Optional dev mode server description
paths:
/fruits:
get:
responses:
200:
description: OK - fruits list
content:
application/json: {}
post:
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Fruit'
responses:
200:
description: new fruit resource created
content:
application/json: {}
delete:
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Fruit'
responses:
200:
description: OK - fruit resource deleted
content:
application/json: {}
components:
schemas:
Fruit:
properties:
description:
type: string
name:
type: string
默认情况下,对 `/q/openapi`发出的请求将提供来自静态文件和应用程序端点代码生成的模型的组合 OpenAPI 文档。但是,可以通过在 `application.properties`中添加 `mp.openapi.scan.disable=true`配置将此项更改为仅提供静态 OpenAPI 文档。
现在,对 `/q/openapi`端点发出的请求将提供静态 OpenAPI 文档,而不是生成的文档。
About OpenAPI document paths
Quarkus 支持多种途径来存储 OpenAPI 文档。我们建议你将其放在
在开发过程中支持静态 OpenAPI 文档的实时重新加载。Quarkus 将实时获取对你 OpenAPI 文档的修改。 |
Changing the OpenAPI version
默认情况下,当文档生成时,将使用 OpenAPI 版本 3.0.3
。如果你使用上面提到的静态文件,将会使用该文件中的版本。你还可以使用以下配置在 SmallRye 中定义版本:
mp.openapi.extensions.smallrye.openapi=3.0.2
如果你的 API 通过需要特定版本的网关,这可能很有用。
Auto-generation of Operation Id
Operation Id 可以使用 @Operation
注释进行设置,并且在使用工具从架构生成客户端存根时,在许多情况下很有用。操作 ID 通常用于客户端存根中的方法名。在 SmallRye 中,你可以使用以下配置自动生成此操作 ID:
mp.openapi.extensions.smallrye.operationIdStrategy=METHOD
现在,你无需使用 @Operation
注释。生成文档时,方法名将用于操作 ID。
Property value | Description |
---|---|
|
Use the method name. |
|
使用类名(不带包)加上方法。 |
|
使用类名加上方法名。 |
Use Swagger UI for development
在构建 API 时,开发人员希望快速测试它们。 Swagger UI 是一个非常棒的工具,允许可视化和与你的 API 交互。用户界面会自动从 OpenAPI 规范中生成。
Quarkus smallrye-openapi
扩展附带了一个 swagger-ui
扩展,该扩展嵌入了正确配置的 Swagger UI 页面。
默认情况下,Swagger UI 仅在以开发或测试模式启动 Quarkus 时可用。
如果你也想在生产环境中使用,则可以在你的
这是一个构建时属性,在应用程序构建后无法在运行时更改。 |
默认情况下,可以在 /q/swagger-ui
访问 Swagger UI。
你可以通过在 application.properties
中设置 quarkus.swagger-ui.path
属性来更新 /swagger-ui
子路径:
quarkus.swagger-ui.path=my-custom-path
不允许值 /
,因为它会阻止应用程序提供任何其他服务。以 '/' 为前缀的值使其成为绝对路径,而不是相对路径。
现在,我们准备运行我们的应用程序:
./mvnw compile quarkus:dev
你可以在应用程序的日志中检查 Swagger UI 路径:
00:00:00,000 INFO [io.qua.swa.run.SwaggerUiServletExtension] Swagger UI available at /q/swagger-ui
应用程序启动后,您可以转到 [role="bare"][role="bare"]http://localhost:8080/q/swagger-ui 并操作您的 API。
您可以查看 API 的操作和架构。
您还可以与您的 API 交互,以便快速测试它。
按 CTRL+C
停止应用程序。
Styling
您可以通过提供自己的 logo 和 css 来自定义 swagger ui 的样式。
Logo
要提供您自己的 logo,您需要将一个名为 logo.png
的文件放到 src/main/resources/META-INF/branding
中。
这将为所有 UI(不仅仅是 swagger ui)设置 logo,因此在这种情况下还包括 GraphQL-UI 和 Health-UI(如果包含)。
如果您只想将此 logo 应用于 swagger-ui(而不是全局应用于所有 UI),则将该文件称为 smallrye-open-api-ui.png
而不是 logo.png
。
CSS
要提供您自己的 css 来替代/增强 html 中的样式,您需要将一个名为 style.css
的文件放到 src/main/resources/META-INF/branding
中。
这会将该 css 添加到所有 UI(不仅仅是 swagger ui),因此在这种情况下还包括 GraphQL-UI 和 Health-UI(如果包含)。
如果您只想将此样式应用于 swagger-ui(而不是全局应用于所有 UI),则将该文件称为 smallrye-open-api-ui.css
而不是 style.css
。
有关样式的更多信息,请阅读此博客文章:[role="bare"][role="bare"]https://quarkus.io/blog/stylish-api/
Cross Origin Resource Sharing
如果您计划在运行于不同域上的单页面应用程序中使用此应用程序,您将需要配置 CORS(跨域资源共享)。请阅读“跨域资源共享”指南的 CORS filter 部分,以获取更多详细信息。