Using OpenAPI and Swagger UI
本指南介绍了您的 Quarkus 应用程序如何通过 OpenAPI 规范显示其 API 说明,以及您如何通过名为 Swagger UI 的用户友好 UI 对其进行测试。
This guide explains how your Quarkus application can expose its API description through an OpenAPI specification and how you can test it via a user-friendly UI named Swagger UI.
Prerequisites
Unresolved directive in openapi-swaggerui.adoc - include::{includes}/prerequisites.adoc[]
Architecture
在本指南中,我们将创建一个简单的 REST 应用程序,以展示如何快速显示您的 API 规范以及如何从用户界面中受益以对其进行测试。
In this guide, we create a straightforward REST application to demonstrate how fast you can expose your API specification and benefit from a user interface to test it.
Solution
我们建议你按照后续章节中的说明,逐步创建应用程序。但是,你可以直接跳到已完成的示例。
We recommend that you follow the instructions in the next sections and create the application step by step. However, you can skip right to the completed example.
克隆 Git 存储库: git clone {quickstarts-clone-url}
,或下载 {quickstarts-archive-url}[存档]。
Clone the Git repository: git clone {quickstarts-clone-url}
, or download an {quickstarts-archive-url}[archive].
此解决方案位于 openapi-swaggerui-quickstart
directory。
The solution is located in the openapi-swaggerui-quickstart
directory.
Creating the Maven project
首先,我们需要一个新项目。使用以下命令创建一个新项目:
First, we need a new project. Create a new project with the following command:
Unresolved directive in openapi-swaggerui.adoc - include::{includes}/devtools/create-app.adoc[]
Expose a REST Resource
我们将创建 Fruit
bean 和 FruitResource
REST 资源(如果您想了解有关如何使用 Quarkus 构建 REST API 的更多详细信息,请随时查阅 Writing JSON REST services guide)。
We will create a Fruit
bean and a FruitResource
REST resource
(feel free to take a look to the Writing JSON REST services guide if your want more details on how to build a REST API with Quarkus).
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;
}
}
您还可以创建一个测试:
You can also create a test:
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。
Quarkus provides the Smallrye OpenAPI extension compliant with the MicroProfile OpenAPI specification in order to generate your API OpenAPI v3 specification.
您只需要将 openapi
扩展添加到您的 Quarkus 应用程序:
You just need to add the openapi
extension to your Quarkus application:
Unresolved directive in openapi-swaggerui.adoc - include::{includes}/devtools/extension-add.adoc[]
这会将以下内容添加到构建文件中:
This will add the following to your build file:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-openapi</artifactId>
</dependency>
implementation("io.quarkus:quarkus-smallrye-openapi")
现在,我们准备运行我们的应用程序:
Now, we are ready to run our application:
Unresolved directive in openapi-swaggerui.adoc - include::{includes}/devtools/dev.adoc[]
应用程序启动后,您可以向默认 /q/openapi
终结点发出请求:
Once your application is started, you can make a request to the default /q/openapi
endpoint:
$ 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
如果您不喜欢默认终结点位置 If you do not like the default endpoint location
|
你可以使用 `format`查询参数申请 JSON 格式的 OpenAPI。例如: You can request the OpenAPI in JSON format using the
|
按 CTRL+C
停止应用程序。
Hit CTRL+C
to stop the application.
Providing Application Level OpenAPI Annotations
有一些 MicroProfile OpenAPI 注解描述了全局 API 信息,例如以下这些:
There are some MicroProfile OpenAPI annotations which describe global API information, such as the following:
-
API Title
-
API Description
-
Version
-
Contact Information
-
License
所有这些信息(以及更多信息)都可以通过在 Jakarta REST Application`类中使用相应的 OpenAPI 注解包含在你的 Java 代码中。由于 Quarkus 中不需要 Jakarta REST `Application`类,你可能会需要创建一个。它可以简单地是一个扩展 `jakarta.ws.rs.core.Application`的空类。此空类随后可以使用各种 OpenAPI 注解(例如 `@OpenAPIDefinition
)进行注解。例如:
All of this information (and more) can be included in your Java code by using appropriate OpenAPI annotations
on a Jakarta REST Application
class. Because a Jakarta REST Application
class is not required in Quarkus, you will
likely have to create one. It can simply be an empty class that extends jakarta.ws.rs.core.Application
. This
empty class can then be annotated with various OpenAPI annotations such as @OpenAPIDefinition
. For example:
@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 采用不同的命名。
Another option, that is a feature provided by SmallRye and not part of the specification, is to use configuration to add this global API information.
This way, you do not need to have a Jakarta REST Application
class, and you can name the API differently per environment.
例如,将以下内容添加到 `application.properties`中:
For example, add the following to your 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`示例类似的信息。
This will give you similar information as the @OpenAPIDefinition
example above.
Enhancing the OpenAPI Schema with Filters
你可以使用一个或多个过滤器更改生成的 OpenAPI 模式。这些过滤器可以在构建时或运行时运行。
You can change the Generated OpenAPI Schema using one or more filter. Filters can run during build time, or at runtime.
/**
* 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 | Annotate method with @OpenApiFilter and the run stage (BUILD,RUN,BOTH) |
2 | Implement OASFilter and override any of the methods |
3 | For Build stage filters, the index can be passed in (optional) |
4 | Get a hold of the generated OpenAPI Schema, and enhance as required |
请记住,在模式上设置字段将覆盖所生成的模式,你可能会想要获取和添加到(即修改)。还要注意,生成的值可能是空值,因此你必须进行检查。
Remember that setting fields on the schema will override what has been generated, you might want to get and add to (so modify). Also know that the generated values might be null, so you will have to check for that.
Runtime filters
默认情况下,运行时过滤器在启动时运行(在最终的 OpenAPI 文档创建时)。你可以更改运行时过滤器,使其在每次请求时运行,从而使 openapi 文档具有动态性。要执行此操作,你需要设置以下属性: quarkus.smallrye-openapi.always-run-filter=true
。
Runtime filters by default runs on startup (when the final OpenAPI document gets created). You can change runtime filters to run on every request, making the openapi document dynamic.
To do this you need to set this propery: quarkus.smallrye-openapi.always-run-filter=true
.
Loading OpenAPI Schema From Static Files
除了从注解扫描中动态创建 OpenAPI 模式外,Quarkus 还支持提供静态 OpenAPI 文档。要提供的静态文件必须是符合 OpenAPI specification的有效文档。符合 OpenAPI 规范的 OpenAPI 文档本身就是一个有效的 JSON 对象,可以用 `yaml`或 `json`格式表示。
Instead of dynamically creating OpenAPI schemas from annotation scanning, Quarkus also supports serving static OpenAPI documents.
The static file to serve must be a valid document conforming to the OpenAPI specification.
An OpenAPI document that conforms to the OpenAPI Specification is itself a valid JSON object, that can be represented in yaml
or json
formats.
为了在操作中看到这一点,我们会在 `META-INF/openapi.yaml`中为 `/fruits`端点放置 OpenAPI 文档。如果你喜欢的话,Quarkus 也支持备用 OpenAPI document paths。
To see this in action, we’ll put OpenAPI documentation under META-INF/openapi.yaml
for our /fruits
endpoints.
Quarkus also supports alternative open-document-paths if you prefer.
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 文档。
By default, a request to /q/openapi
will serve the combined OpenAPI document from the static file and the model generated from application endpoints code.
We can however change this to only serve the static OpenAPI document by adding mp.openapi.scan.disable=true
configuration into application.properties
.
现在,对 `/q/openapi`端点发出的请求将提供静态 OpenAPI 文档,而不是生成的文档。
Now, a request to /q/openapi
endpoint will serve the static OpenAPI document instead of the generated one.
About OpenAPI document paths
Quarkus 支持多种途径来存储 OpenAPI 文档。我们建议你将其放在 Quarkus supports various paths to store your OpenAPI document under. We recommend you place it under
在开发过程中支持静态 OpenAPI 文档的实时重新加载。Quarkus 将实时获取对你 OpenAPI 文档的修改。 Live reload of static OpenAPI document is supported during development. A modification to your OpenAPI document will be picked up on fly by Quarkus. |
Changing the OpenAPI version
默认情况下,当文档生成时,将使用 OpenAPI 版本 3.0.3
。如果你使用上面提到的静态文件,将会使用该文件中的版本。你还可以使用以下配置在 SmallRye 中定义版本:
By default, when the document is generated, the OpenAPI version used will be 3.0.3
. If you use a static file as mentioned above, the version in the file
will be used. You can also define the version in SmallRye using the following configuration:
mp.openapi.extensions.smallrye.openapi=3.0.2
如果你的 API 通过需要特定版本的网关,这可能很有用。
This might be useful if your API goes through a Gateway that needs a certain version.
Auto-generation of Operation Id
Operation Id 可以使用 @Operation
注释进行设置,并且在使用工具从架构生成客户端存根时,在许多情况下很有用。操作 ID 通常用于客户端存根中的方法名。在 SmallRye 中,你可以使用以下配置自动生成此操作 ID:
The Operation Id can be set using the @Operation
annotation, and is in many cases useful when using a tool to generate a client stub from the schema.
The Operation Id is typically used for the method name in the client stub. In SmallRye, you can auto-generate this Operation Id by using the following configuration:
mp.openapi.extensions.smallrye.operationIdStrategy=METHOD
现在,你无需使用 @Operation
注释。生成文档时,方法名将用于操作 ID。
Now you do not need to use the @Operation
annotation. While generating the document, the method name will be used for the Operation Id.
Property value | Description |
---|---|
|
Use the method name. |
|
Use the class name (without the package) plus the method. |
|
Use the class name plus the method name. |
Use Swagger UI for development
在构建 API 时,开发人员希望快速测试它们。 Swagger UI 是一个非常棒的工具,允许可视化和与你的 API 交互。用户界面会自动从 OpenAPI 规范中生成。
When building APIs, developers want to test them quickly. Swagger UI is a great tool permitting to visualize and interact with your APIs. The UI is automatically generated from your OpenAPI specification.
Quarkus smallrye-openapi
扩展附带了一个 swagger-ui
扩展,该扩展嵌入了正确配置的 Swagger UI 页面。
The Quarkus smallrye-openapi
extension comes with a swagger-ui
extension embedding a properly configured Swagger UI page.
默认情况下,Swagger UI 仅在以开发或测试模式启动 Quarkus 时可用。 By default, Swagger UI is only available when Quarkus is started in dev or test mode. 如果你也想在生产环境中使用,则可以在你的 If you want to make it available in production too, you can include the following configuration in your
这是一个构建时属性,在应用程序构建后无法在运行时更改。 This is a build time property, it cannot be changed at runtime after your application is built. |
默认情况下,可以在 /q/swagger-ui
访问 Swagger UI。
By default, Swagger UI is accessible at /q/swagger-ui
.
你可以通过在 application.properties
中设置 quarkus.swagger-ui.path
属性来更新 /swagger-ui
子路径:
You can update the /swagger-ui
sub path by setting the quarkus.swagger-ui.path
property in your application.properties
:
quarkus.swagger-ui.path=my-custom-path
不允许值 /
,因为它会阻止应用程序提供任何其他服务。以 '/' 为前缀的值使其成为绝对路径,而不是相对路径。
The value /
is not allowed as it blocks the application from serving anything else.
A value prefixed with '/' makes it absolute and not relative.
现在,我们准备运行我们的应用程序:
Now, we are ready to run our application:
./mvnw compile quarkus:dev
你可以在应用程序的日志中检查 Swagger UI 路径:
You can check the Swagger UI path in your application’s log:
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。
Once your application is started, you can go to [role="bare"]http://localhost:8080/q/swagger-ui and play with your API.
您可以查看 API 的操作和架构。
You can visualize your API’s operations and schemas.
您还可以与您的 API 交互,以便快速测试它。
You can also interact with your API in order to quickly test it.
按 CTRL+C
停止应用程序。
Hit CTRL+C
to stop the application.
Styling
您可以通过提供自己的 logo 和 css 来自定义 swagger ui 的样式。
You can style the swagger ui by supplying your own logo and css.
Logo
要提供您自己的 logo,您需要将一个名为 logo.png
的文件放到 src/main/resources/META-INF/branding
中。
To supply your own logo, you need to place a file called logo.png
in src/main/resources/META-INF/branding
.
这将为所有 UI(不仅仅是 swagger ui)设置 logo,因此在这种情况下还包括 GraphQL-UI 和 Health-UI(如果包含)。
This will set the logo for all UIs (not just swagger ui), so in this case also GraphQL-UI and Health-UI (if included).
如果您只想将此 logo 应用于 swagger-ui(而不是全局应用于所有 UI),则将该文件称为 smallrye-open-api-ui.png
而不是 logo.png
。
If you only want to apply this logo to swagger-ui (and not globally to all UIs) call the file smallrye-open-api-ui.png
rather than logo.png
.
CSS
要提供您自己的 css 来替代/增强 html 中的样式,您需要将一个名为 style.css
的文件放到 src/main/resources/META-INF/branding
中。
To supply your own css that override/enhance style in the html, you need to place a file called style.css
in src/main/resources/META-INF/branding
.
这会将该 css 添加到所有 UI(不仅仅是 swagger ui),因此在这种情况下还包括 GraphQL-UI 和 Health-UI(如果包含)。
This will add that css to all UIs (not just swagger ui), so in this case also GraphQL-UI and Health-UI (if included).
如果您只想将此样式应用于 swagger-ui(而不是全局应用于所有 UI),则将该文件称为 smallrye-open-api-ui.css
而不是 style.css
。
If you only want to apply this style to swagger-ui (and not globally to all UIs) call the file smallrye-open-api-ui.css
rather than style.css
.
有关样式的更多信息,请阅读此博客文章:[role="bare"][role="bare"]https://quarkus.io/blog/stylish-api/
For more information on styling, read this blog entry: [role="bare"]https://quarkus.io/blog/stylish-api/
Cross Origin Resource Sharing
如果您计划在运行于不同域上的单页面应用程序中使用此应用程序,您将需要配置 CORS(跨域资源共享)。请阅读“跨域资源共享”指南的 CORS filter 部分,以获取更多详细信息。
If you plan to consume this application from a Single Page Application running on a different domain, you will need to configure CORS (Cross-Origin Resource Sharing). Please read the CORS filter section of the "Cross-origin resource sharing" guide for more details.