Springdoc 1.6.9结合Spring Boot 2.6.7:基于YAML定义API规范时自动获取部署环境Server URL的配置方法
解决SpringDoc加载外部YAML规范时自动填充Server URL的问题
针对你遇到的问题——用外部YAML定义API规范时,无法像注解方式那样自动填充当前部署环境的Server URL,这里有几个实用的解决方案,你可以根据自己的场景选择:
方案一:通过自定义OpenApiCustomiser动态注入Server地址
这个方案不需要修改你的外部YAML文件,而是在运行时动态替换或添加Server信息,适合不想改动现有规范文件的场景:
步骤1:编写自定义的OpenApiCustomiser组件
创建一个Spring组件,利用当前请求上下文获取部署环境的真实地址,然后注入到加载的OpenAPI实例中:
import jakarta.servlet.http.HttpServletRequest; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.servers.Server; import org.springdoc.core.customizers.OpenApiCustomiser; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; @Component public class DynamicServerCustomiser implements OpenApiCustomiser { @Override public void customise(OpenAPI openApi) { ServletRequestAttributes requestAttrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); if (requestAttrs == null) { return; } HttpServletRequest request = requestAttrs.getRequest(); // 处理反向代理场景下的真实地址(如果有用到Nginx等代理,记得开启X-Forwarded头传递) String scheme = request.getHeader("X-Forwarded-Proto") != null ? request.getHeader("X-Forwarded-Proto") : request.getScheme(); String host = request.getHeader("X-Forwarded-Host") != null ? request.getHeader("X-Forwarded-Host") : request.getServerName(); String port = request.getHeader("X-Forwarded-Port") != null ? request.getHeader("X-Forwarded-Port") : String.valueOf(request.getServerPort()); String contextPath = request.getContextPath(); String serverUrl = scheme + "://" + host + ":" + port + contextPath; Server server = new Server(); server.setUrl(serverUrl); // 如果外部YAML已经定义了Server,就替换第一个;否则直接添加 if (openApi.getServers() == null || openApi.getServers().isEmpty()) { openApi.addServersItem(server); } else { openApi.getServers().set(0, server); } } }
步骤2:保持原有application.yaml配置不变
你的现有配置不需要修改,springdoc会自动识别这个Customiser并应用到所有加载的外部API规范上。
方案二:在外部YAML中使用Spring占位符并解析
这个方案允许你在外部API规范文件中直接使用Spring的属性占位符,然后通过自定义Bean加载时替换这些占位符:
步骤1:修改外部API规范YAML
在api-1.yaml和api-2.yaml中,将Server部分替换为Spring占位符:
openapi: 3.0.1 info: title: API 1 Specification version: v1 servers: - url: "${server.scheme:http}://${server.host:localhost}:${server.port:8080}${server.servlet.context-path:}" # 其他API定义...
步骤2:在application.yaml中配置服务器属性
添加这些自定义属性(也可以直接用环境变量覆盖):
server: scheme: ${PROTOCOL:http} host: ${HOST:localhost} port: ${PORT:8080} servlet: context-path: ${CONTEXT_PATH:/} springdoc: api-docs: enabled: false swagger-ui: groups-order: ASC # 控制分组排序
步骤3:编写自定义Bean加载并解析YAML文件
创建一个配置类,加载外部YAML文件并替换占位符,注册为OpenAPI实例:
import io.swagger.v3.oas.models.OpenAPI; import org.springdoc.core.utils.SpringDocUtils; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; import org.springframework.util.FileCopyUtils; import java.io.IOException; import java.nio.charset.StandardCharsets; @Configuration public class ExternalOpenApiConfig { private final Environment environment; private final ResourceLoader resourceLoader; public ExternalOpenApiConfig(Environment environment, ResourceLoader resourceLoader) { this.environment = environment; this.resourceLoader = resourceLoader; } @Bean public OpenAPI api1Spec() throws IOException { return loadAndResolveOpenApi("classpath:api-spec/api-1.yaml"); } @Bean public OpenAPI api2Spec() throws IOException { return loadAndResolveOpenApi("classpath:api-spec/api-2.yaml"); } private OpenAPI loadAndResolveOpenApi(String resourcePath) throws IOException { Resource resource = resourceLoader.getResource(resourcePath); String yamlContent = FileCopyUtils.copyToString(resource.getInputStream(), StandardCharsets.UTF_8); // 让Spring解析所有占位符 String resolvedContent = environment.resolvePlaceholders(yamlContent); // 转换为OpenAPI对象 return SpringDocUtils.getConfig().getYamlMapper().readValue(resolvedContent, OpenAPI.class); } }
步骤4:移除swagger-ui中的urls配置
因为现在SpringDoc会自动发现注册的OpenAPI Bean,所以可以删掉原有配置中的springdoc.swagger-ui.urls部分。
方案三:使用SpringDoc默认Server配置(简单场景)
如果你的部署环境比较简单,没有反向代理等复杂场景,可以直接在application.yaml中配置默认Server URL,然后通过Customiser应用:
springdoc: api-docs: enabled: false swagger-ui: urls: - name: API 1 url: /api-spec/api-1.yaml - name: API 2 url: /api-spec/api-2.yaml default-server-url: "${server.scheme:http}://${server.host:localhost}:${server.port:8080}${server.servlet.context-path:}"
然后编写一个简化版的Customiser:
import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.servers.Server; import org.springdoc.core.customizers.OpenApiCustomiser; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Component public class DefaultServerCustomiser implements OpenApiCustomiser { @Value("${springdoc.default-server-url}") private String defaultServerUrl; @Override public void customise(OpenAPI openApi) { Server server = new Server(); server.setUrl(defaultServerUrl); if (openApi.getServers() == null || openApi.getServers().isEmpty()) { openApi.addServersItem(server); } else { openApi.getServers().set(0, server); } } }
这个方案配置简单,但不如方案一灵活,适合固定部署地址的场景。
内容的提问来源于stack exchange,提问作者davity




