使用 Panache 生成 Jakarta REST 资源
许多 Web 应用程序都是单调的 CRUD 应用程序,带有 REST API,编写起来很繁琐。为了简化这项任务,REST Data with Panache 扩展可以为您的实体和存储库生成基本的 CRUD 端点。
目前,此扩展支持带有 Panache 的 Hibernate ORM 和 MongoDB,并且可以生成使用 application/json
和 application/hal+json
内容的 CRUD 资源,并生成由 Quarkus REST 支持的 REST 资源。
设置 REST Data with Panache
Quarkus 提供了以下扩展来设置 REST Data with Panache。请查看下面的兼容性表,以根据您正在使用的技术使用正确的扩展
扩展 | 状态 | Hibernate |
---|---|---|
|
|
|
|
|
|
|
|
Hibernate ORM
-
将所需的依赖项添加到您的构建文件中
-
带有 Panache 扩展的 Hibernate ORM REST Data (
quarkus-hibernate-orm-rest-data-panache
) -
JDBC 驱动程序扩展 (
quarkus-jdbc-postgresql
,quarkus-jdbc-h2
,quarkus-jdbc-mariadb
, …) -
REST JSON 序列化扩展之一(例如
quarkus-rest-jackson
)
-
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-orm-rest-data-panache</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-postgresql</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-jackson</artifactId>
</dependency>
-->
implementation("io.quarkus:quarkus-hibernate-orm-rest-data-panache")
implementation("io.quarkus:quarkus-jdbc-postgresql")
implementation("io.quarkus:quarkus-rest-jackson")
-
按照带有 Panache 的 Hibernate ORM指南中的说明实现 Panache 实体和/或存储库。
-
按照生成资源部分中的说明定义用于生成的接口。
要查看带有 Panache 的 Hibernate ORM REST Data 的实际应用,您可以查看hibernate-orm-rest-data-panache-quickstart快速入门。
Hibernate Reactive
-
将所需的依赖项添加到您的
pom.xml
-
带有 Panache 扩展的 Hibernate Reactive REST Data (
quarkus-hibernate-reactive-rest-data-panache
) -
Vert.x 响应式数据库驱动程序扩展 (
quarkus-reactive-pg-client
,quarkus-reactive-mysql-client
, …) -
Quarkus REST 序列化扩展之一 (
quarkus-rest-jackson
,quarkus-rest-jsonb
, …)
-
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-reactive-rest-data-panache</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-reactive-pg-client</artifactId>
</dependency>
<!-- Use this if you are using REST Jackson for serialization -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-jackson</artifactId>
</dependency>
</dependencies>
-
按照带有 Panache 的 Hibernate Reactive指南中的说明实现 Panache 实体和/或存储库。
-
按照资源生成部分中的说明定义用于生成的接口。
MongoDB
-
将所需的依赖项添加到您的构建文件中
-
MongoDB REST Data with Panache 扩展 (
quarkus-mongodb-rest-data-panache
) -
RESTEasy JSON 序列化扩展之一 (
quarkus-rest-jackson
或quarkus-rest-jsonb
)
-
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-mongodb-rest-data-panache</artifactId>
</dependency>
<!-- Use this if you are using Quarkus REST -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-jackson</artifactId>
</dependency>
implementation("io.quarkus:quarkus-mongodb-rest-data-panache")
// Use this if you are using Quarkus REST
implementation("io.quarkus:quarkus-rest-jackson")
-
按照带有 Panache 的 MongoDB指南中的说明实现 Panache 实体和/或存储库。
-
按照资源生成部分中的说明定义用于生成的接口。
生成资源
REST Data with Panache 基于您的应用程序中可用的接口生成 Jakarta REST 资源。对于您要生成的每个实体和存储库,请提供一个资源接口。不要实现这些接口,也不要提供自定义方法,因为它们将被忽略。 但是,您可以覆盖扩展接口中的方法以自定义它们(请参阅末尾的部分)。
PanacheEntityResource
如果您的应用程序有一个实体(例如 Person
),它扩展了 PanacheEntity
或 PanacheEntityBase
类,您可以指示 REST Data with Panache 使用以下接口生成其 Jakarta REST 资源
public interface PeopleResource extends PanacheEntityResource<Person, Long> {
}
PanacheRepositoryResource
如果您的应用程序有一个简单的实体(例如 Person
)和一个存储库(例如 PersonRepository
),它实现了 PanacheRepository
或 PanacheRepositoryBase
接口,您可以指示 REST Data with Panache 使用以下接口生成其 Jakarta REST 资源
public interface PeopleResource extends PanacheRepositoryResource<PersonRepository, Person, Long> {
}
PanacheMongoEntityResource
如果您的应用程序有一个实体(例如 Person
),它扩展了 PanacheMongoEntity
或 PanacheMongoEntityBase
类,您可以指示 REST Data with Panache 使用以下接口生成其 Jakarta REST 资源
public interface PeopleResource extends PanacheMongoEntityResource<Person, Long> {
}
PanacheMongoRepositoryResource
如果您的应用程序有一个简单的实体(例如 Person
)和一个存储库(例如 PersonRepository
),它实现了 PanacheMongoRepository
或 PanacheMongoRepositoryBase
接口,您可以指示 REST Data with Panache 使用以下接口生成其 Jakarta REST 资源
public interface PeopleResource extends PanacheMongoRepositoryResource<PersonRepository, Person, Long> {
}
生成的资源
对于实体和存储库,生成的资源在功能上是等效的。唯一的区别是特定的数据访问模式和正在使用的数据存储。
如果您定义了上述 PeopleResource
接口之一,此扩展将使用特定的数据访问策略生成其实现。然后,实现的类将由生成的 Jakarta REST 资源使用,该资源将如下所示
public class PeopleResourceJaxRs { // The actual class name is going to be unique
@Inject
PeopleResource resource;
@GET
@Path("{id}")
@Produces("application/json")
public RestResponse<Person> get(@PathParam("id") Long id){
Person person = resource.get(id);
if (person == null) {
return ResponseBuilder.create(404).build();
} else {
return ResponseBuilder.ok(person).build();
}
}
@GET
@Produces("application/json")
public RestResponse<Person> list(@QueryParam("sort") List<String> sortQuery,
@QueryParam("page") @DefaultValue("0") int pageIndex,
@QueryParam("size") @DefaultValue("20") int pageSize) {
Page page = Page.of(pageIndex, pageSize);
Sort sort = getSortFromQuery(sortQuery);
List<Person> people = resource.list(page, sort);
// ... build a response with page links and return a 200 response with a list
}
@GET
@Path("/count")
public RestResponse<Long> count() {
return ResponseBuilder.ok(resource.count()).build();
}
@Transactional
@POST
@Consumes("application/json")
@Produces("application/json")
public RestResponse<Person> add(Person personToSave) {
Person person = resource.add(personToSave);
// ... build a new location URL and return 201 response with an entity
}
@Transactional
@PUT
@Path("{id}")
@Consumes("application/json")
@Produces("application/json")
public RestResponse<Person> update(@PathParam("id") Long id, Person personToSave) {
if (resource.get(id) == null) {
Person person = resource.update(id, personToSave);
return ResponseBuilder.create(204).build();
}
Person person = resource.update(id, personToSave);
// ... build a new location URL and return 201 response with an entity
}
@Transactional
@DELETE
@Path("{id}")
public RestResponse<Person> delete(@PathParam("id") Long id) {
return !resource.delete(id) ? ResponseBuilder.create(404).build() : ResponseBuilder.create(204).build();
}
}
资源自定义
REST Data with Panache 提供了 @ResourceProperties
和 @MethodProperties
注释,可用于自定义资源的某些功能。
它可以在您的资源接口中使用
@ResourceProperties(hal = true, path = "my-people")
public interface PeopleResource extends PanacheEntityResource<Person, Long> {
@MethodProperties(path = "all")
List<Person> list(Page page, Sort sort);
@MethodProperties(exposed = false)
boolean delete(Long id);
}
可用选项
@ResourceProperties
-
exposed
- 资源是否可以公开。一种全局资源属性,可以为每种方法覆盖。默认为true
。 -
path
- 资源基本路径。默认路径是带连字符的小写资源名称,不带resource
或controller
的后缀。 -
rolesAllowed
- 允许访问资源的安全角色的列表。它需要一个 Quarkus Security 扩展才能存在,否则它将被忽略。默认为空。 -
paged
- 集合响应是否应该分页。如果存在,则在响应标头中包含第一页、最后一页、上一页和下一页 URI。请求页面索引和大小取自page
和size
查询参数,它们分别默认为0
和20
。默认为true
。 -
hal
- 除了标准的application/json
响应之外,还生成可以返回application/hal+json
响应的其他方法(如果通过Accept
标头请求)。默认为false
。 -
halCollectionName
- 生成 hal 集合响应时应使用的名称。默认名称是带连字符的小写资源名称,不带resource
或controller
的后缀。
@MethodProperties
-
exposed
- 设置为false
时,不会公开特定的 HTTP 动词。默认为true
。 -
path
- 操作路径(这附加到资源基本路径)。默认为空字符串。 -
rolesAllowed
- 允许访问此操作的安全角色的列表。它需要一个 Quarkus Security 扩展才能存在,否则它将被忽略。默认为空。
向生成的资源添加其他方法
您可以通过将这些方法添加到资源接口,通过 REST Data with Panache 扩展向生成的资源添加其他方法,例如
@ResourceProperties
public interface PeopleResource extends PanacheEntityResource<Person, Long> {
@GET
@Path("/name/{name}")
@Produces("application/json")
default List<Person> findByName(@PathParam("name") String name) {
return Person.find("name = :name", Collections.singletonMap("name", name)).list();
}
}
并且此方法将与使用 https://:8080/people/name/Johan
生成的方法一起公开。
保护端点
REST Data with Panache 将使用包 jakarta.annotation.security
中的 Security 注释,这些注释在您的资源接口上定义
import jakarta.annotation.security.DenyAll;
import jakarta.annotation.security.RolesAllowed;
@DenyAll
@ResourceProperties
public interface PeopleResource extends PanacheEntityResource<Person, Long> {
@RolesAllowed("superuser")
boolean delete(Long id);
}
此外,如果您只对指定允许使用资源的角色感兴趣,则 @ResourceProperties
和 @MethodProperties
注释具有字段 rolesAllowed
来列出允许访问资源或操作的安全角色。
查询参数以列出实体
REST Data with Panache 支持以下查询参数来获取实体列表
-
page
- 列表操作应返回的页码。它仅适用于分页资源,并且是一个从 0 开始的数字。默认为 0。 -
size
- 列表操作应返回的页面大小。它仅适用于分页资源,并且是一个从 1 开始的数字。默认为 20。 -
sort
- 一个逗号分隔的字段列表,应使用这些字段来对列表操作的结果进行排序。字段按升序排序,除非它们以-
为前缀。例如,?sort=name,-age
将按名称升序,按年龄降序对结果进行排序。 -
namedQuery
- 应使用注释@NamedQuery
在实体级别配置的命名查询。
例如,如果您想在第一页中获取两个 People
实体,您应该调用 https://:8080/people?page=0&size=2
,并且响应应该如下所示
[
{
"id": 1,
"name": "John Johnson",
"birth": "1988-01-10"
},
{
"id": 2,
"name": "Peter Peterson",
"birth": "1986-11-20"
}
]
此外,您还可以通过添加具有字段名称和值的查询参数来按实体字段进行过滤,例如,调用 https://:8080/people?name=Peter Peterson
将返回
[
{
"id": 2,
"name": "Peter Peterson",
"birth": "1986-11-20"
}
]
仅对 String、Boolean、Character、Double、Float、Integer、Long、Short、Byte 和原始类型支持按字段过滤。 |
使用 @NamedQuery 进行复杂过滤以列出实体
您可以指定一个命名查询来在列出实体时进行过滤。例如,在您的实体中具有以下命名查询
@Entity
@NamedQuery(name = "Person.containsInName", query = "from Person where name like CONCAT('%', CONCAT(:name, '%'))")
public class Person extends PanacheEntity {
String name;
}
在此示例中,我们添加了一个命名查询来列出包含 name
字段中某些文本的所有人员。
接下来,我们可以设置一个查询参数 namedQuery
,在使用生成的资源列出实体时,使用我们要使用的命名查询的名称,例如,调用 https://:8080/people?namedQuery=Person.containsInName&name=ter
将返回名称包含文本“ter”的所有人员。
有关命名查询如何工作的更多信息,请转到Hibernate ORM指南或Hibernate Reactive指南。
资源方法 Before/After 监听器
REST Data with Panache 支持订阅以下资源方法挂钩
-
在添加资源之前/之后
-
在更新资源之前/之后
-
在删除资源之前/之后
要注册您的资源方法监听器,您需要提供一个实现接口 RestDataResourceMethodListener
的 bean,例如
@ApplicationScoped
public class PeopleRestDataResourceMethodListener implements RestDataResourceMethodListener<Person> {
@Override
public void onBeforeAdd(Person person) {
System.out.println("Before Save Person: " + person.name);
}
}
响应正文示例
如上所述,REST Data with Panache 支持 application/json
和 application/hal+json
响应内容类型。以下是一些假设数据库中有五个 Person
记录的 get
和 list
操作的响应正文示例。
GET /people/1
Accept: application/json
{
"id": 1,
"name": "John Johnson",
"birth": "1988-01-10"
}
Accept: application/hal+json
{
"id": 1,
"name": "John Johnson",
"birth": "1988-01-10",
"_links": {
"self": {
"href": "http://example.com/people/1"
},
"remove": {
"href": "http://example.com/people/1"
},
"update": {
"href": "http://example.com/people/1"
},
"add": {
"href": "http://example.com/people"
},
"list": {
"href": "http://example.com/people"
}
}
}
GET /people?page=0&size=2
Accept: application/json
[
{
"id": 1,
"name": "John Johnson",
"birth": "1988-01-10"
},
{
"id": 2,
"name": "Peter Peterson",
"birth": "1986-11-20"
}
]
Accept: application/hal+json
{
"_embedded": [
{
"id": 1,
"name": "John Johnson",
"birth": "1988-01-10",
"_links": {
"self": {
"href": "http://example.com/people/1"
},
"remove": {
"href": "http://example.com/people/1"
},
"update": {
"href": "http://example.com/people/1"
},
"add": {
"href": "http://example.com/people"
},
"list": {
"href": "http://example.com/people"
}
}
},
{
"id": 2,
"name": "Peter Peterson",
"birth": "1986-11-20",
"_links": {
"self": {
"href": "http://example.com/people/2"
},
"remove": {
"href": "http://example.com/people/2"
},
"update": {
"href": "http://example.com/people/2"
},
"add": {
"href": "http://example.com/people"
},
"list": {
"href": "http://example.com/people"
}
}
}
],
"_links": {
"add": {
"href": "http://example.com/people"
},
"list": {
"href": "http://example.com/people"
},
"first": {
"href": "http://example.com/people?page=0&size=2"
},
"last": {
"href": "http://example.com/people?page=2&size=2"
},
"next": {
"href": "http://example.com/people?page=1&size=2"
}
}
}
这两个响应也包含这些标头
-
Link: < http://example.com/people?page=0&size=2 >; rel="first"
-
Link: < http://example.com/people?page=2&size=2 >; rel="last"
-
Link: < http://example.com/people?page=1&size=2 >; rel="next"
不会包含 previous
链接标头(和 hal 链接),因为上一页不存在。
包括/排除 Jakarta REST 类
使用构建时条件
Quarkus 使得可以直接包含或排除 Jakarta REST 资源、提供程序和特性,这归功于与 CDI bean 相同的构建时条件。因此,可以使用配置文件条件 (@io.quarkus.arc.profile.IfBuildProfile
或 @io.quarkus.arc.profile.UnlessBuildProfile
) 和/或属性条件 (io.quarkus.arc.properties.IfBuildProperty
或 io.quarkus.arc.properties.UnlessBuildProperty
) 注释 REST Data with Panache 接口,以在构建时指示 Quarkus 应在哪些条件下包含生成的 Jakarta REST 类。
在以下示例中,当且仅当已启用构建配置文件 app1
时,Quarkus 才会包含从 PeopleResource
接口生成的资源。
@IfBuildProfile("app1")
public interface PeopleResource extends PanacheEntityResource<Person, Long> {
}
使用运行时属性
此选项仅在使用 Quarkus REST Quarkus 扩展时可用。 |
Quarkus 还可以使用 @io.quarkus.resteasy.reactive.server.EndpointDisabled
注释有条件地禁用基于运行时属性值的生成的 Jakarta REST 资源。
在以下示例中,如果在应用程序中将 some.property
配置为 "disable"
,Quarkus 将在运行时排除从 PeopleResource
接口生成的资源。
@EndpointDisabled(name = "some.property", stringValue = "disable")
public interface PeopleResource extends PanacheEntityResource<Person, Long> {
}