加载含外部Schema的OpenAPI文档失败,求无需修改原文档的解决办法
加载缺失Version字段的外部OpenAPI Schema失败的变通方案
问题背景
尝试加载第三方OpenAPI文档时,其引用的外部Schema文件因缺失Version字段导致加载失败,报错信息如下:
[File: schema.json] Version node not found.
Schema unresolved reference: True
原运行代码:
using Microsoft.OpenApi; using Microsoft.OpenApi.Reader; namespace Dasfn { internal class Program { static async Task Main(string[] args) { Uri baseUrl = new("https://www.bcb.gov.br/htms/dasfn/catalogo/1.0.10/"); OpenApiReaderSettings settings = new() { BaseUrl = baseUrl, LoadExternalRefs = true, }; ReadResult readResult = await OpenApiDocument.LoadAsync(new Uri(baseUrl, "openapi.json").AbsoluteUri, settings); if (readResult.Diagnostic?.Errors is IList<OpenApiError> errors) { foreach (OpenApiError error in errors) { Console.WriteLine(error); } } OpenApiDocument document = readResult.Document!; OpenApiSchemaReference schema = (OpenApiSchemaReference) document .Paths["/catalogo"] .Operations![HttpMethod.Get] .Responses!["200"] .Content!["application/json"].Schema! ; Console.WriteLine($"Schema unresolved reference: {schema.UnresolvedReference}"); } } }
由于无法修改原OpenAPI文档及外部Schema,可通过以下方案解决:
方案1:自定义外部引用解析器,补全Version字段
实现IOpenApiExternalReferenceResolver接口,在加载外部Schema内容时手动添加缺失的Version节点(需匹配原OpenAPI文档的版本,比如原文档用3.0.0则添加对应版本),再交给官方解析器处理。
示例代码:
using Microsoft.OpenApi; using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Readers; using System.Text.Json; namespace Dasfn { // 自定义外部引用解析器 public class CustomSchemaResolver : IOpenApiExternalReferenceResolver { private readonly OpenApiExternalReferenceResolver _defaultResolver; private readonly string _targetOpenApiVersion; public CustomSchemaResolver(OpenApiReaderSettings settings, string openApiVersion) { _defaultResolver = new OpenApiExternalReferenceResolver(settings); _targetOpenApiVersion = openApiVersion; } public async Task<OpenApiDocument> ResolveReferenceAsync(string reference, OpenApiDocument rootDocument, OpenApiReaderSettings settings) { // 获取远程Schema内容 using var client = new HttpClient(); var schemaContent = await client.GetStringAsync(reference); // 解析JSON并添加缺失的Version字段 var jsonDoc = JsonDocument.Parse(schemaContent); using var ms = new MemoryStream(); using var writer = new Utf8JsonWriter(ms); writer.WriteStartObject(); // 添加Version节点 writer.WriteString("openapi", _targetOpenApiVersion); // 复制原Schema的所有内容 foreach (var property in jsonDoc.RootElement.EnumerateObject()) { property.WriteTo(writer); } writer.WriteEndObject(); writer.Flush(); ms.Seek(0, SeekOrigin.Begin); // 用修改后的内容加载文档 var reader = new OpenApiStreamReader(settings); var result = reader.Read(ms, out var diagnostic); return result; } } internal class Program { static async Task Main(string[] args) { Uri baseUrl = new("https://www.bcb.gov.br/htms/dasfn/catalogo/1.0.10/"); // 原OpenAPI文档的版本,可从openapi.json中获取,这里假设是3.0.0 string openApiVersion = "3.0.0"; OpenApiReaderSettings settings = new() { BaseUrl = baseUrl, LoadExternalRefs = true, }; // 设置自定义解析器 settings.ExternalReferenceResolver = new CustomSchemaResolver(settings, openApiVersion); ReadResult readResult = await OpenApiDocument.LoadAsync(new Uri(baseUrl, "openapi.json").AbsoluteUri, settings); if (readResult.Diagnostic?.Errors is IList<OpenApiError> errors) { foreach (OpenApiError error in errors) { Console.WriteLine(error); } } OpenApiDocument document = readResult.Document!; var schema = document .Paths["/catalogo"] .Operations![HttpMethod.Get] .Responses!["200"] .Content!["application/json"].Schema!; // 输出是否为未解析引用 Console.WriteLine($"Is schema reference unresolved: {schema is OpenApiSchemaReference refSchema && refSchema.UnresolvedReference}"); } } }
方案2:预加载并修改外部Schema,替换文档引用
先下载外部Schema文件,添加Version字段后,手动替换原OpenAPI文档中的外部引用为修改后的Schema内容,再加载文档。
示例代码:
using Microsoft.OpenApi; using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Readers; using System.Text.Json; namespace Dasfn { internal class Program { static async Task Main(string[] args) { Uri baseUrl = new("https://www.bcb.gov.br/htms/dasfn/catalogo/1.0.10/"); string openApiVersion = "3.0.0"; // 1. 下载并修改外部Schema using var client = new HttpClient(); string schemaContent = await client.GetStringAsync(new Uri(baseUrl, "schema.json")); var jsonObj = JsonSerializer.Deserialize<Dictionary<string, object>>(schemaContent)!; jsonObj["openapi"] = openApiVersion; string modifiedSchemaContent = JsonSerializer.Serialize(jsonObj); // 2. 加载原OpenAPI文档(不加载外部引用) var readerSettings = new OpenApiReaderSettings { LoadExternalRefs = false }; var openApiDoc = await OpenApiDocument.LoadAsync(new Uri(baseUrl, "openapi.json").AbsoluteUri, readerSettings); // 3. 解析修改后的Schema var schemaReader = new OpenApiStringReader(); var modifiedSchemaDoc = schemaReader.Read(modifiedSchemaContent, out _); var targetSchema = modifiedSchemaDoc.Components.Schemas.Values.First(); // 根据实际Schema结构获取目标Schema // 4. 替换原文档中的引用 openApiDoc.Paths["/catalogo"] .Operations[HttpMethod.Get] .Responses["200"] .Content["application/json"].Schema = targetSchema; Console.WriteLine("Schema已成功加载,未标记为未解析引用"); } } }
内容的提问来源于stack exchange,提问作者Alfred Myers
相关产品推荐
相关产品推荐

