Quarkus Extension for Spring Web API

虽然鼓励用户使用 Jakarta REST(以前称为 JAX-RS)注解定义 REST 端点,但 Quarkus 通过 `spring-web`扩展程序提供 Spring Web 兼容层。

While users are encouraged to use Jakarta REST (formerly known as JAX-RS) annotations for defining REST endpoints, Quarkus provides a compatibility layer for Spring Web in the form of the spring-web extension.

本指南解释 Quarkus 应用程序如何利用众所周知的 Spring Web 注解来定义 RESTful 服务。

This guide explains how a Quarkus application can leverage the well known Spring Web annotations to define RESTful services.

Prerequisites

如要完成本指南,您需要:

To complete this guide, you need:

Unresolved directive in spring-web.adoc - include::{includes}/prerequisites.adoc[]

Solution

我们建议您遵循接下来的部分中的说明,按部就班地创建应用程序。然而,您可以直接跳到完成的示例。

We recommend that you follow the instructions in the next sections and create the application step by step. However, you can go 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].

解决方案位于 spring-web-quickstart directory 中。

The solution is located in the spring-web-quickstart directory.

Creating the Maven project

首先,我们需要一个新项目。使用以下命令创建一个新项目:

First, we need a new project. Create a new project with the following command:

Unresolved directive in spring-web.adoc - include::{includes}/devtools/create-app.adoc[]

此命令会生成一个导入 spring-web 扩展的项目。

This command generates a project which imports the spring-web extension.

如果您已配置好 Quarkus 项目,则可以通过在项目基本目录中运行以下命令来将 spring-web 扩展添加到项目中:

If you already have your Quarkus project configured, you can add the spring-web extension to your project by running the following command in your project base directory:

Unresolved directive in spring-web.adoc - include::{includes}/devtools/extension-add.adoc[]

这会将以下内容添加到构建文件中:

This will add the following to your build file:

pom.xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-spring-web</artifactId>
</dependency>
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-rest-jackson</artifactId>
</dependency>
build.gradle
implementation("io.quarkus:quarkus-spring-web")
implementation("io.quarkus:quarkus-rest-jackson")

quarkus-spring-web 需要与 quarkus-rest-jacksonquarkus-resteasy-jackson 结合使用才能起作用。

quarkus-spring-web needs to be complemented with either quarkus-rest-jackson or quarkus-resteasy-jackson in order to work.

GreetingController

创建 src/main/java/org/acme/spring/web/GreetingController.java 文件,一个包含 Spring Web 注解以定义我们的 REST 端点的控制器,如下所示:

Create the src/main/java/org/acme/spring/web/GreetingController.java file, a controller with the Spring Web annotations to define our REST endpoint, as follows:

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

请注意,还创建了该控制器的测试:

Note that a test for the controller has been created as well:

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"));
    }

}

Package and run the application

使用以下内容运行应用程序:

Run the application with:

Unresolved directive in spring-web.adoc - include::{includes}/devtools/dev.adoc[]

用浏览器打开 [role="bare"][role="bare"]http://localhost:8080/greeting.

Open your browser to [role="bare"]http://localhost:8080/greeting.

结果应为: {"message": "hello"}.

The result should be: {"message": "hello"}.

Run the application as a native executable

可以使用以下命令生成本机可执行文件:

You can generate the native executable with:

Unresolved directive in spring-web.adoc - include::{includes}/devtools/build-native.adoc[]

Going further with an endpoint returning JSON

上面的 GreetingController 是一个非常简单的端点的示例。然而,在许多情况下都需要返回 JSON 内容,以下示例说明了如何使用 Spring RestController 来实现该目的:

The GreetingController above was an example of a very simple endpoint. In many cases however it is required to return JSON content. The following example illustrates how that could be achieved using a 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;
        }
    }
}

对应的测试可能如下所示:

The corresponding test could look like:

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 会自动添加到类路径中并得到正确设置。

It should be noted that when using the Spring Web support in Quarkus, Jackson is automatically added to the classpath and properly set up.

Adding OpenAPI and Swagger-UI

通过使用 quarkus-smallrye-openapi 扩展,可以添加对 OpenAPISwagger-UI 的支持。

You can add support for OpenAPI and Swagger-UI by using the quarkus-smallrye-openapi extension.

通过运行此命令添加扩展:

Add the extension by running this command:

./mvnw quarkus:add-extension -Dextensions="io.quarkus:quarkus-smallrye-openapi"

这会将以下内容添加到您的 pom.xml

This will add the following to your pom.xml:

<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-smallrye-openapi</artifactId>
</dependency>

这足以从你的 REST 端点生成基本 OpenAPI 架构文档:

This is enough to generate a basic OpenAPI schema document from your REST Endpoints:

curl http://localhost:8080/q/openapi

你将看到生成的 OpenAPI 模式文档:

You will see the generated OpenAPI schema document:

---
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

另请参阅 the OpenAPI Guide

Adding MicroProfile OpenAPI Annotations

可以使用 MicroProfile OpenAPI 更好地记录你的架构,例如将以下内容添加到 GreetingController 的类级别:

You can use MicroProfile OpenAPI to better document your schema, example, adding the following to the class level of the 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://www.apache.org/licenses/LICENSE-2.0.html"))
)

你的端点将会如下所示:

And describe your endpoints like this:

@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 架构:

will generate this OpenAPI schema:

---
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://www.apache.org/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

Using Swagger UI

DevTest 模式下运行时,默认情况下会包含 Swagger UI,并且可以选择将其添加到 Prod 模式中,请参阅 the Swagger UI 指南了解更多详情。

Swagger UI is included by default when running in Dev or Test mode, and can optionally be added to Prod mode. See the Swagger UI Guide for more details.

导航至 localhost:8080/q/swagger-ui/,你将看到 Swagger UI 屏幕:

Navigate to localhost:8080/q/swagger-ui/ and you will see the Swagger UI screen:

spring web guide screenshot01

Supported Spring Web functionalities

Quarkus 目前支持 Spring Web 提供的部分功能。更具体地说,Quarkus 支持 Spring Web 的 REST 相关功能(考虑使用 @RestController 代替 @Controller)。

Quarkus currently supports a subset of the functionalities that Spring Web provides. More specifically Quarkus supports the REST related features of Spring Web (think of @RestController instead of @Controller).

Annotations

下表总结了受支持的注释:

The table below summarizes the supported annotations:

Table 1. Supported Spring Web annotation
Name Comments

@RestController

@RequestMapping

@GetMapping

@PostMapping

@PutMapping

@DeleteMapping

@PatchMapping

@RequestParam

@RequestHeader

@MatrixVariable

@PathVariable

@CookieValue

@RequestBody

@ResponseStatus

@ExceptionHandler

Can only be used in a @RestControllerAdvice class, not on a per-controller basis

@RestControllerAdvice

Only the @ExceptionHandler capability is supported

Controller method return types

支持以下方法返回类型:

The following method return types are supported:

  • Primitive types

  • String (which will be used as a literal, no Spring MVC view support is provided)

  • POJO classes which will be serialized via JSON

  • org.springframework.http.ResponseEntity

Controller method parameter types

除了可以用前一表格中的适当 Spring Web 注释进行注释的方法参数之外,jakarta.servlet.http.HttpServletRequestjakarta.servlet.http.HttpServletResponse 也受支持。不过,为了实现此功能,用户需要添加 quarkus-undertow 依赖项。

In addition to the method parameters that can be annotated with the appropriate Spring Web annotations from the previous table, jakarta.servlet.http.HttpServletRequest and jakarta.servlet.http.HttpServletResponse are also supported. For this to function however, users need to add the quarkus-undertow dependency.

Exception handler method return types

支持以下方法返回类型:

The following method return types are supported:

  • org.springframework.http.ResponseEntity

  • java.util.Map

Spring ExceptionHandler javadoc 中提到的其他返回类型不受支持。

Other return types mentioned in the Spring ExceptionHandler javadoc are not supported.

Exception handler method parameter types

以下参数类型受支持,顺序任意:

The following parameter types are supported, in arbitrary order:

  • An exception argument: declared as a general Exception or as a more specific exception. This also serves as a mapping hint if the annotation itself does not narrow the exception types through its value().

  • Request and/or response objects (typically from the Servlet API). You may choose any specific request/response type, e.g. ServletRequest / HttpServletRequest. To use Servlet API, the quarkus-undertow dependency needs to be added.

Spring ExceptionHandler javadoc 中提到的其他参数类型不受支持。

Other parameter types mentioned in the Spring ExceptionHandler javadoc are not supported.

Important Technical Note

请注意,Quarkus 中的 Spring 支持不会启动 Spring 应用程序上下文,也不会运行任何 Spring 基础设施类。Spring 类和注释仅用于读取元数据和/或用作用户代码方法返回类型或参数类型。这对最终用户意味着,添加任意 Spring 库不会产生任何影响。此外,Spring 基础设施类(例如 org.springframework.beans.factory.config.BeanPostProcessor)不会被执行。

Please note that the Spring support in Quarkus does not start a Spring Application Context nor are any Spring infrastructure classes run. Spring classes and annotations are only used for reading metadata and / or are used as user code method return types or parameter types. What that means for end users, is that adding arbitrary Spring libraries will not have any effect. Moreover, Spring infrastructure classes (like org.springframework.beans.factory.config.BeanPostProcessor for example) will not be executed.

Conversion Table

下表显示了 Spring Web 注释如何转换为 Jakarta REST 注释。

The following table shows how Spring Web annotations can be converted to Jakarta REST annotations.

Spring Jakarta REST Comments

@RestController

There is no equivalent in Jakarta REST. Annotating a class with @Path suffices

@RequestMapping(path="/api")

@Path("/api")

@RequestMapping(consumes="application/json")

@Consumes("application/json")

@RequestMapping(produces="application/json")

@Produces("application/json")

@RequestParam

@QueryParam

@PathVariable

@PathParam

@RequestBody

No equivalent in Jakarta REST. Method parameters corresponding to the body of the request are handled in Jakarta REST without requiring any annotation

@RestControllerAdvice

No equivalent in Jakarta REST

@ResponseStatus

No equivalent in Jakarta REST

@ExceptionHandler

No equivalent annotation in Jakarta REST. Exceptions are handled by implementing jakarta.ws.rs.ext.ExceptionMapper