【问题标题】:Springfox global response headerSpringfox 全局响应头
【发布时间】:2018-09-28 06:35:30
【问题描述】:

在我的 Spring Boot Rest API 中,我为每个端点的每个响应(不考虑方法)发送回一个唯一的请求 ID 标头“x-request-id”。我可以使用类似这样的方式添加它:

@ApiResponses(value = { 
    @ApiResponse(
            code = 200, 
            message = "Successful status response", 
            responseHeaders = {
                    @ResponseHeader(
                            name = "x-request-id", 
                            description = "auto generated unique request id", 
                            response = String.class)})
})

这很好用,我可以在 Swagger UI 中看到它。但是,为每个端点执行此操作是一个繁琐的 + 维护问题。我希望在全球范围内执行此操作,但 Springfox documentation 仅使用 .globalResponseMessage 选项显示有关全局响应消息 - 我找不到全局响应标头的任何内容。

【问题讨论】:

    标签: swagger swagger-ui springfox


    【解决方案1】:

    最终创建了一个注释来处理这个问题:

    package com.abc.xyz.api.docs.annotations;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Inherited;
    import java.lang.annotation.Retention;
    import java.lang.annotation.Target;
    import io.swagger.annotations.ApiResponse;
    import io.swagger.annotations.ApiResponses;
    import io.swagger.annotations.ResponseHeader;
    
    import com.abc.xyz.api.constants.ApiConstants;
    
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @ApiResponses(value = { 
        @ApiResponse(
                code = 200, 
                message = "Successful status response",
                responseHeaders = {
                        @ResponseHeader(
                                name = ApiConstants.REQUESTIDHEADER,
                                description = ApiConstants.REQUESTIDDESCRIPTION, 
                                response = String.class)}),
        @ApiResponse(
                code = 401, 
                message = "Successful status response",
                responseHeaders = {
                        @ResponseHeader(
                                name = ApiConstants.REQUESTIDHEADER,
                                description = ApiConstants.REQUESTIDDESCRIPTION, 
                                response = String.class)}),
        @ApiResponse(
                code = 403, 
                message = "Successful status response",
                responseHeaders = {
                        @ResponseHeader(
                                name = ApiConstants.REQUESTIDHEADER,
                                description = ApiConstants.REQUESTIDDESCRIPTION, 
                                response = String.class)}),
        @ApiResponse(
                code = 404, 
                message = "Successful status response",
                responseHeaders = {
                        @ResponseHeader(
                                name = ApiConstants.REQUESTIDHEADER,
                                description = ApiConstants.REQUESTIDDESCRIPTION, 
                                response = String.class)}),
        }
    )
    public @interface RequestIdMethod {};
    

    有了这个,我可以在我的方法前面添加它作为标记注释:

    @RequestMapping(value = "/heartbeat", method = RequestMethod.GET)
    @RequestIdMethod
    public Heartbeat checkHeartbeat() {
        return new Heartbeat(status);
    }
    

    这不是很好,因为我需要为每个 http 返回代码重复整个 @ApiResponse 注释块(显然可能还有其他返回代码,但我只介绍了 Springfox 显示的默认代码)。如果有办法参数化整个 @ApiResponse 块会更好。

    【讨论】:

    • 嗨,Soumen,我在这个非常古老的帖子中添加了答案...希望对您有所帮助
    【解决方案2】:

    我更新了我的 Docket 配置以在每个 API 中包含 Global 标头。希望这会有所帮助。

    return new Docket(DocumentationType.SWAGGER_2)
        .apiInfo(new ApiInfoBuilder()
                .contact(new Contact("My Support", null, "My Email"))
                .description("My Description")
                .licenseUrl("My License")
                .title("My Title")
                .termsOfServiceUrl("My Terms and Conditions")
                .version("My Version")
                .build())
        .globalOperationParameters(Collections.singletonList(new ParameterBuilder()
                .name("x-request-id")
                .modelRef(new ModelRef("string"))
                .parameterType("header")
                .required(false)
                .build()))
        .select()
        .paths(PathSelectors.regex("/user*))
        .build()
        .directModelSubstitute(LocalDate.class, String.class)
        .directModelSubstitute(LocalDateTime.class, String.class);
    

    【讨论】:

    • 这会添加一个全局请求标头。不是响应标头。你能添加一个全局响应头吗?
    【解决方案3】:

    我知道我在这里聚会迟到了,但我确实找到了一种使用反射为每个响应全局添加标题的方法(可能不是必需的,但结果证明这是我获得每个响应的最简单方法。您还可以检查所有 ApiResponses 注释,但有些是隐式添加的,因此该方法省略了)。

    @Component
    @Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER + 10)
    public class RequestIdResponseHeaderPlugin implements OperationBuilderPlugin {
    
      @Override
      public boolean supports(DocumentationType documentationType) {
        return true;
      }
    
      @Override
      public void apply(OperationContext operationContext) {
        try {
          // we use reflection here since the operationBuilder.build() method would lead to different operation ids
          // and we only want to access the private field 'responseMessages' to add the request-id header to it
          Field f = operationContext.operationBuilder().getClass().getDeclaredField("responseMessages");
          f.setAccessible(true);
          Set<ResponseMessage> responseMessages = (Set<ResponseMessage>) f.get(operationContext.operationBuilder());
          responseMessages.forEach(message -> {
            int code = message.getCode();
            Map<String, Header> map = new HashMap<>();
            map.put("my-header-name", new Header(null, null, new ModelRef("string")));
            ResponseMessage responseMessage = new ResponseMessageBuilder().code(code).headersWithDescription(map).build();
            operationContext.operationBuilder().responseMessages(Collections.singleton(responseMessage));
          });
        } catch (NoSuchFieldException | IllegalAccessException e) {
          e.printStackTrace();
        }
      }
    }
    

    查看操作生成器的方法responseMessages()后发现这种方式。它在内部根据状态码合并响应标头,并且逻辑本身将简单地将标头添加到现有的响应标头。

    希望它对某人有所帮助,因为它不需要您注释每个端点。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-02-29
      • 2010-12-13
      • 2022-11-10
      • 2015-06-25
      • 2023-04-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多