效果图如下:
打开 管理NuGet程序包 添加 Swashbuckle:
public class CachingSwaggerProvider : ISwaggerProvider
{
private static ConcurrentDictionary _cache =
new ConcurrentDictionary();
private readonly ISwaggerProvider _swaggerProvider;
public CachingSwaggerProvider(ISwaggerProvider swaggerProvider)
{
_swaggerProvider = swaggerProvider;
}
public SwaggerDocument GetSwagger(string rootUrl, string apiVersion)
{
var cacheKey = String.Format("{0}_{1}", rootUrl, apiVersion);
SwaggerDocument srcDoc = null;
//只读取一次
if (!_cache.TryGetValue(cacheKey, out srcDoc))
{
srcDoc = _swaggerProvider.GetSwagger(rootUrl, apiVersion);
var patht = new Dictionary();
foreach (var item in srcDoc.paths)
{
var arr = item.Key.Split('/');
var i = arr[3].LastIndexOf('.') + 1;
if (i != -1)
{
arr[3] = arr[3].Substring(i);
}
patht.Add(string.Join("/", arr), item.Value);
}
srcDoc.paths = patht;
HashSet moduleList = new HashSet();
srcDoc.vendorExtensions = new Dictionary
{
{"ControllerDesc", GetControllerDesc(moduleList)},
{"AreaDescription", moduleList}
};
_cache.TryAdd(cacheKey, srcDoc);
}
return srcDoc;
}
///
/// 从API文档中读取控制器描述
///
/// 所有控制器描述
public static ConcurrentDictionary GetControllerDesc(HashSet moduleList)
{
string xmlpath = String.Format("{0}/bin/BrnMall.Web.XML", AppDomain.CurrentDomain.BaseDirectory);
ConcurrentDictionary controllerDescDict = new ConcurrentDictionary();
if (File.Exists(xmlpath))
{
XmlDocument xmldoc = new XmlDocument();
xmldoc.Load(xmlpath);
string type = String.Empty, path = String.Empty, controllerName = String.Empty;
string[] arrPath;
int length = -1, cCount = "Controller".Length;
XmlNode summaryNode = null;
foreach (XmlNode node in xmldoc.SelectNodes("//member"))
{
type = node.Attributes["name"].Value;
if (type.StartsWith("T:"))
{
//控制器
arrPath = type.Split('.');
length = arrPath.Length;
controllerName = arrPath[length - 1];
if (controllerName.EndsWith("Controller"))
{
//模块信息
var moduleName = arrPath[length - 2];
moduleList.Add(moduleName);
//获取控制器注释
summaryNode = node.SelectSingleNode("summary");
string key = controllerName.Remove(controllerName.Length - cCount, cCount);
if (summaryNode != null && !String.IsNullOrEmpty(summaryNode.InnerText) && !controllerDescDict.ContainsKey(key))
{
controllerDescDict.TryAdd(key, summaryNode.InnerText.Trim());
}
}
}
}
}
return controllerDescDict;
}
}
App_Start 文件夹下添加 CachingSwaggerProvider 类:
在SwaggerConfig类中添加如下配置:
配置多版本
public class SwaggerVersionHelper
{
public static bool ResolveVersionSupportByRouteConstraint(ApiDescription apiDesc, string targetApiVersion)
{
var attr = apiDesc.ActionDescriptor.ControllerDescriptor.GetCustomAttributes().FirstOrDefault();
return attr.Version == Convert.ToInt32(targetApiVersion.TrimStart('v'));
}
}
[AttributeUsage(AttributeTargets.All)]
public class VersionedRoute : Attribute
{
public VersionedRoute(string name, int version)
{
Name = name;
Version = version;
}
public string Name { get; set; }
public int Version { get; set; }
}
c.MultipleApiVersions(
(apiDesc, targetApiVersion) => SwaggerVersionHelper.ResolveVersionSupportByRouteConstraint(apiDesc, targetApiVersion),
(vc) =>
{
vc.Version("v1", "App接口V1");
vc.Version("v2", "App接口V2");
vc.Version("v3", "App接口V3");
});
配置返回参数
c.CustomProvider((defaultProvider) => new CachingSwaggerProvider(defaultProvider));
添加注释
string path = string.Format("{0}/bin/BrnMall.Core.XML", System.AppDomain.CurrentDomain.BaseDirectory);
c.IncludeXmlComments(path);
path = string.Format("{0}/bin/BrnMall.Web.XML", System.AppDomain.CurrentDomain.BaseDirectory);
c.IncludeXmlComments(path);
path = string.Format("{0}/bin/BrnMall.Services.XML", System.AppDomain.CurrentDomain.BaseDirectory);
c.IncludeXmlComments(path);
添加VersionControllerSelector 类用于实现多版控制器:
public class VersionControllerSelector : IHttpControllerSelector
{
private const string VersionKey = "version";
private const string ControllerKey = "controller";
private readonly HttpConfiguration _configuration;
private readonly Lazy> _controllers;
private readonly HashSet _duplicates;
public VersionControllerSelector(HttpConfiguration config)
{
_configuration = config;
_duplicates = new HashSet(StringComparer.OrdinalIgnoreCase);
_controllers = new Lazy>(InitializeControllerDictionary);
}
private Dictionary InitializeControllerDictionary()
{
var dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase);
IAssembliesResolver assembliesResolver = _configuration.Services.GetAssembliesResolver();
IHttpControllerTypeResolver controllersResolver = _configuration.Services.GetHttpControllerTypeResolver();
ICollection controllerTypes = controllersResolver.GetControllerTypes(assembliesResolver);
foreach (Type t in controllerTypes)
{
var segments = t.Namespace.Split(Type.Delimiter);
var controllerName = t.Name.Remove(t.Name.Length - DefaultHttpControllerSelector.ControllerSuffix.Length);
string version = segments[segments.Length - 1];
var key = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", version, controllerName);
if (version == "Controllers")
{
key = String.Format(CultureInfo.InvariantCulture, "{0}", controllerName);
}
if (dictionary.Keys.Contains(key))
{
_duplicates.Add(key);
}
else
{
dictionary[key] = new HttpControllerDescriptor(_configuration, t.Name, t);
}
}
foreach (string s in _duplicates)
{
dictionary.Remove(s);
}
return dictionary;
}
private static T GetRouteVariable(IHttpRouteData routeData, string name)
{
object result = null;
if (routeData.Values.TryGetValue(name, out result))
{
return (T)result;
}
return default(T);
}
public HttpControllerDescriptor SelectController(HttpRequestMessage request)
{
IHttpRouteData routeData = request.GetRouteData();
if (routeData == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
string version = GetRouteVariable(routeData, VersionKey);
if (string.IsNullOrEmpty(version))
{
version = GetVersionFromHTTPHeaderAndAcceptHeader(request);
}
string controllerName = GetRouteVariable(routeData, ControllerKey);
if (controllerName == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
string key = String.Format(CultureInfo.InvariantCulture, "{0}", controllerName);
if (!string.IsNullOrEmpty(version))
{
key = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", version, controllerName);
}
HttpControllerDescriptor controllerDescriptor;
if (_controllers.Value.TryGetValue(key, out controllerDescriptor))
{
return controllerDescriptor;
}
else if (_duplicates.Contains(key))
{
throw new HttpResponseException(
request.CreateErrorResponse(HttpStatusCode.InternalServerError,
"Multiple controllers were found that match this request."));
}
else
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
}
public IDictionary GetControllerMapping()
{
return _controllers.Value;
}
private string GetVersionFromHTTPHeaderAndAcceptHeader(HttpRequestMessage request)
{
if (request.Headers.Contains(VersionKey))
{
var versionHeader = request.Headers.GetValues(VersionKey).FirstOrDefault();
if (versionHeader != null)
{
return versionHeader;
}
}
var acceptHeader = request.Headers.Accept;
foreach (var mime in acceptHeader)
{
if (mime.MediaType == "application/json" || mime.MediaType == "text/html")
{
var version = mime.Parameters
.Where(v => v.Name.Equals(VersionKey, StringComparison.OrdinalIgnoreCase))
.FirstOrDefault();
if (version != null)
{
return version.Value;
}
return string.Empty;
}
}
return string.Empty;
}
}
在WebApiConfig类中添加如下代码
config.Services.Replace(typeof(IHttpControllerSelector), new VersionControllerSelector(config));
测试代码如下:
namespace BrnMall.Web.Areas.Web.Controllers.v1
{
[VersionedRoute("api/vrsion", 1)]
public class TextController : BaseWebController
{
}
}
namespace BrnMall.Web.Areas.Web.Controllers.v2
{
[VersionedRoute("api/vrsion", 2)]
public class TextController : BaseWebController
{
}
}
namespace BrnMall.Web.Areas.Web.Controllers.v3
{
[VersionedRoute("api/vrsion", 3)]
public class TextController : BaseWebController
{
}
}
到此就大功告成!!