Migrating to Quarkus REST (formerly RESTEasy Reactive)

大多数情况下,从 RESTEasy Classic 迁移到 Quarkus REST(以前称为 RESTEasy Reactive)非常简单,但有少数情况需要引起注意。本文档提供了一个列表,列出迁移尝试者应该注意的问题。

Quarkus REST 的参考文档可以在 here 中找到。

Server

Quarkus REST 的服务器部分( quarkus-rest 及其依赖项)提供了 Jakarta REST 规范的实现,但利用了 Quarkus 的构建时间处理功能和 Vert.x 提供的统一 I/O 模型。

Dependencies

下表将旧版 RESTEasy 依赖性与新的 Quarkus REST 依赖性进行匹配。

Legacy Quarkus REST

quarkus-resteasy

quarkus-rest

quarkus-resteasy-jackson

quarkus-rest-jackson

quarkus-resteasy-jsonb

quarkus-rest-jsonb

quarkus-resteasy-jaxb

quarkus-rest-jaxb

quarkus-resteasy-qute

quarkus-rest-qute

quarkus-resteasy-mutiny 没有相应的依赖项,因为 Quarkus REST 提供了开箱即用的 Mutiny 集成。

Annotations

Quarkus REST 不支持 org.jboss.resteasy.annotations 包下的各种自定义注释。

下表将旧版 RESTEasy 注释与新的 Quarkus REST 注释进行匹配。

Legacy Quarkus REST Comments

org.jboss.resteasy.annotations.jaxrs.PathParam

org.jboss.resteasy.reactive.RestPath

当路径部分与方法参数名称匹配时,此注释不是必需的

org.jboss.resteasy.annotations.jaxrs.QueryParam

org.jboss.resteasy.reactive.RestQuery

org.jboss.resteasy.annotations.jaxrs.FormParam

org.jboss.resteasy.reactive.RestForm

org.jboss.resteasy.annotations.jaxrs.HeaderParam

org.jboss.resteasy.reactive.RestHeader

org.jboss.resteasy.annotations.jaxrs.CookieParam

org.jboss.resteasy.reactive.RestCookie

org.jboss.resteasy.annotations.jaxrs.MatrixParam

org.jboss.resteasy.reactive.RestMatrix

org.jboss.resteasy.annotations.cache.Cache

org.jboss.resteasy.reactive.Cache

org.jboss.resteasy.annotations.cache.NoCache

org.jboss.resteasy.reactive.NoCache

org.jboss.resteasy.annotations.SseElementType

org.jboss.resteasy.reactive.RestStreamElementType

org.jboss.resteasy.annotations.Separator

org.jboss.resteasy.reactive.Separator

前一个表不包括 org.jboss.resteasy.annotations.Form 注释,因为没有可替代它的特定于 Quarkus REST 的注释。相反,鼓励用户使用在服务器和客户端上均受支持的 Jakarta REST 标准 jakarta.ws.rs.BeanParam 注释。

Jakarta REST providers

虽然 Quarkus REST 提供了与 RESTEasy Classic 相同的规范兼容的行为,但它在运行时不包含相同的精确提供程序实现。

提供程序的差异可能会导致不同行为的最常见情况是包含的`jakarta.ws.rs.ext.ExceptionMapper`实现。要查看应用程序中包含哪些类,请以开发模式启动应用程序,然后导航至[role="bare"][role="bare"]http://localhost:8080/q/dev-ui/io.quarkus.quarkus-rest/exception-mappers。

Service Loading

RESTEasy Classic 支持使用 Java 的服务加载器在构建时确定提供程序。为了确保在构建时确定所有提供程序,Quarkus REST 不支持此功能。相反,建议在应用程序依赖项中具有提供程序的用户使用 CDI 指南的Bean Discovery部分中描述的一种方法对这些依赖项进行索引。

Multipart support

Quarkus REST 中的 HTTP 多部分支持*not*不重用与 RESTEasy Classic 相同的类型或注释,因此建议用户阅读参考文档的this部分。

将多部分资源迁移到 Quarkus REST 的用户应注意配置参数`quarkus.http.limits.max-form-attribute-size`,因为它对每个部分的大小设定了上限。任何部分大小超过此配置值的请求将导致 HTTP 状态代码 413。

Default media types

Quarkus 在确定 Jakarta REST 方法的媒体类型时使用智能默认值,以便简化常见的用例。quarkus-rest`和`quarkus-resteasy`之间的区别在于当方法返回`String`时使用`text/plain`作为默认媒体类型而不是`text/html

Injection of @SessionScoped beans

当前不支持`@SessionScoped`bean。如果你确实需要此功能,则需要使用 RESTEasy Classic 而不是 RESTEasy Reactive。

Servlets

Quarkus REST not 支持 servlet。如果您的项目依赖于 servlet,则必须迁移它们。基于 servlet 的 JAX-RS 实现必须使用 @Context 注释支持这些类型的注入:ServletConfigServletContextHttpServletRequestHttpServletResponse。因为 Quarkus REST 不是基于 servlet 的,所以这些注入将不起作用。

即使你依赖于提供接口的扩展(例如`quarkus-undertow`),也不总是很明显这点会失败。例如,如果你编写此内容,则可以编译它,但在调用它时会得到一个异常:

@Path("/reactive")
public class ReactiveResource {

    @Context
    HttpServletRequest httpServletRequest;

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String servletContextPath() {

        String contextPath = httpServletRequest.getContextPath();

        return "My context path is: " + contextPath;
    }
}

第三方库也是如此。如果它们碰巧依赖于 servlet,则需要找到它们的迁移路径。

Log authentication and authorization failures

Quarkus REST 端点安全检查在调用CDI interceptors之前执行。记录 Quarkus Security 身份验证异常的最安全方法是确保启用了主动身份验证并使用 Vert.x HTTP 路由故障处理程序。有关更多信息,请参阅主动身份验证指南的Customize authentication exception responses部分。

Client

REST 客户端(quarkus-rest-client`及其依赖项)取代了基于 RESTEasy Classic 的旧版`quarkus-resteasy-client,并利用 Quarkus 的构建时间处理和 Vert.x 提供的统一 I/O 模型。

Dependencies

下表将旧版基于 RESTEasy Classic 的 REST 客户端依赖项与新的 REST 客户端依赖项进行匹配。

Legacy Quarkus REST

quarkus-resteasy-client

quarkus-rest-client

quarkus-resteasy-client-jackson

quarkus-rest-client-jackson

quarkus-resteasy-client-jsonb

quarkus-rest-client-jsonb

quarkus-resteasy-client-jaxb

quarkus-rest-client-jaxb

quarkus-resteasy-client-mutiny

没有替代品,本机支持 Mutiny

Keycloak admin client

在使用`quarkus-rest-client`时,用户可以使用`quarkus-keycloak-admin-rest-client`通过利用 REST 客户端来管理目标 Keycloak 实例。

然而,在使用`quarkus-resteasy-client`时,用户必须使用`quarkus-keycloak-admin-resteasy-client`来访问相同的功能并使用基于 RESTEasy Classic 的旧版 REST 客户端。

OIDC

在使用`quarkus-rest-client`时,用户可以使用`quarkus-rest-client-oidc-filter`extension 从 OpenID Connect 和 OAuth 2.0 兼容授权服务器获取和刷新访问令牌。

然而,在使用`quarkus-resteasy-client`时,用户必须使用`quarkus-resteasy-client-oidc-filter`来访问相同的功能。

同样,`quarkus-rest-client-oidc-token-propagation`允许传统 REST 的用户传播当前的`Bearer`或`Authorization Code Flow`访问令牌。

然而,在使用`quarkus-resteasy-client`时,用户必须使用`quarkus-resteasy-client-oidc-token-propagation`来访问相同的功能。

Custom extensions

这是一个高级部分,仅需要由开发自定义扩展的用户阅读,这些扩展依赖于 Jakarta REST 和/或 REST 客户端功能。

Dependencies

首要顾虑是自定义扩展应明确依赖于 Quarkus REST 还是同时支持 RESTEasy 组件,并由用户决定。如果该扩展是一些通用扩展,那么可能合理选择后者选项,而当特定用户/应用程序集使用自定义扩展时,前者选项最易于采纳。

在选择支持这两个扩展时,该自定义扩展的部署模块通常将依赖于 SPI 模块——quarkus-jaxrs-spi-deploymentquarkus-resteasy-common-spiquarkus-rest-spi-deployment,而运行时模块将对 RESTEasy 两个组件的运行时模块存在 optional 依赖关系。

一些很好的例子说明 Quarkus 如何在核心存储库中使用此策略来支持这两个 RESTEasy 组件,可参见 [此处]([role="bare"][role="bare"]https://github.com/quarkusio/quarkus/pull/21089) 和 [此处]([role="bare"][role="bare"]https://github.com/quarkusio/quarkus/pull/20874)。

通常,支持这两个组件不需要该自定义扩展的两个不同版本。这样的选择只在扩展使用者(即 Quarkus 应用程序)希望不必自己选择 RESTEasy 版本时才是严格需要的。

Resource and Provider discovery

在其运行时模块中包含 Jakarta REST 资源、提供程序或 REST 客户端接口且依赖于 Jandex 索引以发现它们的自定义扩展(例如,因为它们有一个空的 META-INF/beans.xml 文件),不必执行任何其他设置以使 Quarkus REST 发现它们。

Provider registration via Build Items

通过构建项目使用 io.quarkus.resteasy.common.spi.ResteasyJaxrsProviderBuildItem 构建项目在 RESTEasy Classic 中注册提供程序。然而,使用 Quarkus REST 时,扩展需要使用特定的构建项目,如 io.quarkus.resteasy.reactive.spi.MessageBodyWriterBuildItemio.quarkus.resteasy.reactive.spi.MessageBodyWriterBuildItem

REST Client

作为使用 RESTEasy 客户端的 Quarkus 应用程序一部分运行的任何代码,都可以安全地使用 REST 客户端,因为它在应用程序的静态初始化阶段已经配置了所有必要的设置。