色综合图-色综合图片-色综合图片二区150p-色综合图区-玖玖国产精品视频-玖玖香蕉视频

您的位置:首頁(yè)技術(shù)文章
文章詳情頁(yè)

源碼分析MinimalApi是如何在Swagger中展示

瀏覽:199日期:2022-06-04 15:04:03
目錄
  • 前言
  • 使用方式
  • 源碼探究
  • swagger的數(shù)據(jù)源
  • ASP.Net Core如何提供
  • 源碼小結(jié)
  • 使用擴(kuò)展
  • 總結(jié)

前言

之前看到技術(shù)群里有同學(xué)討論說(shuō)對(duì)于MinimalApi能接入到Swagger中感到很神奇,加上Swagger的數(shù)據(jù)本身是支持OpenApi2.0OpenApi3.0使得swagger.json成為了許多接口文檔管理工具的標(biāo)準(zhǔn)數(shù)據(jù)源。

ASP.NET Core能夠輕松快速的集成Swagger得益于微軟對(duì)OpenApi的大力支持,大部分情況下幾乎是添加默認(rèn)配置,就能很好的工作了。這一切都是得益于ASP.NET Core底層提供了對(duì)接口元數(shù)據(jù)的描述和對(duì)終結(jié)點(diǎn)的相關(guān)描述。本文我們就通過(guò)MinimalApi來(lái)了解一下ASP.NET Core為何能更好的集成Swagger。

使用方式

雖然我們討論的是MInimalApi與Swagger數(shù)據(jù)源的關(guān)系,但是為了使得看起來(lái)更清晰,我們還是先看一下MinimalApi如何集成到Swagger,直接上代碼

var builder = WebApplication.CreateBuilder(args);
//這是重點(diǎn),是ASP.NET Core自身提供的
builder.Services.AddEndpointsApiExplorer();
//添加swagger配置
builder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new() 
    { 
	Title = builder.Environment.ApplicationName,
	Version = "v1"
    });
});
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
    //swagger終結(jié)點(diǎn)
    app.UseSwagger();
    app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", 
  $"{builder.Environment.ApplicationName} v1"));
}
app.MapGet("/swag", () => "Hello Swagger!");
app.Run();

上面我們提到了AddEndpointsApiExplorer是ASP.NET Core自身提供的,但是如果使得MinimalApi能在Swagger中展示就必須要添加這個(gè)服務(wù)。所以Swagger還是那個(gè)Swagger,變的是ASP.NET Core本身,但是變化是如何適配數(shù)據(jù)源的問(wèn)題,Swagger便是建立在這個(gè)便利基礎(chǔ)上。接下來(lái)咱們就通過(guò)源碼看一下它們之間的關(guān)系。

源碼探究

想了解它們的關(guān)系就會(huì)涉及到兩個(gè)主角,一個(gè)是swagger的數(shù)據(jù)源來(lái)自何處,另一個(gè)是ASP.NET Core是如何提供這個(gè)數(shù)據(jù)源的。首先我們來(lái)看一下Swagger的數(shù)據(jù)源來(lái)自何處。

swagger的數(shù)據(jù)源

熟悉Swashbuckle.AspNetCore的應(yīng)該知道它其實(shí)是由幾個(gè)程序集一起構(gòu)建的,也就是說(shuō)Swashbuckle.AspNetCore本身是一個(gè)解決方案,不過(guò)這不是重點(diǎn),其中生成Swagger.json的是在Swashbuckle.AspNetCore.SwaggerGen程序集中,直接找到位置在SwaggerGenerator類中[點(diǎn)擊查看源碼]只摘要我們關(guān)注的地方即可

public class SwaggerGenerator : ISwaggerProvider
{
    private readonly IApiDescriptionGroupCollectionProvider _apiDescriptionsProvider;
    private readonly ISchemaGenerator _schemaGenerator;
    private readonly SwaggerGeneratorOptions _options;
    public SwaggerGenerator(
SwaggerGeneratorOptions options,
IApiDescriptionGroupCollectionProvider apiDescriptionsProvider,
ISchemaGenerator schemaGenerator)
    {
_options = options ?? new SwaggerGeneratorOptions();
_apiDescriptionsProvider = apiDescriptionsProvider;
_schemaGenerator = schemaGenerator;
    }
    /// <summary>
    /// 獲取Swagger文檔的核心方法
    /// </summary>
    public OpenApiDocument GetSwagger(string documentName, string host = null, string basePath = null)
    {
if (!_options.SwaggerDocs.TryGetValue(documentName, out OpenApiInfo info))
    throw new UnknownSwaggerDocument(documentName, _options.SwaggerDocs.Select(d => d.Key));
//組裝OpenApiDocument核心數(shù)據(jù)源源來(lái)自_apiDescriptionsProvider
var applicableApiDescriptions = _apiDescriptionsProvider.ApiDescriptionGroups.Items
    .SelectMany(group => group.Items)
    .Where(apiDesc => !(_options.IgnoreObsoleteActions && apiDesc.CustomAttributes().OfType<ObsoleteAttribute().Any()))
    .Where(apiDesc => _options.DocInclusionPredicate(documentName, apiDesc));
var schemaRepository = new SchemaRepository(documentName);
var swaggerDoc = new OpenApiDocument
{
    Info = info,
    Servers = GenerateServers(host, basePath),
    // Paths組裝是來(lái)自applicableApiDescriptions
    Paths = GeneratePaths(applicableApiDescriptions, schemaRepository),
    Components = new OpenApiComponents
    {
Schemas = schemaRepository.Schemas,
SecuritySchemes = new Dictionary<string, OpenApiSecurityScheme>(_options.SecuritySchemes)
    },
    SecurityRequirements = new List<OpenApiSecurityRequirement>(_options.SecurityRequirements)
};
//省略其他代碼
return swaggerDoc;
    }
}

如果你比較了解Swagger.json的話那么對(duì)OpenApiDocument這個(gè)類的結(jié)構(gòu)一定是一目了然,不信的話你可以自行看看它的結(jié)構(gòu)

{
  "openapi": "3.0.1",
  "info": {
    "title": "MyTest.WebApi",
    "description": "測(cè)試接口",
    "version": "v1"
  },
  "paths": {
    "/": {
      "get": {
"tags": [
  "MyTest.WebApi"
],
"responses": {
  "200": {
    "description": "Success",
    "content": {
      "text/plain": {
"schema": {
  "type": "string"
}
      }
    }
  }
}
      }
    }
  },
  "components": {}
}

這么看清晰了吧OpenApiDocument這個(gè)類就是返回Swagger.json的模型類,而承載描述接口信息的核心字段paths正是來(lái)自IApiDescriptionGroupCollectionProvider。所以小結(jié)一下,Swagger接口的文檔信息的數(shù)據(jù)源來(lái)自于IApiDescriptionGroupCollectionProvider。

ASP.Net Core如何提供

通過(guò)上面在Swashbuckle.AspNetCore.SwaggerGen程序集中,我們看到了真正組裝Swagger接口文檔部分的數(shù)據(jù)源來(lái)自于IApiDescriptionGroupCollectionProvider,但是這個(gè)接口并非來(lái)自Swashbuckle而是來(lái)自ASP.NET Core。這就引入了另一個(gè)主角,也是我們上面提到的AddEndpointsApiExplorer方法。直接在dotnet/aspnetcore倉(cāng)庫(kù)里找到方法位置[點(diǎn)擊查看源碼]看一下方法實(shí)現(xiàn)

public static IServiceCollection AddEndpointsApiExplorer(this IServiceCollection services)
{
    services.TryAddSingleton<IActionDescriptorCollectionProvider, DefaultActionDescriptorCollectionProvider>();
    //swagger用到的核心操作IApiDescriptionGroupCollectionProvider
    services.TryAddSingleton<IApiDescriptionGroupCollectionProvider, ApiDescriptionGroupCollectionProvider>();
    services.TryAddEnumerable(
ServiceDescriptor.Transient<IApiDescriptionProvider, EndpointMetadataApiDescriptionProvider>());
    return services;
}

看到了AddEndpointsApiExplorer方法相信就明白了為啥要添加這個(gè)方法了吧,那你就有疑問(wèn)了為啥不使用MinimalApi的時(shí)候就不用引入AddEndpointsApiExplorer這個(gè)方法了,況且也能使用swagger。這是因?yàn)樵?code>AddControllers方法里添加了AddApiExplorer方法,這個(gè)方法里包含了針對(duì)Controller的接口描述信息,這里就不過(guò)多說(shuō)了,畢竟這種的核心是MinimalApi。接下來(lái)就看下IApiDescriptionGroupCollectionProvider接口的默認(rèn)實(shí)現(xiàn)ApiDescriptionGroupCollectionProvider類里的實(shí)現(xiàn)[點(diǎn)擊查看源碼]

public class ApiDescriptionGroupCollectionProvider : IApiDescriptionGroupCollectionProvider
{
	private readonly IActionDescriptorCollectionProvider _actionDescriptorCollectionProvider;
	private readonly IApiDescriptionProvider[] _apiDescriptionProviders;
	private ApiDescriptionGroupCollection? _apiDescriptionGroups;
	public ApiDescriptionGroupCollectionProvider(
		IActionDescriptorCollectionProvider actionDescriptorCollectionProvider,
		IEnumerable<IApiDescriptionProvider> apiDescriptionProviders)
	{
		_actionDescriptorCollectionProvider = actionDescriptorCollectionProvider;
		_apiDescriptionProviders = apiDescriptionProviders.OrderBy(item => item.Order).ToArray();
	}
	public ApiDescriptionGroupCollection ApiDescriptionGroups
	{
		get
		{
			var actionDescriptors = _actionDescriptorCollectionProvider.ActionDescriptors;
			if (_apiDescriptionGroups == null || _apiDescriptionGroups.Version != actionDescriptors.Version)
			{
				//如果_apiDescriptionGroups為null則使用GetCollection方法返回的數(shù)據(jù)
				_apiDescriptionGroups = GetCollection(actionDescriptors);
			}
			return _apiDescriptionGroups;
		}
	}
	private ApiDescriptionGroupCollection GetCollection(ActionDescriptorCollection actionDescriptors)
	{
		var context = new ApiDescriptionProviderContext(actionDescriptors.Items);
		//這里使用了_apiDescriptionProviders
		foreach (var provider in _apiDescriptionProviders)
		{
			provider.OnProvidersExecuting(context);
		}
		for (var i = _apiDescriptionProviders.Length - 1; i >= 0; i--)
		{
			_apiDescriptionProviders[i].OnProvidersExecuted(context);
		}
		var groups = context.Results
			.GroupBy(d => d.GroupName)
			.Select(g => new ApiDescriptionGroup(g.Key, g.ToArray()))
			.ToArray();
		return new ApiDescriptionGroupCollection(groups, actionDescriptors.Version);
	}
}

這里我們看到了IApiDescriptionProvider[]通過(guò)上面的方法我們可以知道IApiDescriptionProvider默認(rèn)實(shí)現(xiàn)是EndpointMetadataApiDescriptionProvider類[點(diǎn)擊查看源碼]看一下相實(shí)現(xiàn)

internal class EndpointMetadataApiDescriptionProvider : IApiDescriptionProvider
{
    private readonly EndpointDataSource _endpointDataSource;
    private readonly IHostEnvironment _environment;
    private readonly IServiceProviderIsService? _serviceProviderIsService;
    private readonly ParameterBindingMethodCache ParameterBindingMethodCache = new();
    public EndpointMetadataApiDescriptionProvider(
EndpointDataSource endpointDataSource,
IHostEnvironment environment,
IServiceProviderIsService? serviceProviderIsService)
    {
_endpointDataSource = endpointDataSource;
_environment = environment;
_serviceProviderIsService = serviceProviderIsService;
    }
    public void OnProvidersExecuting(ApiDescriptionProviderContext context)
    {
//核心數(shù)據(jù)來(lái)自EndpointDataSource類
foreach (var endpoint in _endpointDataSource.Endpoints)
{
    if (endpoint is RouteEndpoint routeEndpoint &&
routeEndpoint.Metadata.GetMetadata<MethodInfo>() is { } methodInfo &&
routeEndpoint.Metadata.GetMetadata<IHttpMethodMetadata>() is { } httpMethodMetadata &&
routeEndpoint.Metadata.GetMetadata<IExcludeFromDescriptionMetadata>() is null or { ExcludeFromDescription: false })
    {
foreach (var httpMethod in httpMethodMetadata.HttpMethods)
{
    context.Results.Add(CreateApiDescription(routeEndpoint, httpMethod, methodInfo));
}
    }
}
    }
    private ApiDescription CreateApiDescription(RouteEndpoint routeEndpoint, string httpMethod, MethodInfo methodInfo)
    {
//實(shí)現(xiàn)代碼省略	
    }
}

這個(gè)類里還有其他方法代碼也非常多,都是在組裝ApiDescription里的數(shù)據(jù),通過(guò)名稱可以得知,這個(gè)類是為了描述API接口信息用的,但是我們了解到的是它的數(shù)據(jù)源都來(lái)自EndpointDataSource類的實(shí)例。我們都知道MinimalApi提供的操作方法就是MapGet、MapPost、MapPut、MapDelete等等,這些方法的本質(zhì)都是在調(diào)用Map方法[點(diǎn)擊查看源碼],看一下核心實(shí)現(xiàn)

private static RouteHandlerBuilder Map(this IEndpointRouteBuilder endpoints,
			RoutePattern pattern, Delegate handler, bool disableInferBodyFromParameters)
{
	//省略部分代碼
	var requestDelegateResult = RequestDelegateFactory.Create(handler, options);
	var builder = new RouteEndpointBuilder(requestDelegateResult.RequestDelegate,pattern,defaultOrder)
	{
		//路由名稱
		DisplayName = pattern.RawText ?? pattern.DebuggerToString(),
	};
	//獲得httpmethod
	builder.Metadata.Add(handler.Method);
	if (GeneratedNameParser.TryParseLocalFunctionName(handler.Method.Name, out var endpointName)
		|| !TypeHelper.IsCompilerGeneratedMethod(handler.Method))
	{
		endpointName ??= handler.Method.Name;
		builder.DisplayName = $"{builder.DisplayName} => {endpointName}";
	}
	var attributes = handler.Method.GetCustomAttributes();
	foreach (var metadata in requestDelegateResult.EndpointMetadata)
	{
		builder.Metadata.Add(metadata);
	}
	if (attributes is not null)
	{
		foreach (var attribute in attributes)
		{
			builder.Metadata.Add(attribute);
		}
	}
	// 添加ModelEndpointDataSource
	var dataSource = endpoints.DataSources.OfType<ModelEndpointDataSource>().FirstOrDefault();
	if (dataSource is null)
	{
		dataSource = new ModelEndpointDataSource();
		endpoints.DataSources.Add(dataSource);
	}
	//將RouteEndpointBuilder添加到ModelEndpointDataSource
	return new RouteHandlerBuilder(dataSource.AddEndpointBuilder(builder));
}

通過(guò)Map方法我們可以看到每次添加一個(gè)MinimalApi終結(jié)點(diǎn)都會(huì)給ModelEndpointDataSource實(shí)例添加一個(gè)EndpointBuilder實(shí)例,EndPointBuilder里承載著MinimalApi終結(jié)點(diǎn)的信息,而ModelEndpointDataSource則是繼承了EndpointDataSource類,這個(gè)可以看它的定義[點(diǎn)擊查看源碼]

internal class ModelEndpointDataSource : EndpointDataSource
{
}

這就和上面提到的EndpointMetadataApiDescriptionProvider里的EndpointDataSource聯(lián)系起來(lái)了,但是我們這里看到的是IEndpointRouteBuilderDataSources屬性,從名字看這明顯是一個(gè)集合,我們可以找到定義的地方看一下[點(diǎn)擊查看源碼]

public interface IEndpointRouteBuilder
{
    IApplicationBuilder CreateApplicationBuilder();
    IServiceProvider ServiceProvider { get; }
    //這里是一個(gè)EndpointDataSource的集合
    ICollection<EndpointDataSource> DataSources { get; }
}

這里既然是一個(gè)集合那如何和EndpointDataSource聯(lián)系起來(lái)呢,接下來(lái)我們就得去看EndpointDataSource是如何被注冊(cè)的即可,找到EndpointDataSource注冊(cè)的地方[點(diǎn)擊查看源碼]查看一下注冊(cè)代碼

var dataSources = new ObservableCollection<EndpointDataSource>();
services.TryAddEnumerable(ServiceDescriptor.Transient<IConfigureOptions<RouteOptions>, ConfigureRouteOptions>(
    serviceProvider => new ConfigureRouteOptions(dataSources)));
services.TryAddSingleton<EndpointDataSource>(s =>
{
    return new CompositeEndpointDataSource(dataSources);
});

通過(guò)這段代碼我們可以得到兩點(diǎn)信息

  • 一是EndpointDataSource這個(gè)抽象類,系統(tǒng)給他注冊(cè)的是CompositeEndpointDataSource這個(gè)子類,看名字可以看出是組合的EndpointDataSource
  • 二是CompositeEndpointDataSource是通過(guò)ObservableCollection<EndpointDataSource>這么一個(gè)集合來(lái)初始化的

我們可以簡(jiǎn)單的來(lái)看下CompositeEndpointDataSource傳遞的dataSources是如何被接收的[點(diǎn)擊查看源碼]咱們只關(guān)注他說(shuō)如何被接收的

public sealed class CompositeEndpointDataSource : EndpointDataSource
{
    private readonly ICollection<EndpointDataSource> _dataSources = default!;
    internal CompositeEndpointDataSource(ObservableCollection<EndpointDataSource> dataSources) : this()
    {
_dataSources = dataSources;
    }
    public IEnumerable<EndpointDataSource> DataSources => _dataSources;
}

通過(guò)上面我們可以看到,系統(tǒng)默認(rèn)為EndpointDataSource抽象類注冊(cè)了CompositeEndpointDataSource實(shí)現(xiàn)類,而這個(gè)實(shí)現(xiàn)類是一個(gè)組合類,它組合了一個(gè)EndpointDataSource的集合。那么到了這里就只剩下一個(gè)問(wèn)題了,那就是EndpointDataSource是如何和IEndpointRouteBuilderDataSources屬性關(guān)聯(lián)起來(lái)的。現(xiàn)在有了提供數(shù)據(jù)源的IEndpointRouteBuilder,有承載數(shù)據(jù)的EndpointDataSource。這個(gè)地方呢大家也比較熟悉那就是UseEndpoints中間件里,我們來(lái)看下是如何實(shí)現(xiàn)的[點(diǎn)擊查看源碼]

public static IApplicationBuilder UseEndpoints(this IApplicationBuilder builder, Action<IEndpointRouteBuilder> configure)
{
    // 省略一堆代碼
    //得到IEndpointRouteBuilder實(shí)例
    VerifyEndpointRoutingMiddlewareIsRegistered(builder, out var endpointRouteBuilder);
    //獲取RouteOptions
    var routeOptions = builder.ApplicationServices.GetRequiredService<IOptions<RouteOptions>>();
    //遍歷IEndpointRouteBuilder的DataSources
    foreach (var dataSource in endpointRouteBuilder.DataSources)
    {
if (!routeOptions.Value.EndpointDataSources.Contains(dataSource))
{
    //dataSource放入RouteOptions的EndpointDataSources集合
    routeOptions.Value.EndpointDataSources.Add(dataSource);
}
    }
    return builder.UseMiddleware<EndpointMiddleware>();
}
private static void VerifyEndpointRoutingMiddlewareIsRegistered(IApplicationBuilder app, out IEndpointRouteBuilder endpointRouteBuilder)
{
    if (!app.Properties.TryGetValue(EndpointRouteBuilder, out var obj))
    {
throw new InvalidOperationException();
    }
    endpointRouteBuilder = (IEndpointRouteBuilder)obj!;
    if (endpointRouteBuilder is DefaultEndpointRouteBuilder defaultRouteBuilder && !object.ReferenceEquals(app, defaultRouteBuilder.ApplicationBuilder))
    {
throw new InvalidOperationException();
    }
}

這里我們看到是獲取的IOptions<RouteOptions>里的EndpointDataSources,怎么和預(yù)想的劇本不一樣呢?并非如此,你看上面咱們說(shuō)的這段代碼

var dataSources = new ObservableCollection<EndpointDataSource>();
services.TryAddEnumerable(ServiceDescriptor.Transient<IConfigureOptions<RouteOptions>, ConfigureRouteOptions>(
	serviceProvider => new ConfigureRouteOptions(dataSources)));

上面的dataSources同時(shí)傳遞給了CompositeEndpointDataSourceConfigureRouteOptions,而ConfigureRouteOptions則正是IConfigureOptions<RouteOptions>類型的,所以獲取IOptions<RouteOptions>就是獲取的ConfigureRouteOptions的實(shí)例,咱們來(lái)看一下ConfigureRouteOptions類的實(shí)現(xiàn)[點(diǎn)擊查看源碼]

internal class ConfigureRouteOptions : IConfigureOptions<RouteOptions>
{
    private readonly ICollection<EndpointDataSource> _dataSources;
    public ConfigureRouteOptions(ICollection<EndpointDataSource> dataSources)
    {
if (dataSources == null)
{
    throw new ArgumentNullException(nameof(dataSources));
}
_dataSources = dataSources;
    }
    public void Configure(RouteOptions options)
    {
if (options == null)
{
    throw new ArgumentNullException(nameof(options));
}
options.EndpointDataSources = _dataSources;
    }
}

它的本質(zhì)操作就是對(duì)RouteOptions的EndpointDataSources的屬性進(jìn)行操作,因?yàn)?code>ICollection<EndpointDataSource>是引用類型,所以這個(gè)集合是共享的,因此IEndpointRouteBuilderDataSourcesIConfigureOptions<RouteOptions>本質(zhì)是使用了同一個(gè)ICollection<EndpointDataSource>集合,所以上面的UseEndpoints里獲取RouteOptions選項(xiàng)的本質(zhì)正是獲取的EndpointDataSource集合。

每次對(duì)IEndpointRouteBuilderDataSources集合Add的時(shí)候其實(shí)是在為ICollection<EndpointDataSource>集合添加數(shù)據(jù),而IConfigureOptions<RouteOptions>也使用了這個(gè)集合,所以它們的數(shù)據(jù)是互通的。

許多同學(xué)都很好強(qiáng),默認(rèn)并沒(méi)在MinimalApi看到注冊(cè)UseEndpoints,但是在ASP.NET Core6.0之前還是需要注冊(cè)UseEndpoints中間件的。這其實(shí)是ASP.NET Core6.0進(jìn)行的一次升級(jí)優(yōu)化,因?yàn)楹芏嗖僮髂J(rèn)都得添加,所以把它統(tǒng)一封裝起來(lái)了,這個(gè)可以在WebApplicationBuilder類中看到[點(diǎn)擊查看源碼]在ConfigureApplication方法中的代碼

private void ConfigureApplication(WebHostBuilderContext context, IApplicationBuilder app)
{
    // 省略部分代碼
    // 注冊(cè)UseDeveloperExceptionPage全局異常中間件
    if (context.HostingEnvironment.IsDevelopment())
    {
app.UseDeveloperExceptionPage();
    }
    app.Properties.Add(WebApplication.GlobalEndpointRouteBuilderKey, _builtApplication);
    if (_builtApplication.DataSources.Count > 0)
    {
// 注冊(cè)UseRouting中間件
if (!_builtApplication.Properties.TryGetValue(EndpointRouteBuilderKey, out var localRouteBuilder))
{
    app.UseRouting();
}
else
{
    app.Properties[EndpointRouteBuilderKey] = localRouteBuilder;
}
    }
    app.Use(next =>
    {
//調(diào)用WebApplication的Run方法
_builtApplication.Run(next);
return _builtApplication.BuildRequestDelegate();
    });
    // 如果DataSources集合有數(shù)據(jù)則注冊(cè)UseEndpoints
    if (_builtApplication.DataSources.Count > 0)
    {
app.UseEndpoints(_ => { });
    }
    // 省略部分代碼
}

相信大家通過(guò)ConfigureApplication這個(gè)方法大家就了解了吧,之前我們能看到的熟悉方法UseDeveloperExceptionPage、UseRouting、UseEndpoints方法都在這里,畢竟之前這幾個(gè)方法幾乎也成了新建項(xiàng)目時(shí)候必須要添加的,所以微軟干脆就在內(nèi)部統(tǒng)一封裝起來(lái)了。

源碼小結(jié)

上面咱們分析了相關(guān)的源碼,整理起來(lái)就是這么一個(gè)思路。

  • Swashbuckle.AspNetCore.SwaggerGen用來(lái)生成swagger的數(shù)據(jù)源來(lái)自IApiDescriptionGroupCollectionProvider
  • IApiDescriptionGroupCollectionProvider實(shí)例的數(shù)據(jù)來(lái)自EndpointDataSource
  • 因?yàn)?code>EndpointDataSource的DataSourcesIConfigureOptions<RouteOptions>本質(zhì)是使用了同一個(gè)ICollection<EndpointDataSource>集合,所以它們是同一份數(shù)據(jù)
  • 每次使用MinimalApi的Map相關(guān)的方法的是會(huì)給IEndpointRouteBuilderDataSources集合添加數(shù)據(jù)
  • UseEndpoints中間件里獲取IEndpointRouteBuilderDataSources數(shù)據(jù)給RouteOptions選項(xiàng)的EndpointDataSources集合屬性添加數(shù)據(jù),本質(zhì)則是給ICollection<EndpointDataSource>集合賦值,自然也就是給EndpointDataSourceDataSources屬性賦值

這也給我們提供了一個(gè)思路,如果你想自己去適配swagger數(shù)據(jù)源的話完全也可以參考這個(gè)思路,想辦法把你要提供的接口信息放到EndpointDataSource的DataSources集合屬性里即可,或者直接適配IApiDescriptionGroupCollectionProvider里的數(shù)據(jù),有興趣的同學(xué)可以自行研究一下。

使用擴(kuò)展

我們看到了微軟給我們提供了IApiDescriptionGroupCollectionProvider這個(gè)便利條件,所以如果以后有獲取接口信息的時(shí)候則可以直接使用了,很多時(shí)候比如寫(xiě)監(jiān)控程序或者寫(xiě)Api接口調(diào)用的代碼生成器的時(shí)候都可以考慮一下,咱們簡(jiǎn)單的示例一下如何使用,首先定義個(gè)模型類來(lái)承載接口信息

public class ApiDoc
{
    /// &lt;summary&gt;
    /// 接口分組
    /// &lt;/summary&gt;
    public string Group { get; set; }
    /// &lt;summary&gt;
    /// 接口路由
    /// &lt;/summary&gt;
    public string Route { get; set; }
    /// &lt;summary&gt;
    /// http方法
    /// &lt;/summary&gt;
    public string HttpMethod { get; set; }
}

這個(gè)類非常簡(jiǎn)單只做演示使用,然后我們?cè)贗ApiDescriptionGroupCollectionProvider里獲取信息來(lái)填充這個(gè)集合,這里我們寫(xiě)一個(gè)htt接口來(lái)展示

app.MapGet("/apiinfo", (IApiDescriptionGroupCollectionProvider provider) =&gt; {
    List&lt;ApiDoc&gt; docs = new List&lt;ApiDoc&gt;();
    foreach (var group in provider.ApiDescriptionGroups.Items)
    {
foreach (var apiDescription in group.Items)
{
    docs.Add(new ApiDoc 
    { 
Group = group.GroupName, 
Route = apiDescription.RelativePath,
HttpMethod = apiDescription.HttpMethod
    });
}
    }
    return docs;
});

這個(gè)時(shí)候當(dāng)你在瀏覽器里請(qǐng)求/apiinfo路徑的時(shí)候會(huì)返回你的webapi包含的接口相關(guān)的信息。咱們的示例是非常簡(jiǎn)單的,實(shí)際上IApiDescriptionGroupCollectionProvider包含的接口信息是非常多的包含請(qǐng)求參數(shù)信息、輸出返回信息等很全面,這也是swagger可以完全依賴它的原因,有興趣的同學(xué)可以自行的了解一下,這里就不過(guò)多講解了。

總結(jié)

本文咱們主要通過(guò)MinimalApi如何適配swagger的這么一個(gè)過(guò)程來(lái)講解了ASP.NET Core是如何給Swagger提供了數(shù)據(jù)的。本質(zhì)是微軟在ASP.NET Core本身提供了IApiDescriptionGroupCollectionProvider這么一個(gè)數(shù)據(jù)源,Swagger借助這個(gè)數(shù)據(jù)源生成了swagger文檔,IApiDescriptionGroupCollectionProvider來(lái)自聲明終結(jié)點(diǎn)的時(shí)候往EndpointDataSourceDataSources集合里添加的接口信息等。其實(shí)它內(nèi)部比這個(gè)還要復(fù)雜一點(diǎn),不過(guò)如果我們用來(lái)獲取接口信息的話,大部分時(shí)候使用IApiDescriptionGroupCollectionProvider應(yīng)該就足夠了。    

分享一段我個(gè)人比較認(rèn)可的話,與其天天鉆頭覓縫、找各種機(jī)會(huì),不如把這些時(shí)間和金錢投入到自己的能力建設(shè)上。機(jī)會(huì)稍縱即逝,而且別人給你的機(jī)會(huì),沒(méi)準(zhǔn)兒反而是陷阱。而投資個(gè)人能力就是積累一個(gè)資產(chǎn)賬戶,只能越存越多,看起來(lái)慢,但是你永遠(yuǎn)在享受時(shí)間帶來(lái)的復(fù)利,其實(shí)快得很,收益也穩(wěn)定得多。有了能力之后,機(jī)會(huì)也就來(lái)了。

以上就是源碼分析MinimalApi是如何在Swagger中展示的詳細(xì)內(nèi)容,更多關(guān)于MinimalApi在Swagger展示的資料請(qǐng)關(guān)注其它相關(guān)文章!

標(biāo)簽: ASP
主站蜘蛛池模板: 美女视频黄色网址 | 国产亚洲精品一区二区 | 久久精品成人国产午夜 | 欧美一区=区三区 | 国产网站免费 | 国产精品自拍一区 | 在线 | 一区二区三区四区 | 亚洲香蕉一区二区三区在线观看 | 久久久久久久久久免观看 | 亚洲精品www久久久久久久软件 | 免费一级欧美毛片 | 欧美多人三级级视频播放 | 国产一区亚洲二区三区 | 加勒比色综合久久久久久久久 | 国产三级精品91三级在专区 | 日本不卡不码高清免费观看 | 女人抠逼视频 | 成人影院人人免费 | ririai99在线视频观看 | 香港经典a毛片免费观看看 香港经典a毛片免费观看爽爽影院 | 亚洲精品一区二区观看 | 2022国产精品网站在线播放 | 香蕉久久一区二区不卡无毒影院 | 成人国产精品一级毛片天堂 | 久久九九久精品国产 | 韩国一级毛片 | 亚洲精品亚洲人成在线麻豆 | 久久er热视频在这里精品 | 国产亚洲精品九九久在线观看 | 特级一级毛片视频免费观看 | 九草网| 亚洲专区在线视频 | 国产成人深夜福利在线观看 | 亚洲一级成人 | 亚洲加勒比久久88色综合 | 国产午夜精品理论片影院 | 亚洲国产成人麻豆精品 | 欧美性色大片 | 亚洲自偷自偷图片在线高清 | 日韩专区欧美 | 宅男毛片|