Springfox 全球响应 header
Springfox global response header
在我的 spring boot rest API 中,我为每个响应(无论方法如何)发回一个唯一的请求 ID header "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 选项的全球响应消息 - 我找不到任何全球响应 headers.
最终创建了一个注解来处理这个问题:
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 return 代码重复整个 @ApiResponse 注释块(显然可能还有其他 return 代码,但我只涵盖了显示的默认代码春狐)。如果有一种方法可以参数化整个 @ApiResponse 块,那就更好了。
我更新了我的 Docket 配置以在每个 API 上包含全局 header。希望这有帮助。
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);
我知道我来晚了,但我确实找到了一种使用反射向每个响应全局添加 header 的方法(可能不是必需的,但事实证明这是最简单的方法我得到每一个回应。您还可以检查所有 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();
}
}
}
在查看operation-builder的方法responseMessages()
后找到了这种方式。它基于 status-code 在内部合并 response-headers 并且逻辑本身将简单地将 header 添加到现有的 response-headers.
希望它对某人有所帮助,因为它不需要您对每个端点进行注释。
在我的 spring boot rest API 中,我为每个响应(无论方法如何)发回一个唯一的请求 ID header "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 选项的全球响应消息 - 我找不到任何全球响应 headers.
最终创建了一个注解来处理这个问题:
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 return 代码重复整个 @ApiResponse 注释块(显然可能还有其他 return 代码,但我只涵盖了显示的默认代码春狐)。如果有一种方法可以参数化整个 @ApiResponse 块,那就更好了。
我更新了我的 Docket 配置以在每个 API 上包含全局 header。希望这有帮助。
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);
我知道我来晚了,但我确实找到了一种使用反射向每个响应全局添加 header 的方法(可能不是必需的,但事实证明这是最简单的方法我得到每一个回应。您还可以检查所有 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();
}
}
}
在查看operation-builder的方法responseMessages()
后找到了这种方式。它基于 status-code 在内部合并 response-headers 并且逻辑本身将简单地将 header 添加到现有的 response-headers.
希望它对某人有所帮助,因为它不需要您对每个端点进行注释。