在 REST API 声明中重用 Eclipse Microprofile @Header 定义

问题描述 投票:0回答:1

我正在努力解决如何使用 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 文档并定义自定义注释。

java rest header quarkus microprofile
1个回答
0
投票

首先我想提一下,将分页信息传递到 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
© www.soinside.com 2019 - 2024. All rights reserved.