23
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web.Http;
|
||||
using System.Web.Http.Controllers;
|
||||
using System.Web.Http.Description;
|
||||
using System.Web.Http.Routing;
|
||||
|
||||
namespace Swashbuckle.Application
|
||||
{
|
||||
public class AreaApiExplorer:IApiExplorer
|
||||
{
|
||||
private IApiExplorer _innerApiExplorer;
|
||||
private HttpConfiguration _configuration;
|
||||
private Lazy<Collection<ApiDescription>> _apiDescriptions;
|
||||
private MethodInfo _apiDescriptionPopulator;
|
||||
|
||||
public AreaApiExplorer(IApiExplorer apiExplorer, HttpConfiguration configuration)
|
||||
{
|
||||
_innerApiExplorer = apiExplorer;
|
||||
_configuration = configuration;
|
||||
_apiDescriptions=new Lazy<Collection<ApiDescription>>(new Func<Collection<ApiDescription>>(Init));
|
||||
}
|
||||
|
||||
public Collection<ApiDescription> ApiDescriptions
|
||||
{
|
||||
get { return _apiDescriptions.Value; }
|
||||
}
|
||||
|
||||
private Collection<ApiDescription> Init()
|
||||
{
|
||||
var descriptions = _innerApiExplorer.ApiDescriptions;
|
||||
var controllerSelector = _configuration.Services.GetHttpControllerSelector();
|
||||
var controllerMappings = controllerSelector.GetControllerMapping();
|
||||
|
||||
var flatRoutes = FlattenRoutes(_configuration.Routes);
|
||||
var result=new Collection<ApiDescription>();
|
||||
|
||||
foreach (var description in descriptions)
|
||||
{
|
||||
result.Add(description);
|
||||
|
||||
if (controllerMappings != null)
|
||||
{
|
||||
var matchingRoutes =
|
||||
flatRoutes.Where(
|
||||
r => r.RouteTemplate == description.Route.RouteTemplate && r != description.Route);
|
||||
foreach (var route in matchingRoutes)
|
||||
{
|
||||
GetRouteDescriptions(route,result);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void GetRouteDescriptions(IHttpRoute route, Collection<ApiDescription> apiDescriptions)
|
||||
{
|
||||
var actionDescriptor = route.DataTokens["actions"] as IEnumerable<HttpActionDescriptor>;
|
||||
if (actionDescriptor != null && actionDescriptor.Count() > 0)
|
||||
{
|
||||
GetPopulateMethod()
|
||||
.Invoke(_innerApiExplorer,
|
||||
new object[] {actionDescriptor.First(), route, route.RouteTemplate, apiDescriptions});
|
||||
}
|
||||
}
|
||||
|
||||
private MethodInfo GetPopulateMethod()
|
||||
{
|
||||
if (_apiDescriptionPopulator == null)
|
||||
{
|
||||
_apiDescriptionPopulator =
|
||||
_innerApiExplorer.GetType()
|
||||
.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance)
|
||||
.FirstOrDefault(m => m.Name == "PopulateActionDescriptions" && m.GetParameters().Length == 4);
|
||||
}
|
||||
return _apiDescriptionPopulator;
|
||||
}
|
||||
|
||||
private static IEnumerable<IHttpRoute> FlattenRoutes(IEnumerable<IHttpRoute> routes)
|
||||
{
|
||||
var flatRoutes=new List<HttpRoute>();
|
||||
foreach (var route in routes)
|
||||
{
|
||||
if (route is HttpRoute)
|
||||
{
|
||||
yield return route;
|
||||
}
|
||||
var subRoutes = route as IReadOnlyCollection<IHttpRoute>;
|
||||
if (subRoutes != null)
|
||||
{
|
||||
foreach (var subRoute in FlattenRoutes(subRoutes))
|
||||
{
|
||||
yield return subRoute;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using Swashbuckle.Swagger;
|
||||
|
||||
namespace Swashbuckle.Application
|
||||
{
|
||||
public class ContactBuilder
|
||||
{
|
||||
private string _name;
|
||||
private string _url;
|
||||
private string _email;
|
||||
|
||||
public ContactBuilder Name(string name)
|
||||
{
|
||||
_name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ContactBuilder Url(string url)
|
||||
{
|
||||
_url = url;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ContactBuilder Email(string email)
|
||||
{
|
||||
_email = email;
|
||||
return this;
|
||||
}
|
||||
|
||||
internal Contact Build()
|
||||
{
|
||||
if ((_name ?? _url ?? _email) == null) return null;
|
||||
|
||||
return new Contact
|
||||
{
|
||||
name = _name,
|
||||
url = _url,
|
||||
email = _email
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Web.Http;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using Swashbuckle.Application;
|
||||
using System.Net.Http;
|
||||
using System.Collections.Generic;
|
||||
using System.Web.Http.Routing;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Swashbuckle.Application
|
||||
{
|
||||
public static class HttpConfigurationExtensions
|
||||
{
|
||||
private static readonly string DefaultRouteTemplate = "swagger/docs/{apiVersion}";
|
||||
|
||||
public static SwaggerEnabledConfiguration EnableSwagger(
|
||||
this HttpConfiguration httpConfig,
|
||||
Action<SwaggerDocsConfig> configure = null)
|
||||
{
|
||||
return EnableSwagger(httpConfig, DefaultRouteTemplate, configure);
|
||||
}
|
||||
|
||||
public static SwaggerEnabledConfiguration EnableSwagger(
|
||||
this HttpConfiguration httpConfig,
|
||||
string routeTemplate,
|
||||
Action<SwaggerDocsConfig> configure = null)
|
||||
{
|
||||
var config = new SwaggerDocsConfig();
|
||||
if (configure != null) configure(config);
|
||||
|
||||
httpConfig.Routes.MapHttpRoute(
|
||||
name: "swagger_docs" + routeTemplate,
|
||||
routeTemplate: routeTemplate,
|
||||
defaults: null,
|
||||
constraints: new { apiVersion = @".+" },
|
||||
handler: new SwaggerDocsHandler(config)
|
||||
);
|
||||
|
||||
|
||||
return new SwaggerEnabledConfiguration(
|
||||
httpConfig,
|
||||
config.GetRootUrl,
|
||||
config.GetApiVersions().Select(version => routeTemplate.Replace("{apiVersion}", version)));
|
||||
}
|
||||
|
||||
internal static JsonSerializerSettings SerializerSettingsOrDefault(this HttpConfiguration httpConfig)
|
||||
{
|
||||
var formatter = httpConfig.Formatters.JsonFormatter;
|
||||
if (formatter != null)
|
||||
return formatter.SerializerSettings;
|
||||
|
||||
return new JsonSerializerSettings();
|
||||
}
|
||||
}
|
||||
|
||||
public class SwaggerEnabledConfiguration
|
||||
{
|
||||
private static readonly string DefaultRouteTemplate = "swagger/ui/{*assetPath}";
|
||||
|
||||
private readonly HttpConfiguration _httpConfig;
|
||||
private readonly Func<HttpRequestMessage, string> _rootUrlResolver;
|
||||
private readonly IEnumerable<string> _discoveryPaths;
|
||||
|
||||
internal static IEnumerable<string> DiscoveryPaths;
|
||||
|
||||
public SwaggerEnabledConfiguration(
|
||||
HttpConfiguration httpConfig,
|
||||
Func<HttpRequestMessage, string> rootUrlResolver,
|
||||
IEnumerable<string> discoveryPaths)
|
||||
{
|
||||
_httpConfig = httpConfig;
|
||||
_rootUrlResolver = rootUrlResolver;
|
||||
_discoveryPaths = discoveryPaths;
|
||||
DiscoveryPaths = _discoveryPaths;
|
||||
}
|
||||
|
||||
public void EnableSwaggerUi(Action<SwaggerUiConfig> configure = null)
|
||||
{
|
||||
EnableSwaggerUi(DefaultRouteTemplate, configure);
|
||||
}
|
||||
|
||||
public void EnableSwaggerUi(
|
||||
string routeTemplate,
|
||||
Action<SwaggerUiConfig> configure = null)
|
||||
{
|
||||
var config = new SwaggerUiConfig(_discoveryPaths, _rootUrlResolver);
|
||||
if (configure != null) configure(config);
|
||||
|
||||
_httpConfig.Routes.MapHttpRoute(
|
||||
name: "swagger_ui" + routeTemplate,
|
||||
routeTemplate: routeTemplate,
|
||||
defaults: null,
|
||||
constraints: new { assetPath = @".+" },
|
||||
handler: new SwaggerUiHandler(config)
|
||||
);
|
||||
|
||||
if (routeTemplate == DefaultRouteTemplate)
|
||||
{
|
||||
_httpConfig.Routes.MapHttpRoute(
|
||||
name: "swagger_ui_shortcut",
|
||||
routeTemplate: "swagger",
|
||||
defaults: null,
|
||||
constraints: new { uriResolution = new HttpRouteDirectionConstraint(HttpRouteDirection.UriResolution) },
|
||||
handler: new RedirectHandler(_rootUrlResolver, "swagger/ui/index"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Web.Http.Routing;
|
||||
|
||||
namespace Swashbuckle.Application
|
||||
{
|
||||
public class HttpRouteDirectionConstraint : IHttpRouteConstraint
|
||||
{
|
||||
private readonly HttpRouteDirection _allowedDirection;
|
||||
|
||||
public HttpRouteDirectionConstraint(HttpRouteDirection allowedDirection)
|
||||
{
|
||||
_allowedDirection = allowedDirection;
|
||||
}
|
||||
|
||||
public bool Match(
|
||||
HttpRequestMessage request,
|
||||
IHttpRoute route,
|
||||
string parameterName,
|
||||
IDictionary<string, object> values,
|
||||
HttpRouteDirection routeDirection)
|
||||
{
|
||||
return routeDirection == _allowedDirection;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using Swashbuckle.Swagger;
|
||||
|
||||
namespace Swashbuckle.Application
|
||||
{
|
||||
public class InfoBuilder
|
||||
{
|
||||
private string _version;
|
||||
private string _title;
|
||||
private string _description;
|
||||
private string _termsOfService;
|
||||
/// <summary>
|
||||
/// 是否默认路由
|
||||
/// </summary>
|
||||
private bool _isDefaultRoute;
|
||||
private readonly ContactBuilder _contactBuilder = new ContactBuilder();
|
||||
private readonly LicenseBuilder _licenseBuilder = new LicenseBuilder();
|
||||
|
||||
public InfoBuilder(string version, string title)
|
||||
{
|
||||
_version = version;
|
||||
_title = title;
|
||||
}
|
||||
|
||||
public InfoBuilder(string version, string title, bool isDefualt):this(version,title)
|
||||
{
|
||||
_isDefaultRoute = isDefualt;
|
||||
}
|
||||
|
||||
public InfoBuilder Description(string description)
|
||||
{
|
||||
_description = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
public InfoBuilder TermsOfService(string termsOfService)
|
||||
{
|
||||
_termsOfService = termsOfService;
|
||||
return this;
|
||||
}
|
||||
|
||||
public InfoBuilder Contact(Action<ContactBuilder> contact)
|
||||
{
|
||||
contact(_contactBuilder);
|
||||
return this;
|
||||
}
|
||||
|
||||
public InfoBuilder License(Action<LicenseBuilder> license)
|
||||
{
|
||||
license(_licenseBuilder);
|
||||
return this;
|
||||
}
|
||||
|
||||
internal Info Build()
|
||||
{
|
||||
return new Info
|
||||
{
|
||||
version = _version,
|
||||
title = _title,
|
||||
description = _description,
|
||||
termsOfService = _termsOfService,
|
||||
contact = _contactBuilder.Build(),
|
||||
license = _licenseBuilder.Build(),
|
||||
isDefaultRoute = _isDefaultRoute
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using Swashbuckle.Swagger;
|
||||
|
||||
namespace Swashbuckle.Application
|
||||
{
|
||||
public class LicenseBuilder
|
||||
{
|
||||
private string _name;
|
||||
private string _url;
|
||||
|
||||
public LicenseBuilder Name(string name)
|
||||
{
|
||||
_name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public LicenseBuilder Url(string url)
|
||||
{
|
||||
_url = url;
|
||||
return this;
|
||||
}
|
||||
|
||||
internal License Build()
|
||||
{
|
||||
if ((_name ?? _url) == null) return null;
|
||||
|
||||
return new License
|
||||
{
|
||||
name = _name,
|
||||
url = _url
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Swashbuckle.Application
|
||||
{
|
||||
public class RedirectHandler : HttpMessageHandler
|
||||
{
|
||||
private readonly Func<HttpRequestMessage, string> _rootUrlResolver;
|
||||
private readonly string _redirectPath;
|
||||
|
||||
public RedirectHandler(Func<HttpRequestMessage, string> rootUrlResolver, string redirectPath)
|
||||
{
|
||||
_rootUrlResolver = rootUrlResolver;
|
||||
_redirectPath = redirectPath;
|
||||
}
|
||||
|
||||
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
var redirectUrl = _rootUrlResolver(request) + "/" + _redirectPath;
|
||||
|
||||
var response = request.CreateResponse(HttpStatusCode.Moved);
|
||||
response.Headers.Location = new Uri(redirectUrl);
|
||||
|
||||
var tsc = new TaskCompletionSource<HttpResponseMessage>();
|
||||
tsc.SetResult(response);
|
||||
return tsc.Task;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Swashbuckle.Swagger;
|
||||
|
||||
namespace Swashbuckle.Application
|
||||
{
|
||||
public abstract class SecuritySchemeBuilder
|
||||
{
|
||||
internal abstract SecurityScheme Build();
|
||||
}
|
||||
|
||||
public class BasicAuthSchemeBuilder : SecuritySchemeBuilder
|
||||
{
|
||||
private string _description;
|
||||
|
||||
public BasicAuthSchemeBuilder Description(string description)
|
||||
{
|
||||
_description = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
internal override SecurityScheme Build()
|
||||
{
|
||||
return new SecurityScheme
|
||||
{
|
||||
type = "basic",
|
||||
description = _description
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class ApiKeySchemeBuilder : SecuritySchemeBuilder
|
||||
{
|
||||
private string _description;
|
||||
private string _name;
|
||||
private string _in;
|
||||
|
||||
public ApiKeySchemeBuilder Description(string description)
|
||||
{
|
||||
_description = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ApiKeySchemeBuilder Name(string name)
|
||||
{
|
||||
_name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ApiKeySchemeBuilder In(string @in)
|
||||
{
|
||||
_in = @in;
|
||||
return this;
|
||||
}
|
||||
|
||||
internal override SecurityScheme Build()
|
||||
{
|
||||
return new SecurityScheme
|
||||
{
|
||||
type = "apiKey",
|
||||
description = _description,
|
||||
name = _name,
|
||||
@in = _in
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class OAuth2SchemeBuilder : SecuritySchemeBuilder
|
||||
{
|
||||
private string _description;
|
||||
private string _flow;
|
||||
private string _authorizationUrl;
|
||||
private string _tokenUrl;
|
||||
private IDictionary<string, string> _scopes = new Dictionary<string, string>();
|
||||
|
||||
public OAuth2SchemeBuilder Description(string description)
|
||||
{
|
||||
_description = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
public OAuth2SchemeBuilder Flow(string flow)
|
||||
{
|
||||
_flow = flow;
|
||||
return this;
|
||||
}
|
||||
|
||||
public OAuth2SchemeBuilder AuthorizationUrl(string authorizationUrl)
|
||||
{
|
||||
_authorizationUrl = authorizationUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
public OAuth2SchemeBuilder TokenUrl(string tokenUrl)
|
||||
{
|
||||
_tokenUrl = tokenUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
public OAuth2SchemeBuilder Scopes(Action<IDictionary<string, string>> configure)
|
||||
{
|
||||
configure(_scopes);
|
||||
return this;
|
||||
}
|
||||
|
||||
internal override SecurityScheme Build()
|
||||
{
|
||||
// TODO: Validate required fields for given flow
|
||||
|
||||
return new SecurityScheme
|
||||
{
|
||||
type = "oauth2",
|
||||
flow = _flow,
|
||||
authorizationUrl = _authorizationUrl,
|
||||
tokenUrl = _tokenUrl,
|
||||
scopes = _scopes,
|
||||
description = _description,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,335 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Web.Http;
|
||||
using System.Web.Http.Description;
|
||||
using System.Xml.XPath;
|
||||
using Newtonsoft.Json;
|
||||
using Swashbuckle.Swagger;
|
||||
using Swashbuckle.Swagger.Annotations;
|
||||
using Swashbuckle.Swagger.FromUriParams;
|
||||
using Swashbuckle.Swagger.XmlComments;
|
||||
|
||||
namespace Swashbuckle.Application
|
||||
{
|
||||
public class SwaggerDocsConfig
|
||||
{
|
||||
private VersionInfoBuilder _versionInfoBuilder;
|
||||
private Func<ApiDescription, string, bool> _versionSupportResolver;
|
||||
private IEnumerable<string> _schemes;
|
||||
private IDictionary<string, SecuritySchemeBuilder> _securitySchemeBuilders;
|
||||
private bool _prettyPrint;
|
||||
private bool _ignoreObsoleteActions;
|
||||
private Func<ApiDescription, string> _groupingKeySelector;
|
||||
private IComparer<string> _groupingKeyComparer;
|
||||
private readonly IDictionary<Type, Func<Schema>> _customSchemaMappings;
|
||||
private readonly IList<Func<ISchemaFilter>> _schemaFilters;
|
||||
private readonly IList<Func<IModelFilter>> _modelFilters;
|
||||
private Func<Type, string> _schemaIdSelector;
|
||||
private bool _ignoreObsoleteProperties;
|
||||
private bool _describeAllEnumsAsStrings;
|
||||
private bool _describeStringEnumsInCamelCase;
|
||||
private bool _applyFiltersToAllSchemas;
|
||||
private readonly IList<Func<IOperationFilter>> _operationFilters;
|
||||
private readonly IList<Func<IDocumentFilter>> _documentFilters;
|
||||
private readonly IList<Func<XPathDocument>> _xmlDocFactories;
|
||||
private Func<IEnumerable<ApiDescription>, ApiDescription> _conflictingActionsResolver;
|
||||
private Func<HttpRequestMessage, string> _rootUrlResolver;
|
||||
|
||||
private Func<ISwaggerProvider, ISwaggerProvider> _customProviderFactory;
|
||||
|
||||
/// <summary>
|
||||
/// 是否显示开发者信息
|
||||
/// </summary>
|
||||
internal static bool ShowDeveloper = false;
|
||||
|
||||
public SwaggerDocsConfig()
|
||||
{
|
||||
_versionInfoBuilder = new VersionInfoBuilder();
|
||||
_securitySchemeBuilders = new Dictionary<string, SecuritySchemeBuilder>();
|
||||
_prettyPrint = false;
|
||||
_ignoreObsoleteActions = false;
|
||||
_customSchemaMappings = new Dictionary<Type, Func<Schema>>();
|
||||
_schemaFilters = new List<Func<ISchemaFilter>>();
|
||||
_modelFilters = new List<Func<IModelFilter>>();
|
||||
_ignoreObsoleteProperties = false;
|
||||
_describeAllEnumsAsStrings = false;
|
||||
_describeStringEnumsInCamelCase = false;
|
||||
_applyFiltersToAllSchemas = false;
|
||||
_operationFilters = new List<Func<IOperationFilter>>();
|
||||
_documentFilters = new List<Func<IDocumentFilter>>();
|
||||
_xmlDocFactories = new List<Func<XPathDocument>>();
|
||||
_rootUrlResolver = DefaultRootUrlResolver;
|
||||
|
||||
SchemaFilter<ApplySwaggerSchemaFilterAttributes>();
|
||||
|
||||
OperationFilter<HandleFromUriParams>();
|
||||
OperationFilter<ApplySwaggerOperationAttributes>();
|
||||
OperationFilter<ApplySwaggerResponseAttributes>();
|
||||
OperationFilter<ApplySwaggerOperationFilterAttributes>();
|
||||
}
|
||||
|
||||
public InfoBuilder SingleApiVersion(string version, string title)
|
||||
{
|
||||
_versionSupportResolver = null;
|
||||
_versionInfoBuilder = new VersionInfoBuilder();
|
||||
return _versionInfoBuilder.Version(version, title, true);
|
||||
}
|
||||
|
||||
public void MultipleApiVersions(
|
||||
Func<ApiDescription, string, bool> versionSupportResolver,
|
||||
Action<VersionInfoBuilder> configure)
|
||||
{
|
||||
_versionSupportResolver = versionSupportResolver;
|
||||
_versionInfoBuilder = new VersionInfoBuilder();
|
||||
configure(_versionInfoBuilder);
|
||||
}
|
||||
|
||||
public void Schemes(IEnumerable<string> schemes)
|
||||
{
|
||||
_schemes = schemes;
|
||||
}
|
||||
|
||||
public BasicAuthSchemeBuilder BasicAuth(string name)
|
||||
{
|
||||
var schemeBuilder = new BasicAuthSchemeBuilder();
|
||||
_securitySchemeBuilders[name] = schemeBuilder;
|
||||
return schemeBuilder;
|
||||
}
|
||||
|
||||
public ApiKeySchemeBuilder ApiKey(string name)
|
||||
{
|
||||
var schemeBuilder = new ApiKeySchemeBuilder();
|
||||
_securitySchemeBuilders[name] = schemeBuilder;
|
||||
return schemeBuilder;
|
||||
}
|
||||
|
||||
public OAuth2SchemeBuilder OAuth2(string name)
|
||||
{
|
||||
var schemeBuilder = new OAuth2SchemeBuilder();
|
||||
_securitySchemeBuilders[name] = schemeBuilder;
|
||||
return schemeBuilder;
|
||||
}
|
||||
|
||||
public void PrettyPrint()
|
||||
{
|
||||
_prettyPrint = true;
|
||||
}
|
||||
|
||||
public void IgnoreObsoleteActions()
|
||||
{
|
||||
_ignoreObsoleteActions = true;
|
||||
}
|
||||
|
||||
public void GroupActionsBy(Func<ApiDescription, string> keySelector)
|
||||
{
|
||||
_groupingKeySelector = keySelector;
|
||||
}
|
||||
|
||||
public void OrderActionGroupsBy(IComparer<string> keyComparer)
|
||||
{
|
||||
_groupingKeyComparer = keyComparer;
|
||||
}
|
||||
|
||||
public void MapType<T>(Func<Schema> factory)
|
||||
{
|
||||
MapType(typeof(T), factory);
|
||||
}
|
||||
|
||||
public void MapType(Type type, Func<Schema> factory)
|
||||
{
|
||||
_customSchemaMappings.Add(type, factory);
|
||||
}
|
||||
|
||||
public void SchemaFilter<TFilter>()
|
||||
where TFilter : ISchemaFilter, new()
|
||||
{
|
||||
SchemaFilter(() => new TFilter());
|
||||
}
|
||||
|
||||
public void SchemaFilter(Func<ISchemaFilter> factory)
|
||||
{
|
||||
_schemaFilters.Add(factory);
|
||||
}
|
||||
|
||||
// NOTE: In next major version, ModelFilter will completely replace SchemaFilter
|
||||
internal void ModelFilter<TFilter>()
|
||||
where TFilter : IModelFilter, new()
|
||||
{
|
||||
ModelFilter(() => new TFilter());
|
||||
}
|
||||
|
||||
// NOTE: In next major version, ModelFilter will completely replace SchemaFilter
|
||||
internal void ModelFilter(Func<IModelFilter> factory)
|
||||
{
|
||||
_modelFilters.Add(factory);
|
||||
}
|
||||
|
||||
public void SchemaId(Func<Type, string> schemaIdStrategy)
|
||||
{
|
||||
_schemaIdSelector = schemaIdStrategy;
|
||||
}
|
||||
|
||||
public void UseFullTypeNameInSchemaIds()
|
||||
{
|
||||
_schemaIdSelector = t => t.FriendlyId(true);
|
||||
}
|
||||
|
||||
public void DescribeAllEnumsAsStrings(bool camelCase = false)
|
||||
{
|
||||
_describeAllEnumsAsStrings = true;
|
||||
_describeStringEnumsInCamelCase = camelCase;
|
||||
}
|
||||
|
||||
public void IgnoreObsoleteProperties()
|
||||
{
|
||||
_ignoreObsoleteProperties = true;
|
||||
}
|
||||
|
||||
[Obsolete("This will be removed in 6.0.0; it will always be true.")]
|
||||
public void ApplyFiltersToAllSchemas()
|
||||
{
|
||||
_applyFiltersToAllSchemas = true;
|
||||
}
|
||||
|
||||
public void OperationFilter<TFilter>()
|
||||
where TFilter : IOperationFilter, new()
|
||||
{
|
||||
OperationFilter(() => new TFilter());
|
||||
}
|
||||
|
||||
public void OperationFilter(Func<IOperationFilter> factory)
|
||||
{
|
||||
_operationFilters.Add(factory);
|
||||
}
|
||||
|
||||
public void DocumentFilter<TFilter>()
|
||||
where TFilter : IDocumentFilter, new()
|
||||
{
|
||||
DocumentFilter(() => new TFilter());
|
||||
}
|
||||
|
||||
public void DocumentFilter(Func<IDocumentFilter> factory)
|
||||
{
|
||||
_documentFilters.Add(factory);
|
||||
}
|
||||
|
||||
public void IncludeXmlComments(Func<XPathDocument> xmlDocFactory)
|
||||
{
|
||||
_xmlDocFactories.Add(xmlDocFactory);
|
||||
}
|
||||
|
||||
public void IncludeXmlComments(string filePath)
|
||||
{
|
||||
_xmlDocFactories.Add(() => new XPathDocument(filePath));
|
||||
}
|
||||
|
||||
public void ResolveConflictingActions(Func<IEnumerable<ApiDescription>, ApiDescription> conflictingActionsResolver)
|
||||
{
|
||||
_conflictingActionsResolver = conflictingActionsResolver;
|
||||
}
|
||||
|
||||
public void RootUrl(Func<HttpRequestMessage, string> rootUrlResolver)
|
||||
{
|
||||
_rootUrlResolver = rootUrlResolver;
|
||||
}
|
||||
|
||||
public void CustomProvider(Func<ISwaggerProvider, ISwaggerProvider> customProviderFactory)
|
||||
{
|
||||
_customProviderFactory = customProviderFactory;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 显示开发者信息
|
||||
/// </summary>
|
||||
public void ShowDeveloperInfo()
|
||||
{
|
||||
ShowDeveloper = true;
|
||||
}
|
||||
|
||||
internal ISwaggerProvider GetSwaggerProvider(HttpRequestMessage swaggerRequest)
|
||||
{
|
||||
var httpConfig = swaggerRequest.GetConfiguration();
|
||||
|
||||
var securityDefintitions = _securitySchemeBuilders.Any()
|
||||
? _securitySchemeBuilders.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.Build())
|
||||
: null;
|
||||
|
||||
// NOTE: Instantiate & add the XML comments filters here so they're executed before any
|
||||
// custom filters AND so they can share the same XPathDocument (perf. optimization)
|
||||
var modelFilters = _modelFilters.Select(factory => factory()).ToList();
|
||||
var operationFilters = _operationFilters.Select(factory => factory()).ToList();
|
||||
foreach (var xmlDocFactory in _xmlDocFactories)
|
||||
{
|
||||
var xmlDoc = xmlDocFactory();
|
||||
modelFilters.Insert(0, new ApplyXmlTypeComments(xmlDoc));
|
||||
operationFilters.Insert(0, new ApplyXmlActionComments(xmlDoc));
|
||||
}
|
||||
|
||||
var options = new SwaggerGeneratorOptions(
|
||||
versionSupportResolver: _versionSupportResolver,
|
||||
schemes: _schemes,
|
||||
securityDefinitions: securityDefintitions,
|
||||
ignoreObsoleteActions: _ignoreObsoleteActions,
|
||||
groupingKeySelector: _groupingKeySelector,
|
||||
groupingKeyComparer: _groupingKeyComparer,
|
||||
customSchemaMappings: _customSchemaMappings,
|
||||
schemaFilters: _schemaFilters.Select(factory => factory()).ToList(),
|
||||
modelFilters: modelFilters,
|
||||
ignoreObsoleteProperties: _ignoreObsoleteProperties,
|
||||
schemaIdSelector: _schemaIdSelector,
|
||||
describeAllEnumsAsStrings: _describeAllEnumsAsStrings,
|
||||
describeStringEnumsInCamelCase: _describeStringEnumsInCamelCase,
|
||||
applyFiltersToAllSchemas: _applyFiltersToAllSchemas,
|
||||
operationFilters: operationFilters,
|
||||
documentFilters: _documentFilters.Select(factory => factory()).ToList(),
|
||||
conflictingActionsResolver: _conflictingActionsResolver
|
||||
);
|
||||
|
||||
var defaultProvider = new SwaggerGenerator(
|
||||
httpConfig.Services.GetApiExplorer(),
|
||||
httpConfig.SerializerSettingsOrDefault(),
|
||||
_versionInfoBuilder.Build(),
|
||||
options);
|
||||
|
||||
return (_customProviderFactory != null)
|
||||
? _customProviderFactory(defaultProvider)
|
||||
: defaultProvider;
|
||||
}
|
||||
|
||||
internal string GetRootUrl(HttpRequestMessage swaggerRequest)
|
||||
{
|
||||
return _rootUrlResolver(swaggerRequest);
|
||||
}
|
||||
|
||||
internal IEnumerable<string> GetApiVersions()
|
||||
{
|
||||
return _versionInfoBuilder.Build().Select(entry => entry.Key);
|
||||
}
|
||||
|
||||
internal Formatting GetFormatting()
|
||||
{
|
||||
return _prettyPrint ? Formatting.Indented : Formatting.None;
|
||||
}
|
||||
|
||||
public static string DefaultRootUrlResolver(HttpRequestMessage request)
|
||||
{
|
||||
var scheme = GetHeaderValue(request, "X-Forwarded-Proto") ?? request.RequestUri.Scheme;
|
||||
var host = GetHeaderValue(request, "X-Forwarded-Host") ?? request.RequestUri.Host;
|
||||
var port = GetHeaderValue(request, "X-Forwarded-Port") ?? request.RequestUri.Port.ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
var httpConfiguration = request.GetConfiguration();
|
||||
var virtualPathRoot = httpConfiguration.VirtualPathRoot.TrimEnd('/');
|
||||
|
||||
return string.Format("{0}://{1}:{2}{3}", scheme, host, port, virtualPathRoot);
|
||||
}
|
||||
|
||||
private static string GetHeaderValue(HttpRequestMessage request, string headerName)
|
||||
{
|
||||
IEnumerable<string> list;
|
||||
return request.Headers.TryGetValues(headerName, out list) ? list.FirstOrDefault() : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web.Http;
|
||||
using System.Net.Http.Formatting;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using System.Collections.Generic;
|
||||
using Swashbuckle.Swagger;
|
||||
using System.Net;
|
||||
|
||||
namespace Swashbuckle.Application
|
||||
{
|
||||
public class SwaggerDocsHandler : HttpMessageHandler
|
||||
{
|
||||
private readonly SwaggerDocsConfig _config;
|
||||
|
||||
public SwaggerDocsHandler(SwaggerDocsConfig config)
|
||||
{
|
||||
_config = config;
|
||||
}
|
||||
|
||||
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
var swaggerProvider = _config.GetSwaggerProvider(request);
|
||||
var rootUrl = _config.GetRootUrl(request);
|
||||
var apiVersion = request.GetRouteData().Values["apiVersion"].ToString();
|
||||
|
||||
try
|
||||
{
|
||||
var swaggerDoc = swaggerProvider.GetSwagger(rootUrl, apiVersion);
|
||||
var content = ContentFor(request, swaggerDoc);
|
||||
return TaskFor(new HttpResponseMessage { Content = content });
|
||||
}
|
||||
catch (UnknownApiVersion ex)
|
||||
{
|
||||
return TaskFor(request.CreateErrorResponse(HttpStatusCode.NotFound, ex));
|
||||
}
|
||||
}
|
||||
|
||||
private HttpContent ContentFor(HttpRequestMessage request, SwaggerDocument swaggerDoc)
|
||||
{
|
||||
var negotiator = request.GetConfiguration().Services.GetContentNegotiator();
|
||||
var result = negotiator.Negotiate(typeof(SwaggerDocument), request, GetSupportedSwaggerFormatters());
|
||||
|
||||
return new ObjectContent(typeof(SwaggerDocument), swaggerDoc, result.Formatter, result.MediaType);
|
||||
}
|
||||
|
||||
private IEnumerable<MediaTypeFormatter> GetSupportedSwaggerFormatters()
|
||||
{
|
||||
var jsonFormatter = new JsonMediaTypeFormatter
|
||||
{
|
||||
SerializerSettings = new JsonSerializerSettings
|
||||
{
|
||||
NullValueHandling = NullValueHandling.Ignore,
|
||||
Formatting = _config.GetFormatting(),
|
||||
Converters = new[] { new VendorExtensionsConverter() }
|
||||
}
|
||||
};
|
||||
// NOTE: The custom converter would not be neccessary in Newtonsoft.Json >= 5.0.5 as JsonExtensionData
|
||||
// provides similar functionality. But, need to stick with older version for WebApi 5.0.0 compatibility
|
||||
return new[] { jsonFormatter };
|
||||
}
|
||||
|
||||
private Task<HttpResponseMessage> TaskFor(HttpResponseMessage response)
|
||||
{
|
||||
var tsc = new TaskCompletionSource<HttpResponseMessage>();
|
||||
tsc.SetResult(response);
|
||||
return tsc.Task;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json;
|
||||
using Swashbuckle.SwaggerUi;
|
||||
|
||||
namespace Swashbuckle.Application
|
||||
{
|
||||
public class SwaggerUiConfig
|
||||
{
|
||||
private readonly Dictionary<string, EmbeddedAssetDescriptor> _pathToAssetMap;
|
||||
private readonly Dictionary<string, string> _templateParams;
|
||||
private readonly Func<HttpRequestMessage, string> _rootUrlResolver;
|
||||
|
||||
public SwaggerUiConfig(IEnumerable<string> discoveryPaths, Func<HttpRequestMessage, string> rootUrlResolver)
|
||||
{
|
||||
_pathToAssetMap = new Dictionary<string, EmbeddedAssetDescriptor>();
|
||||
|
||||
_templateParams = new Dictionary<string, string>
|
||||
{
|
||||
{ "%(StylesheetIncludes)", "" },
|
||||
{ "%(DiscoveryPaths)", String.Join("|", discoveryPaths) },
|
||||
{ "%(BooleanValues)", "true|false" },
|
||||
{ "%(ValidatorUrl)", "" },
|
||||
{ "%(CustomScripts)", "" },
|
||||
{ "%(DocExpansion)", "none" },
|
||||
{ "%(SupportedSubmitMethods)", "get|put|post|delete|options|head|patch" },
|
||||
{ "%(OAuth2Enabled)", "false" },
|
||||
{ "%(OAuth2ClientId)", "" },
|
||||
{ "%(OAuth2ClientSecret)", "" },
|
||||
{ "%(OAuth2Realm)", "" },
|
||||
{ "%(OAuth2AppName)", "" },
|
||||
{ "%(OAuth2ScopeSeperator)", "," },
|
||||
{ "%(OAuth2AdditionalQueryStringParams)", "{}" },
|
||||
{ "%(ApiKeyName)", "api_key" },
|
||||
{ "%(ApiKeyIn)", "query" }
|
||||
};
|
||||
_rootUrlResolver = rootUrlResolver;
|
||||
|
||||
MapPathsForSwaggerUiAssets();
|
||||
|
||||
// Use some custom versions to support config and extensionless paths
|
||||
var thisAssembly = GetType().Assembly;
|
||||
CustomAsset("index", thisAssembly, "Swashbuckle.SwaggerUi.CustomAssets.index.html", isTemplate: true);
|
||||
CustomAsset("css/screen.css", thisAssembly, "Swashbuckle.SwaggerUi.CustomAssets.screen.css");
|
||||
CustomAsset("css/typography.css", thisAssembly, "Swashbuckle.SwaggerUi.CustomAssets.typography.css");
|
||||
//CustomAsset("index", thisAssembly, "Swashbuckle.Core.SwaggerUi.Ui.index.html", isTemplate: true);
|
||||
//CustomAsset("css/screen-css", thisAssembly, "Swashbuckle.Core.SwaggerUi.Ui.screen.css");
|
||||
//CustomAsset("css/typography-css", thisAssembly, "Swashbuckle.Core.SwaggerUi.Ui.typography.css");
|
||||
//CustomAsset("css/reset-css", thisAssembly, "Swashbuckle.Core.SwaggerUi.Ui.reset.css");
|
||||
//CustomAsset("css/ext-css", thisAssembly, "Swashbuckle.Core.SwaggerUi.Ui.ext.css");
|
||||
}
|
||||
|
||||
public void InjectStylesheet(Assembly resourceAssembly, string resourceName, string media = "screen", bool isTemplate = false)
|
||||
{
|
||||
var path = "ext/" + resourceName.Replace(".", "-");
|
||||
|
||||
var stringBuilder = new StringBuilder(_templateParams["%(StylesheetIncludes)"]);
|
||||
stringBuilder.AppendLine("<link href='" + path + "' media='" + media + "' rel='stylesheet' type='text/css' />");
|
||||
_templateParams["%(StylesheetIncludes)"] = stringBuilder.ToString();
|
||||
|
||||
CustomAsset(path, resourceAssembly, resourceName, isTemplate);
|
||||
}
|
||||
|
||||
public void BooleanValues(IEnumerable<string> values)
|
||||
{
|
||||
_templateParams["%(BooleanValues)"] = String.Join("|", values);
|
||||
}
|
||||
|
||||
public void SetValidatorUrl(string url)
|
||||
{
|
||||
_templateParams["%(ValidatorUrl)"] = url;
|
||||
}
|
||||
|
||||
public void DisableValidator()
|
||||
{
|
||||
_templateParams["%(ValidatorUrl)"] = "null";
|
||||
}
|
||||
|
||||
public void InjectJavaScript(Assembly resourceAssembly, string resourceName, bool isTemplate = false)
|
||||
{
|
||||
var path = "ext/" + resourceName.Replace(".", "-");
|
||||
|
||||
var stringBuilder = new StringBuilder(_templateParams["%(CustomScripts)"]);
|
||||
if (stringBuilder.Length > 0)
|
||||
stringBuilder.Append("|");
|
||||
|
||||
stringBuilder.Append(path);
|
||||
_templateParams["%(CustomScripts)"] = stringBuilder.ToString();
|
||||
|
||||
CustomAsset(path, resourceAssembly, resourceName, isTemplate);
|
||||
}
|
||||
|
||||
public void DocExpansion(DocExpansion docExpansion)
|
||||
{
|
||||
_templateParams["%(DocExpansion)"] = docExpansion.ToString().ToLower();
|
||||
}
|
||||
|
||||
public void SupportedSubmitMethods(params string[] methods)
|
||||
{
|
||||
_templateParams["%(SupportedSubmitMethods)"] = String.Join("|", methods).ToLower();
|
||||
}
|
||||
|
||||
public void CustomAsset(string path, Assembly resourceAssembly, string resourceName, bool isTemplate = false)
|
||||
{
|
||||
if (path == "index") isTemplate = true;
|
||||
_pathToAssetMap[path] = new EmbeddedAssetDescriptor(resourceAssembly, resourceName, isTemplate);
|
||||
}
|
||||
|
||||
public void EnableDiscoveryUrlSelector()
|
||||
{
|
||||
InjectJavaScript(GetType().Assembly, "Swashbuckle.SwaggerUi.CustomAssets.discoveryUrlSelector.js");
|
||||
}
|
||||
|
||||
public void EnableOAuth2Support(string clientId, string realm, string appName)
|
||||
{
|
||||
EnableOAuth2Support(clientId, "N/A", realm, appName);
|
||||
}
|
||||
|
||||
public void EnableOAuth2Support(
|
||||
string clientId,
|
||||
string clientSecret,
|
||||
string realm,
|
||||
string appName,
|
||||
string scopeSeperator = " ",
|
||||
Dictionary<string, string> additionalQueryStringParams = null)
|
||||
{
|
||||
_templateParams["%(OAuth2Enabled)"] = "true";
|
||||
_templateParams["%(OAuth2ClientId)"] = clientId;
|
||||
_templateParams["%(OAuth2ClientSecret)"] = clientSecret;
|
||||
_templateParams["%(OAuth2Realm)"] = realm;
|
||||
_templateParams["%(OAuth2AppName)"] = appName;
|
||||
_templateParams["%(OAuth2ScopeSeperator)"] = scopeSeperator;
|
||||
|
||||
if (additionalQueryStringParams != null)
|
||||
_templateParams["%(OAuth2AdditionalQueryStringParams)"] = JsonConvert.SerializeObject(additionalQueryStringParams);
|
||||
}
|
||||
|
||||
public void EnableApiKeySupport(string name, string apiKeyIn) {
|
||||
_templateParams["%(ApiKeyName)"] = name;
|
||||
_templateParams["%(ApiKeyIn)"] = apiKeyIn;
|
||||
}
|
||||
|
||||
internal IAssetProvider GetSwaggerUiProvider()
|
||||
{
|
||||
return new EmbeddedAssetProvider(_pathToAssetMap, _templateParams);
|
||||
}
|
||||
|
||||
internal string GetRootUrl(HttpRequestMessage swaggerRequest)
|
||||
{
|
||||
return _rootUrlResolver(swaggerRequest);
|
||||
}
|
||||
|
||||
private void MapPathsForSwaggerUiAssets()
|
||||
{
|
||||
var thisAssembly = GetType().Assembly;
|
||||
foreach (var resourceName in thisAssembly.GetManifestResourceNames())
|
||||
{
|
||||
if (resourceName.Contains("Swashbuckle.SwaggerUi.CustomAssets")) continue; // original assets only
|
||||
|
||||
var path = resourceName
|
||||
.Replace("\\", "/");
|
||||
//.Replace(".", "-"); // extensionless to avoid RUMMFAR
|
||||
|
||||
_pathToAssetMap[path] = new EmbeddedAssetDescriptor(thisAssembly, resourceName, path == "index");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum DocExpansion
|
||||
{
|
||||
None,
|
||||
List,
|
||||
Full
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Net;
|
||||
using System.Net.Http.Headers;
|
||||
using Swashbuckle.SwaggerUi;
|
||||
|
||||
namespace Swashbuckle.Application
|
||||
{
|
||||
public class SwaggerUiHandler : HttpMessageHandler
|
||||
{
|
||||
private readonly SwaggerUiConfig _config;
|
||||
|
||||
public SwaggerUiHandler(SwaggerUiConfig config)
|
||||
{
|
||||
_config = config;
|
||||
}
|
||||
|
||||
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
var swaggerUiProvider = _config.GetSwaggerUiProvider();
|
||||
var rootUrl = _config.GetRootUrl(request);
|
||||
var assetPath = request.GetRouteData().Values["assetPath"].ToString();
|
||||
|
||||
try
|
||||
{
|
||||
var webAsset = swaggerUiProvider.GetAsset(rootUrl, assetPath);
|
||||
var content = ContentFor(webAsset);
|
||||
return TaskFor(new HttpResponseMessage { Content = content });
|
||||
}
|
||||
catch (AssetNotFound ex)
|
||||
{
|
||||
return TaskFor(request.CreateErrorResponse(HttpStatusCode.NotFound, ex));
|
||||
}
|
||||
}
|
||||
|
||||
private HttpContent ContentFor(Asset webAsset)
|
||||
{
|
||||
int bufferSize = webAsset.Stream.Length > int.MaxValue
|
||||
? int.MaxValue
|
||||
: (int)webAsset.Stream.Length;
|
||||
|
||||
var content = new StreamContent(webAsset.Stream, bufferSize);
|
||||
content.Headers.ContentType = new MediaTypeHeaderValue(webAsset.MediaType);
|
||||
return content;
|
||||
}
|
||||
|
||||
private Task<HttpResponseMessage> TaskFor(HttpResponseMessage response)
|
||||
{
|
||||
var tsc = new TaskCompletionSource<HttpResponseMessage>();
|
||||
tsc.SetResult(response);
|
||||
return tsc.Task;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using Swashbuckle.Swagger;
|
||||
|
||||
namespace Swashbuckle.Application
|
||||
{
|
||||
public class VendorExtensionsConverter : JsonConverter
|
||||
{
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return objectType.GetField("vendorExtensions") != null;
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
var jsonContract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(value.GetType());
|
||||
|
||||
writer.WriteStartObject();
|
||||
|
||||
foreach (var jsonProp in jsonContract.Properties)
|
||||
{
|
||||
var propValue = jsonProp.ValueProvider.GetValue(value);
|
||||
if (propValue == null && serializer.NullValueHandling == NullValueHandling.Ignore)
|
||||
continue;
|
||||
|
||||
if (jsonProp.PropertyName == "vendorExtensions")
|
||||
{
|
||||
var vendorExtensions = (IDictionary<string, object>)propValue;
|
||||
if (vendorExtensions.Any())
|
||||
{
|
||||
foreach (var entry in vendorExtensions)
|
||||
{
|
||||
writer.WritePropertyName(entry.Key);
|
||||
serializer.Serialize(writer, entry.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.WritePropertyName(jsonProp.PropertyName);
|
||||
serializer.Serialize(writer, propValue);
|
||||
}
|
||||
}
|
||||
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Swashbuckle.Swagger;
|
||||
|
||||
namespace Swashbuckle.Application
|
||||
{
|
||||
public class VersionInfoBuilder
|
||||
{
|
||||
private readonly Dictionary<string, InfoBuilder> _versionInfos;
|
||||
|
||||
public VersionInfoBuilder()
|
||||
{
|
||||
_versionInfos = new Dictionary<string, InfoBuilder>();
|
||||
}
|
||||
|
||||
public InfoBuilder Version(string version, string title)
|
||||
{
|
||||
var infoBuilder = new InfoBuilder(version, title);
|
||||
_versionInfos[version] = infoBuilder;
|
||||
return infoBuilder;
|
||||
}
|
||||
|
||||
public InfoBuilder Version(string version, string title, bool isDefaultRoute)
|
||||
{
|
||||
var infoBuilder = new InfoBuilder(version, title, isDefaultRoute);
|
||||
_versionInfos[version] = infoBuilder;
|
||||
return infoBuilder;
|
||||
}
|
||||
|
||||
public IDictionary<string, Info> Build()
|
||||
{
|
||||
return _versionInfos.ToDictionary(entry => entry.Key, entry => entry.Value.Build());
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user