You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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.yamlapi-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

火山引擎 最新活动