OpenAPI 文件轉換器
轉換器會提供 API,用於透過使用者定義的自訂項目修改 OpenAPI 文件。 轉換器適用於如下案例:
- 將參數新增至文件中的所有作業。
- 修改參數或作業的描述。
- 將最上層資訊新增至 OpenAPI 文件。
轉換器分為三種類別:
- 文件轉換器可以存取整份 OpenAPI 文件。 這些可用來對文件進行全域修改。
- 作業轉換器會套用至每個個別作業。 每個個別作業都是路徑與 HTTP 方法的組合。 這些可用來修改端點上的參數或回應。
- 結構描述轉換器會套用至文件中每個結構描述。 這些可用來修改要求或回應主體的結構描述,或任何嵌套結構描述。
可以透過 AddDocumentTransformer 物件的 OpenApiOptions 呼叫方法,將轉換器註冊到文件上。 以下程式碼片段顯示了將轉換器註冊到文件的不同方法:
- 使用代理函式註冊文件轉換器。
- 使用 IOpenApiDocumentTransformer 的實例註冊文件轉換器。
- 使用 DI 啟動的 IOpenApiDocumentTransformer 註冊文件轉換器。
- 使用委派註冊作業轉換器。
- 使用 IOpenApiOperationTransformer 的實例來註冊作業轉換器。
- 使用已透過 DI 啟動的 IOpenApiOperationTransformer 來註冊作業轉換器。
- 藉由委派註冊架構轉換器。
- 使用 IOpenApiSchemaTransformer的執行個體註冊結構描述轉換器。
- 使用 DI 啟動的 IOpenApiSchemaTransformer註冊結構描述轉換器。
using Microsoft.AspNetCore.OpenApi;
using Microsoft.OpenApi;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOpenApi(options =>
{
options.AddDocumentTransformer((document, context, cancellationToken)
=> Task.CompletedTask);
options.AddDocumentTransformer(new MyDocumentTransformer());
options.AddDocumentTransformer<MyDocumentTransformer>();
options.AddOperationTransformer((operation, context, cancellationToken)
=> Task.CompletedTask);
options.AddOperationTransformer(new MyOperationTransformer());
options.AddOperationTransformer<MyOperationTransformer>();
options.AddSchemaTransformer((schema, context, cancellationToken)
=> Task.CompletedTask);
options.AddSchemaTransformer(new MySchemaTransformer());
options.AddSchemaTransformer<MySchemaTransformer>();
});
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
app.MapGet("/", () => "Hello world!");
app.Run();
轉換器的執行順序
轉換器會依下列順序執行:
- 架構轉換器會在架構註冊至檔時執行。 它們會依新增的順序執行。 所有架構都會在發生任何作業處理之前新增至檔,因此架構轉換器會在作業轉換程式之前執行。
- 作業轉換程式會在將作業新增至檔時執行。 它們會依新增的順序執行。 所有作業都會新增至文件後,才會執行任何文件轉換程式。
- 檔轉換程式會在產生檔時執行。 這是文件的最後一次傳遞,而此時會新增所有作業和架構。
- 當應用程式設定為產生多個 OpenAPI 檔時,轉換器會針對每個檔案獨立執行。
例如,在下列程式碼片段中:
-
SchemaTransformer2已執行,而且可以存取由SchemaTransformer1進行的修改。 -
OperationTransformer1和OperationTransformer2都能夠存取由兩個結構轉換器針對它們被呼叫以處理的作業所涉及的類型所做的修改。 -
OperationTransformer2在OperationTransformer1執行後執行,因此可以存取OperationTransformer1所做的修改。 -
DocumentTransformer1和DocumentTransformer2都會在所有作業和架構新增至文件後執行,因此可以存取作業和架構轉換器所做的所有修改。 -
DocumentTransformer2在DocumentTransformer1執行後執行,因此可以存取DocumentTransformer1所做的修改。
using Microsoft.AspNetCore.OpenApi;
using Microsoft.OpenApi;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOpenApi(options =>
{
options.AddDocumentTransformer<DocumentTransformer1>();
options.AddSchemaTransformer<SchemaTransformer1>();
options.AddDocumentTransformer<DocumentTransformer2>();
options.AddOperationTransformer<OperationTransformer1>();
options.AddSchemaTransformer<SchemaTransformer2>();
options.AddOperationTransformer<OperationTransformer2>();
});
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
app.MapGet("/", () => "Hello world!");
app.Run();
使用文件轉換器
文件轉換程式可以存取內容物件,其中包括:
- 正要修改的文件名稱。
- 與該文件相關聯的ApiDescriptionGroups。
- 產生文件時使用的 IServiceProvider。
文件轉換器也可變動產生的 OpenAPI 文件。 下列範例示範一個文件轉換器,其會將 API 的一些相關資訊新增至 OpenAPI 文件。
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Builder;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOpenApi(options =>
{
options.AddDocumentTransformer((document, context, cancellationToken) =>
{
document.Info = new()
{
Title = "Checkout API",
Version = "v1",
Description = "API for processing checkouts from cart."
};
return Task.CompletedTask;
});
});
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
app.MapGet("/", () => "Hello world!");
app.Run();
服務啟動的文件轉換器可以利用 DI 中的執行個體修改應用程式。 下列範例示範一個文件轉換器使用來自驗證層的 IAuthenticationSchemeProvider 服務。 它會檢查應用程式中是否已註冊任何 JWT 持有者相關的方案,並將其新增至 OpenAPI 文件的頂層。
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.OpenApi;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi;
var builder = WebApplication.CreateBuilder();
builder.Services.AddAuthentication().AddJwtBearer();
builder.Services.AddOpenApi(options =>
{
options.AddDocumentTransformer<BearerSecuritySchemeTransformer>();
});
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
app.MapGet("/", () => "Hello world!");
app.Run();
internal sealed class BearerSecuritySchemeTransformer(IAuthenticationSchemeProvider authenticationSchemeProvider) : IOpenApiDocumentTransformer
{
public async Task TransformAsync(OpenApiDocument document, OpenApiDocumentTransformerContext context, CancellationToken cancellationToken)
{
var authenticationSchemes = await authenticationSchemeProvider.GetAllSchemesAsync();
if (authenticationSchemes.Any(authScheme => authScheme.Name == "Bearer"))
{
var securitySchemes = new Dictionary<string, IOpenApiSecurityScheme>
{
["Bearer"] = new OpenApiSecurityScheme
{
Type = SecuritySchemeType.Http,
Scheme = "bearer", // "bearer" refers to the header name here
In = ParameterLocation.Header,
BearerFormat = "Json Web Token"
}
};
document.Components ??= new OpenApiComponents();
document.Components.SecuritySchemes = securitySchemes;
}
}
}
文件轉換器對與其相關聯的文件執行個體來說是唯一的。 在下列範例中,轉換器:
- 將驗證相關需求註冊至
internal文件。 - 將
public文件保持未修改狀態。
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.OpenApi;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi;
var builder = WebApplication.CreateBuilder();
builder.Services.AddAuthentication().AddJwtBearer();
builder.Services.AddOpenApi("internal", options =>
{
options.AddDocumentTransformer<BearerSecuritySchemeTransformer>();
});
builder.Services.AddOpenApi("public");
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
app.MapGet("/world", () => "Hello world!")
.WithGroupName("internal");
app.MapGet("/", () => "Hello universe!")
.WithGroupName("public");
app.Run();
internal sealed class BearerSecuritySchemeTransformer(IAuthenticationSchemeProvider authenticationSchemeProvider) : IOpenApiDocumentTransformer
{
public async Task TransformAsync(OpenApiDocument document, OpenApiDocumentTransformerContext context, CancellationToken cancellationToken)
{
var authenticationSchemes = await authenticationSchemeProvider.GetAllSchemesAsync();
if (authenticationSchemes.Any(authScheme => authScheme.Name == "Bearer"))
{
// Add the security scheme at the document level
var securitySchemes = new Dictionary<string, IOpenApiSecurityScheme>
{
["Bearer"] = new OpenApiSecurityScheme
{
Type = SecuritySchemeType.Http,
Scheme = "bearer", // "bearer" refers to the header name here
In = ParameterLocation.Header,
BearerFormat = "Json Web Token"
}
};
document.Components ??= new OpenApiComponents();
document.Components.SecuritySchemes = securitySchemes;
// Apply it as a requirement for all operations
foreach (var operation in document.Paths.Values.SelectMany(path => path.Operations))
{
operation.Value.Security ??= [];
operation.Value.Security.Add(new OpenApiSecurityRequirement
{
[new OpenApiSecuritySchemeReference("Bearer", document)] = []
});
}
}
}
}
使用運算轉換器
作業是 OpenAPI 文件中 HTTP 路徑與方法的獨特組合。 當需要進行修改時,操作變壓器會很有幫助。
- 應該對應用程式中的每個端點進行修改,或
- 有條件地套用至特定路由。
作業轉換程式可以存取上下文物件,其中包含:
- 作業所屬文件的名稱。
- 與作業關聯的 ApiDescription。
- 產生文件時使用的 IServiceProvider。
例如,下列作業轉換器會在文件中新增 500,作為所有作業支援的回應狀態碼。
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.OpenApi;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi;
var builder = WebApplication.CreateBuilder();
builder.Services.AddAuthentication().AddJwtBearer();
builder.Services.AddOpenApi(options =>
{
options.AddOperationTransformer((operation, context, cancellationToken) =>
{
operation.Responses ??= new OpenApiResponses();
operation.Responses.Add("500", new OpenApiResponse { Description = "Internal server error" });
return Task.CompletedTask;
});
});
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
app.MapGet("/", () => "Hello world!");
app.Run();
作業轉換器可以透過 API 新增至特定的 AddOpenApiOperationTransformer 端點,而非文件中的所有端點。 這對於變更特定端點的特定 OpenAPI 資料非常有用,例如新增安全性配置、回應描述或其他 OpenAPI 作業屬性。 下列範例示範如何將作業轉換器新增至已棄用的端點,這會在 OpenAPI 文件中將端點標示為已棄用。
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
var builder = WebApplication.CreateBuilder();
builder.Services.AddOpenApi();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
app.MapGet("/old", () => "This endpoint is old and should not be used anymore")
.AddOpenApiOperationTransformer((operation, context, cancellationToken) =>
{
operation.Deprecated = true;
return Task.CompletedTask;
});
app.MapGet("/new", () => "This endpoint replaces /old");
app.Run();
使用架構轉換器
模式是用於 OpenAPI 文件中要求和回應主體的資料模型。 當需要進行修改時,Schema 轉換器非常有用。
- 應該對文件中每一個結構描述進行修改,或
- 有條件地套用至特定架構。
結構轉換器可以存取一個上下文對象,其中包含:
- 架構所屬文件的名稱。
- 與目標結構描述相關的 JSON 類型資訊。
- 產生文件時使用的 IServiceProvider。
例如,下列結構描述轉換器會將十進位類型的 format 設定為 decimal,而不是 double:
using Microsoft.AspNetCore.OpenApi;
var builder = WebApplication.CreateBuilder();
builder.Services.AddOpenApi(options => {
// Schema transformer to set the format of decimal to 'decimal'
options.AddSchemaTransformer((schema, context, cancellationToken) =>
{
if (context.JsonTypeInfo.Type == typeof(decimal))
{
schema.Format = "decimal";
}
return Task.CompletedTask;
});
});
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
app.MapGet("/", () => new Body { Amount = 1.1m });
app.Run();
public class Body {
public decimal Amount { get; set; }
}
支援在轉換器中產生OpenApiSchemas
開發人員可以使用與 ASP.NET Core OpenAPI 檔產生相同的邏輯來產生 C# 類型的架構,並將其新增至 OpenAPI 檔。 架構接著可以從 OpenAPI 檔中的其他地方參考。 這項功能從 .NET 10 開始提供。
傳遞給文件、操作和結構轉換器的上下文包含了一個新的 GetOrCreateSchemaAsync 方法,可以用來生成某一型別的結構。
這個方法也有選擇性 ApiParameterDescription 參數,可指定所產生架構的其他元數據。
為了支援將架構新增至 OpenAPI 檔, Document 屬性已新增至 Operation 和 Schema transformer 內容。 這可讓任何轉換器使用檔的 AddComponent 方法,將架構新增至 OpenAPI 檔。
範例
若要在文件、作業或架構轉換程式中使用這項功能,請使用內容中提供的方法建立架構 GetOrCreateSchemaAsync ,並使用檔的 AddComponent 方法將它新增至 OpenAPI 檔。
builder.Services.AddOpenApi(options =>
{
options.AddOperationTransformer(async (operation, context, cancellationToken) =>
{
// Generate schema for error responses
var errorSchema = await context.GetOrCreateSchemaAsync(typeof(ProblemDetails), null, cancellationToken);
context.Document?.AddComponent("Error", errorSchema);
operation.Responses ??= new OpenApiResponses();
// Add a "4XX" response to the operation with the newly created schema
operation.Responses["4XX"] = new OpenApiResponse
{
Description = "Bad Request",
Content = new Dictionary<string, OpenApiMediaType>
{
["application/problem+json"] = new OpenApiMediaType
{
Schema = new OpenApiSchemaReference("Error", context.Document)
}
}
};
});
});
自定義架構重複使用
套用所有轉換程式之後,架構會逐一檢查文件,將特定架構傳送至 components.schemas 區段,並將其替換為指向已傳送架構的 $ref 參考。
這會減少檔的大小,並讓您更容易閱讀。
此處理的詳細數據很複雜,未來 .NET 版本可能會變更,但一般而言:
- 類別/記錄/結構類型的模式如果在文件中出現多次,會被取代為
$ref中的components.schemas。 - 基本類型和標準集合的模式將以內嵌方式保留。
- 列舉類型的架構總是會被替換為 components.schemas 中某一架構的
$ref。
一般而言,components.schemas 架構的名稱是類別/記錄/結構類型的名稱,但在某些情況下必須使用不同的名稱。
ASP.NET Core 允許您使用 $ref的 components.schemas 屬性,自訂用 CreateSchemaReferenceId 替換為 OpenApiOptions 中的某些架構。
這個屬性是一個委派,它接受 JsonTypeInfo 物件並返回應用於該類型的 components.schemas 架構名稱。
此框架提供了這個委派的預設實作,CreateDefaultSchemaReferenceId 使用型別的名稱,但您可以用您自己的實作來取代它。
作為此自定義的簡單範例,您可以選擇一律內嵌列舉架構。 若要這麼做,請將 CreateSchemaReferenceId 設定為會傳回 null 給列舉型別的委派;否則將從預設實作中傳回值。 下列程式代碼示範這一點:
builder.Services.AddOpenApi(options =>
{
// Always inline enum schemas
options.CreateSchemaReferenceId = (type) =>
type.Type.IsEnum ? null : OpenApiOptions.CreateDefaultSchemaReferenceId(type);
});
其他資源
OpenAPI 文件轉換器
轉換器會提供 API,用於透過使用者定義的自訂項目修改 OpenAPI 文件。 轉換器適用於如下案例:
- 將參數新增至文件中的所有作業。
- 修改參數或作業的描述。
- 將最上層資訊新增至 OpenAPI 文件。
轉換器分為三種類別:
- 文件轉換器可以存取整份 OpenAPI 文件。 這些可用來對文件進行全域修改。
- 作業轉換器會套用至每個個別作業。 每個個別作業都是路徑與 HTTP 方法的組合。 這些可用來修改端點上的參數或回應。
- 結構描述轉換器會套用至文件中每個結構描述。 這些可用來修改要求或回應主體的結構描述,或任何嵌套結構描述。
可以透過 AddDocumentTransformer 物件的 OpenApiOptions 呼叫方法,將轉換器註冊到文件上。 以下程式碼片段顯示了將轉換器註冊到文件的不同方法:
- 使用代理函式註冊文件轉換器。
- 使用 IOpenApiDocumentTransformer 的實例註冊文件轉換器。
- 使用 DI 啟動的 IOpenApiDocumentTransformer 註冊文件轉換器。
- 使用委派註冊作業轉換器。
- 使用 IOpenApiOperationTransformer 的實例來註冊作業轉換器。
- 使用已透過 DI 啟動的 IOpenApiOperationTransformer 來註冊作業轉換器。
- 藉由委派註冊架構轉換器。
- 使用 IOpenApiSchemaTransformer的執行個體註冊結構描述轉換器。
- 使用 DI 啟動的 IOpenApiSchemaTransformer註冊結構描述轉換器。
using Microsoft.AspNetCore.OpenApi;
using Microsoft.OpenApi.Models;
var builder = WebApplication.CreateBuilder();
builder.Services.AddOpenApi(options =>
{
options.AddDocumentTransformer((document, context, cancellationToken)
=> Task.CompletedTask);
options.AddDocumentTransformer(new MyDocumentTransformer());
options.AddDocumentTransformer<MyDocumentTransformer>();
options.AddOperationTransformer((operation, context, cancellationToken)
=> Task.CompletedTask);
options.AddOperationTransformer(new MyOperationTransformer());
options.AddOperationTransformer<MyOperationTransformer>();
options.AddSchemaTransformer((schema, context, cancellationToken)
=> Task.CompletedTask);
options.AddSchemaTransformer(new MySchemaTransformer());
options.AddSchemaTransformer<MySchemaTransformer>();
});
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
app.MapGet("/", () => "Hello world!");
app.Run();
轉換器的執行順序
轉換器的執行方式如下:
- 架構轉換程式會在架構註冊至文件時執行。 架構轉換程式會依新增的順序執行。 所有架構都會在任何作業處理發生之前新增至檔,因此所有架構轉換器都會在任何作業轉換程式之前執行。
- 作業轉換程式會在將作業新增至檔時執行。 作業轉換程式會依新增的順序執行。 在執行任何文件轉換程式之前,所有作業都會新增至檔案。
- 檔轉換程式會在產生檔時執行。 這是文件的最後一次傳遞,此時已新增所有作業和架構。
- 當應用程式設定為產生多個 OpenAPI 檔時,會針對每個檔案獨立執行轉換器。
例如,在下列程式碼片段中:
-
SchemaTransformer2已執行,而且可以存取由SchemaTransformer1進行的修改。 -
OperationTransformer1和OperationTransformer2都能夠存取架構轉換程式針對它們所呼叫之操作所涉及類型所做的修改。 -
OperationTransformer2在OperationTransformer1執行後執行,因此可以存取OperationTransformer1所做的修改。 -
DocumentTransformer1和DocumentTransformer2都會在所有作業和架構新增至文件後執行,因此可以存取作業和架構轉換器所做的所有修改。 -
DocumentTransformer2在DocumentTransformer1執行後執行,因此可以存取DocumentTransformer1所做的修改。
using Microsoft.AspNetCore.OpenApi;
using Microsoft.OpenApi.Models;
var builder = WebApplication.CreateBuilder();
builder.Services.AddOpenApi(options =>
{
options.AddDocumentTransformer<DocumentTransformer1>();
options.AddSchemaTransformer<SchemaTransformer1>();
options.AddDocumentTransformer<DocumentTransformer2>();
options.AddOperationTransformer<OperationTransformer1>();
options.AddSchemaTransformer<SchemaTransformer2>();
options.AddOperationTransformer<OperationTransformer2>();
});
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
app.MapGet("/", () => "Hello world!");
app.Run();
使用文件轉換器
文件轉換程式可以存取內容物件,其中包括:
- 正要修改的文件名稱。
- 與該文件相關聯的ApiDescriptionGroups。
- 產生文件時使用的 IServiceProvider。
文件轉換器也可變動產生的 OpenAPI 文件。 下列範例示範一個文件轉換器,其會將 API 的一些相關資訊新增至 OpenAPI 文件。
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Builder;
var builder = WebApplication.CreateBuilder();
builder.Services.AddOpenApi(options =>
{
options.AddDocumentTransformer((document, context, cancellationToken) =>
{
document.Info = new()
{
Title = "Checkout API",
Version = "v1",
Description = "API for processing checkouts from cart."
};
return Task.CompletedTask;
});
});
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
app.MapGet("/", () => "Hello world!");
app.Run();
服務啟動的文件轉換器可以利用 DI 中的執行個體修改應用程式。 下列範例示範一個文件轉換器使用來自驗證層的 IAuthenticationSchemeProvider 服務。 它會檢查應用程式中是否已註冊任何 JWT 持有者相關的方案,並將其新增至 OpenAPI 文件的頂層。
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.OpenApi;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;
var builder = WebApplication.CreateBuilder();
builder.Services.AddAuthentication().AddJwtBearer();
builder.Services.AddOpenApi(options =>
{
options.AddDocumentTransformer<BearerSecuritySchemeTransformer>();
});
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
app.MapGet("/", () => "Hello world!");
app.Run();
internal sealed class BearerSecuritySchemeTransformer(IAuthenticationSchemeProvider authenticationSchemeProvider) : IOpenApiDocumentTransformer
{
public async Task TransformAsync(OpenApiDocument document, OpenApiDocumentTransformerContext context, CancellationToken cancellationToken)
{
var authenticationSchemes = await authenticationSchemeProvider.GetAllSchemesAsync();
if (authenticationSchemes.Any(authScheme => authScheme.Name == "Bearer"))
{
var requirements = new Dictionary<string, OpenApiSecurityScheme>
{
["Bearer"] = new OpenApiSecurityScheme
{
Type = SecuritySchemeType.Http,
Scheme = "bearer", // "bearer" refers to the header name here
In = ParameterLocation.Header,
BearerFormat = "Json Web Token"
}
};
document.Components ??= new OpenApiComponents();
document.Components.SecuritySchemes = requirements;
}
}
}
文件轉換器對與其相關聯的文件執行個體來說是唯一的。 在下列範例中,轉換器:
- 將驗證相關需求註冊至
internal文件。 - 將
public文件保持未修改狀態。
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.OpenApi;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;
var builder = WebApplication.CreateBuilder();
builder.Services.AddAuthentication().AddJwtBearer();
builder.Services.AddOpenApi("internal", options =>
{
options.AddDocumentTransformer<BearerSecuritySchemeTransformer>();
});
builder.Services.AddOpenApi("public");
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
app.MapGet("/world", () => "Hello world!")
.WithGroupName("internal");
app.MapGet("/", () => "Hello universe!")
.WithGroupName("public");
app.Run();
internal sealed class BearerSecuritySchemeTransformer(IAuthenticationSchemeProvider authenticationSchemeProvider) : IOpenApiDocumentTransformer
{
public async Task TransformAsync(OpenApiDocument document, OpenApiDocumentTransformerContext context, CancellationToken cancellationToken)
{
var authenticationSchemes = await authenticationSchemeProvider.GetAllSchemesAsync();
if (authenticationSchemes.Any(authScheme => authScheme.Name == "Bearer"))
{
// Add the security scheme at the document level
var requirements = new Dictionary<string, OpenApiSecurityScheme>
{
["Bearer"] = new OpenApiSecurityScheme
{
Type = SecuritySchemeType.Http,
Scheme = "bearer", // "bearer" refers to the header name here
In = ParameterLocation.Header,
BearerFormat = "Json Web Token"
}
};
document.Components ??= new OpenApiComponents();
document.Components.SecuritySchemes = requirements;
// Apply it as a requirement for all operations
foreach (var operation in document.Paths.Values.SelectMany(path => path.Operations))
{
operation.Value.Security.Add(new OpenApiSecurityRequirement
{
[new OpenApiSecurityScheme { Reference = new OpenApiReference { Id = "Bearer", Type = ReferenceType.SecurityScheme } }] = Array.Empty<string>()
});
}
}
}
}
使用運算轉換器
作業是 OpenAPI 文件中 HTTP 路徑與方法的獨特組合。 當需要進行修改時,操作變壓器會很有幫助。
- 應該對應用程式中的每個端點進行修改,或
- 有條件地套用至特定路由。
作業轉換程式可以存取上下文物件,其中包含:
- 作業所屬文件的名稱。
- 與作業關聯的 ApiDescription。
- 產生文件時使用的 IServiceProvider。
例如,下列作業轉換器會在文件中新增 500,作為所有作業支援的回應狀態碼。
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.OpenApi;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;
var builder = WebApplication.CreateBuilder();
builder.Services.AddAuthentication().AddJwtBearer();
builder.Services.AddOpenApi(options =>
{
options.AddOperationTransformer((operation, context, cancellationToken) =>
{
operation.Responses.Add("500", new OpenApiResponse { Description = "Internal server error" });
return Task.CompletedTask;
});
});
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
app.MapGet("/", () => "Hello world!");
app.Run();
使用架構轉換器
模式是用於 OpenAPI 文件中要求和回應主體的資料模型。 當需要進行修改時,Schema 轉換器非常有用。
- 應該對文件中每一個結構描述進行修改,或
- 有條件地套用至特定架構。
結構轉換器可以存取一個上下文對象,其中包含:
- 架構所屬文件的名稱。
- 與目標結構描述相關的 JSON 類型資訊。
- 產生文件時使用的 IServiceProvider。
例如,下列結構描述轉換器會將十進位類型的 format 設定為 decimal,而不是 double:
using Microsoft.AspNetCore.OpenApi;
var builder = WebApplication.CreateBuilder();
builder.Services.AddOpenApi(options => {
// Schema transformer to set the format of decimal to 'decimal'
options.AddSchemaTransformer((schema, context, cancellationToken) =>
{
if (context.JsonTypeInfo.Type == typeof(decimal))
{
schema.Format = "decimal";
}
return Task.CompletedTask;
});
});
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
app.MapGet("/", () => new Body { Amount = 1.1m });
app.Run();
public class Body {
public decimal Amount { get; set; }
}
自定義架構重複使用
套用所有轉換程式之後,架構會逐一檢查文件,將特定架構傳送至 components.schemas 區段,並將其替換為指向已傳送架構的 $ref 參考。
這會減少檔的大小,並讓您更容易閱讀。
此處理的詳細數據很複雜,未來 .NET 版本可能會變更,但一般而言:
- 類別/記錄/結構類型的模式如果在文件中出現多次,會被取代為
$ref中的components.schemas。 - 基本類型和標準集合的模式將以內嵌方式保留。
- 列舉類型的架構總是會被替換為 components.schemas 中某一架構的
$ref。
一般而言,components.schemas 架構的名稱是類別/記錄/結構類型的名稱,但在某些情況下必須使用不同的名稱。
ASP.NET Core 允許您使用 $ref的 components.schemas 屬性,自訂用 CreateSchemaReferenceId 替換為 OpenApiOptions 中的某些架構。
這個屬性是一個委派,它接受 JsonTypeInfo 物件並返回應用於該類型的 components.schemas 架構名稱。
此框架提供了這個委派的預設實作,CreateDefaultSchemaReferenceId 使用型別的名稱,但您可以用您自己的實作來取代它。
作為此自定義的簡單範例,您可以選擇一律內嵌列舉架構。 若要這麼做,請將 CreateSchemaReferenceId 設定為會傳回 null 給列舉型別的委派;否則將從預設實作中傳回值。 下列程式代碼示範如何執行這項操作:
builder.Services.AddOpenApi(options =>
{
// Always inline enum schemas
options.CreateSchemaReferenceId = (type) =>
type.Type.IsEnum ? null : OpenApiOptions.CreateDefaultSchemaReferenceId(type);
});