我正在努力解决如何使用 Quarkus 和 Eclipse Microprofile 在我的 API 规范中重用 @Header 注释。我有很多方法返回对象的分页列表,我想传递有关
的信息作为 X-... 标题,目前看起来像
@GET
@Path("/obj1")
@Operation(description = "get filtered list of obj1")
@APIResponse(
responseCode = "200",
headers = { @Header(name = "X-Length"),@Header(name = "X-Offset") ,@Header(name = "X-Limit")},
content = @Content(
mediaType = MediaType.APPLICATION_JSON,
schema = @Schema(implementation = Obj1.class, type = SchemaType.ARRAY)
)
)
由于返回列表的每个方法的标题始终相同,因此我不想将它们复制并粘贴到每个方法上,因为我目前也不确定命名。
是否可以,如果可以,如何声明该数组一次并像这样重用它
paginationHeaders = { @Header(name = "X-Length"),@Header(name = "X-Offset") ,@Header(name = "X-Limit")}
...
@APIResponse(...
headers = paginationHeaders
是否有另一种标准化方法使用 Quarkus 返回分页信息(但不在响应对象中)?
我尝试通过 Google 搜索、浏览 Quarkus 文档并定义自定义注释。
首先我想提一下,将分页信息传递到 HTTP 标头并不是最好的主意。例如,防火墙或代理服务器可以修改(甚至删除)HTTP 标头。稍后我将展示我的首选解决方案。
但是,不完全支持重用标头常量,无论是 OpenAPI 还是 Quarkus,MicroProfile OpenAPI 规范(和 Smallrye OpenAPI 实现)都不支持以编程方式修改 OpenAPI 文档。
休息资源
@Path("/hello")
public class ExampleResource {
@Operation(
operationId = "listExamples",
summary = "List examples"
)
@APIResponse(
headers = @Header(name = "X-Paginated"),
responseCode = "200",
description = "Successful operation"
)
@APIResponse(
responseCode = "418",
description = "In case of Armageddon"
)
@GET
@Produces(MediaType.TEXT_PLAIN)
public List<String> filter() {
return List.of("Hello from RESTEasy Reactive");
}
@DELETE
public void remove() {
// Just to haven an other endpoint
}
}
listExamples
操作可能有两个响应,但唯一一个应包含分页响应标头。我引入了一个元标头 X-Paginated
来标记必须修改哪个响应。
通过实现自定义 OpenAPI 过滤器,可以以编程方式更改 OpenAPI 定义。
以下过滤器会将每次出现的
X-Paginated
标头更改为 X-Length
、X-Offset
和 X-Limit
三重。
public class PaginatedHeaderOASFilter implements OASFilter {
@Override
public APIResponse filterAPIResponse(APIResponse apiResponse) {
return Optional.of(apiResponse)
.filter(isPaginated)
.map(mapResponse)
.orElse(apiResponse);
}
private static final Predicate<APIResponse> isPaginated = response -> Optional
.ofNullable(response.getHeaders())
.map(map -> map.containsKey("X-Paginated"))
.orElse(false);
private static final UnaryOperator<APIResponse> mapResponse = response -> {
response.removeHeader("X-Paginated");
return response
.addHeader("X-Length", OASFactory.createHeader()
.description("Description of X-Length")
.schema(OASFactory.createSchema().type(Schema.SchemaType.INTEGER))
)
.addHeader("X-Offset", OASFactory.createHeader().description("Description of X-Offset"))
.addHeader("X-Limit", OASFactory.createHeader().description("Description of X-Limit"));
};
}
注意:不要忘记在应用程序配置文件中注册自定义 OASFilter,例如:
mp.openapi.filter=io.github.zforgo.PaginatedHeaderOASFilter
当我开始时,我更喜欢在响应正文中传递分页属性。幸运的是它可以重复使用,因为 Smallrye 支持通用响应类型。
Pagination
保存分页属性
public class Pagination {
protected long totalCount;
protected Integer pageCount;
protected Integer pageIndex;
protected Integer pageSize;
public Pagination(long totalCount, Integer pageCount, Integer pageIndex) {
this.totalCount = totalCount;
this.pageCount = pageCount;
this.pageIndex = pageIndex;
}
// getters / setters / others
}
FilterResult
包含结果列表以及分页
public class FilterResult<T> {
private final Pagination pagination;
private final List<T> items;
public FilterResult(List<T> items, Pagination pagination) {
this.items = items;
this.pagination = pagination;
}
@JsonCreator
public FilterResult(List<T> items, long totalCount, Integer pageCount, Integer pageIndex) {
this(items, new Pagination(totalCount, pageCount, pageIndex));
}
// getters / setters / others
}
现在,像
这样的一个点@GET
@Path("/orders")
@Produces(MediaType.APPLICATION_JSON)
public FilterResult<OrderDto> filterOrders() {
return null;
}
将在 OpenAPI 文档中生成为:
paths:
/orders:
get:
responses:
"200":
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/FilterResultOrderDto'
components:
schemas:
FilterResultOrderDto:
type: object
properties:
pagination:
$ref: '#/components/schemas/Pagination'
items:
type: array
items:
$ref: '#/components/schemas/OrderDto'
OrderDto:
type: object
properties:
billingAddress:
type: string
Pagination:
type: object
properties:
totalCount:
format: int64
type: integer
pageCount:
format: int32
type: integer
pageIndex:
format: int32
type: integer
pageSize:
format: int32
type: integer