Quarkus Spring Web API 扩展
尽管用户被鼓励使用 Jakarta REST(以前称为 JAX-RS)注解来定义 REST 端点,但 Quarkus 以 spring-web
扩展的形式提供了 Spring Web 的兼容层。
本指南将介绍 Quarkus 应用程序如何利用众所周知的 Spring Web 注解来定义 RESTful 服务。
先决条件
要完成本指南,您需要
要完成本指南,您需要
-
大约 15 分钟
-
一个 IDE
-
已安装 JDK 17+ 并正确配置了
JAVA_HOME
-
Apache Maven 3.9.9
-
如果您想使用它,可以选择 Quarkus CLI
-
如果您想构建本机可执行文件(或者如果您使用本机容器构建,则为 Docker),可以选择安装 Mandrel 或 GraalVM 并进行适当的配置
解决方案
我们建议您按照以下章节中的说明,逐步创建应用程序。但是,您可以直接转到完整的示例。
克隆 Git 存储库:git clone https://github.com/quarkusio/quarkus-quickstarts.git
,或下载一个存档。
解决方案位于 spring-web-quickstart
目录中。
创建 Maven 项目
首先,我们需要一个新项目。使用以下命令创建一个新项目
对于 Windows 用户
-
如果使用 cmd,(不要使用反斜杠
\
并将所有内容放在同一行上) -
如果使用 Powershell,请将
-D
参数用双引号括起来,例如"-DprojectArtifactId=spring-web-quickstart"
此命令将生成一个导入 spring-web
扩展的项目。
如果您已经配置了 Quarkus 项目,您可以通过在项目根目录中运行以下命令将 spring-web
扩展添加到您的项目中
quarkus extension add spring-web,rest-jackson
./mvnw quarkus:add-extension -Dextensions='spring-web,rest-jackson'
./gradlew addExtension --extensions='spring-web,rest-jackson'
这会将以下内容添加到您的构建文件中
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-spring-web</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-jackson</artifactId>
</dependency>
implementation("io.quarkus:quarkus-spring-web")
implementation("io.quarkus:quarkus-rest-jackson")
|
GreetingController
创建 src/main/java/org/acme/spring/web/GreetingController.java
文件,这是一个带有 Spring Web 注解的控制器,用于定义我们的 REST 端点,如下所示
package org.acme.spring.web;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/greeting")
public class GreetingController {
@GetMapping
public String hello() {
return "hello";
}
}
GreetingControllerTest
请注意,控制器也已创建了相应的测试
package org.acme.spring.web;
import io.quarkus.test.junit.QuarkusTest;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.given;
import static org.hamcrest.CoreMatchers.is;
@QuarkusTest
public class GreetingControllerTest {
@Test
public void testHelloEndpoint() {
given()
.when().get("/greeting")
.then()
.statusCode(200)
.body(is("hello"));
}
}
打包并运行应用程序
使用以下命令运行应用程序
quarkus dev
./mvnw quarkus:dev
./gradlew --console=plain quarkusDev
在浏览器中打开 https://:8080/greeting。
结果应为:{"message": "hello"}
。
将应用程序作为原生可执行文件运行
您可以使用以下命令生成原生可执行文件
quarkus build --native
./mvnw install -Dnative
./gradlew build -Dquarkus.native.enabled=true
进一步使用返回 JSON 的端点
上面的 GreetingController
是一个非常简单的端点的示例。但在许多情况下,需要返回 JSON 内容。下面的示例说明了如何使用 Spring RestController 来实现这一点。
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/greeting")
public class GreetingController {
@GetMapping("/{name}")
public Greeting hello(@PathVariable(name = "name") String name) {
return new Greeting("hello " + name);
}
public static class Greeting {
private final String message;
public Greeting(String message) {
this.message = message;
}
public String getMessage(){
return message;
}
}
}
相应的测试可能如下所示
package org.acme.spring.web;
import io.quarkus.test.junit.QuarkusTest;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.given;
import static org.hamcrest.CoreMatchers.is;
@QuarkusTest
public class GreetingControllerTest {
@Test
public void testHelloEndpoint() {
given()
.when().get("/greeting/quarkus")
.then()
.statusCode(200)
.body("message", is("hello quarkus"));
}
}
值得注意的是,在使用 Quarkus 中的 Spring Web 支持时,Jackson 会被自动添加到类路径并正确设置。
添加 OpenAPI 和 Swagger-UI
您可以使用 quarkus-smallrye-openapi
扩展来添加对 OpenAPI 和 Swagger-UI 的支持。
运行此命令添加扩展
./mvnw quarkus:add-extension -Dextensions="io.quarkus:quarkus-smallrye-openapi"
这会将以下内容添加到您的 pom.xml
中
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-openapi</artifactId>
</dependency>
这足以从您的 REST 端点生成基本的 OpenAPI 架构文档
curl https://:8080/q/openapi
您将看到生成的 OpenAPI 架构文档
---
openapi: 3.0.1
info:
title: Generated API
version: "1.0"
paths:
/greeting:
get:
responses:
"200":
description: OK
content:
'*/*':
schema:
type: string
/greeting/{name}:
get:
parameters:
- name: name
in: path
required: true
schema:
type: string
responses:
"200":
description: OK
content:
'application/json':
schema:
$ref: '#/components/schemas/Greeting'
components:
schemas:
Greeting:
type: object
properties:
message:
type: string
另请参阅 OpenAPI 指南
添加 MicroProfile OpenAPI 注解
您可以使用 MicroProfile OpenAPI 来更好地记录您的架构,例如,将以下内容添加到 GreetingController
的类级别
@OpenAPIDefinition(
info = @Info(
title="Greeting API",
version = "1.0.1",
contact = @Contact(
name = "Greeting API Support",
url = "http://exampleurl.com/contact",
email = "techsupport@example.com"),
license = @License(
name = "Apache 2.0",
url = "https://apache.ac.cn/licenses/LICENSE-2.0.html"))
)
并像这样描述您的端点
@Tag(name = "Hello", description = "Just say hello")
@GetMapping(produces=MediaType.TEXT_PLAIN_VALUE)
public String hello() {
return "hello";
}
@GetMapping(value = "/{name}", produces=MediaType.APPLICATION_JSON_VALUE)
@Tag(name = "Hello to someone", description = "Just say hello to someone")
public Greeting hello(@PathVariable(name = "name") String name) {
return new Greeting("hello " + name);
}
将生成此 OpenAPI 架构
---
openapi: 3.0.1
info:
title: Greeting API
contact:
name: Greeting API Support
url: http://exampleurl.com/contact
email: techsupport@example.com
license:
name: Apache 2.0
url: https://apache.ac.cn/licenses/LICENSE-2.0.html
version: 1.0.1
tags:
- name: Hello
description: Just say hello
- name: Hello to someone
description: Just say hello to someone
paths:
/greeting:
get:
tags:
- Hello
responses:
"200":
description: OK
content:
'*/*':
schema:
type: string
/greeting/{name}:
get:
tags:
- Hello to someone
parameters:
- name: name
in: path
required: true
schema:
type: string
responses:
"200":
description: OK
content:
'*/*':
schema:
$ref: '#/components/schemas/Greeting'
components:
schemas:
Greeting:
type: object
properties:
message:
type: string
使用 Swagger UI
Swagger UI 在 Dev
或 Test
模式下运行时默认包含,并且可以选择性地添加到 Prod
模式。有关更多详细信息,请参阅 Swagger UI 指南。
导航到 localhost:8080/q/swagger-ui/,您将看到 Swagger UI 屏幕
支持的 Spring Web 功能
Quarkus 目前支持 Spring Web 提供的一部分功能。更具体地说,Quarkus 支持 Spring Web 的 REST 相关功能(考虑 @RestController
而不是 @Controller
)。
注解
下表总结了支持的注解
名称 | 注释 |
---|---|
@RestController |
|
@RequestMapping |
|
@GetMapping |
|
@PostMapping |
|
@PutMapping |
|
@DeleteMapping |
|
@PatchMapping |
|
@RequestParam |
|
@RequestHeader |
|
@MatrixVariable |
|
@PathVariable |
|
@CookieValue |
|
@RequestBody |
|
@ResponseStatus |
|
@ExceptionHandler |
只能在 @RestControllerAdvice 类中使用,不能在每个控制器基础上使用 |
@RestControllerAdvice |
仅支持 @ExceptionHandler 功能 |
控制器方法返回类型
支持以下方法返回类型
-
基本类型
-
String(将用作字面量,不提供 Spring MVC 视图支持)
-
POJO 类(将通过 JSON 序列化)
-
org.springframework.http.ResponseEntity
控制器方法参数类型
除了可以被前面表格中相应 Spring Web 注解注解的方法参数之外,还支持 jakarta.servlet.http.HttpServletRequest
和 jakarta.servlet.http.HttpServletResponse
。但是,要使其工作,用户需要添加 quarkus-undertow
依赖。
异常处理方法返回类型
支持以下方法返回类型
-
org.springframework.http.ResponseEntity
-
java.util.Map
Spring ExceptionHandler javadoc
中提到的其他返回类型不支持。
异常处理方法参数类型
支持以下参数类型,顺序任意
-
异常参数:声明为通用
Exception
或更具体的异常。如果注解本身没有通过其value()
缩小异常类型,这也可作为映射提示。 -
请求和/或响应对象(通常来自 Servlet API)。您可以选择任何特定的请求/响应类型,例如
ServletRequest
/HttpServletRequest
。要使用 Servlet API,需要添加quarkus-undertow
依赖。
Spring ExceptionHandler javadoc
中提到的其他参数类型不支持。
重要的技术说明
请注意,Quarkus 中的 Spring 支持不会启动 Spring Application Context,也不会运行任何 Spring 基础设施类。Spring 类和注解仅用于读取元数据和/或用作用户代码的方法返回类型或参数类型。这意味着对于最终用户来说,添加任意 Spring 库将不会产生任何效果。此外,Spring 基础设施类(例如 org.springframework.beans.factory.config.BeanPostProcessor
)将不会被执行。
转换表
下表显示了如何将 Spring Web 注解转换为 Jakarta REST 注解。
Spring | Jakarta REST | 注释 |
---|---|---|
@RestController |
Jakarta REST 中没有等价的。用 @Path 注解类就足够了 |
|
@RequestMapping(path="/api") |
@Path("/api") |
|
@RequestMapping(consumes="application/json") |
@Consumes("application/json") |
|
@RequestMapping(produces="application/json") |
@Produces("application/json") |
|
@RequestParam |
@QueryParam |
|
@PathVariable |
@PathParam |
|
@RequestBody |
Jakarta REST 中没有等价的。Jakarta REST 在处理与请求体对应的方法参数时,无需任何注解 |
|
@RestControllerAdvice |
Jakarta REST 中没有等价的 |
|
@ResponseStatus |
Jakarta REST 中没有等价的 |
|
@ExceptionHandler |
Jakarta REST 中没有等价的注解。异常通过实现 |