WebApi 自定义版本选择器

using Manjinba.Communication.Common.Logging;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Reflection;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Dispatcher;
using System.Web.Http.Routing;

namespace Manjinba.Communication.WebApi.Selector
{
/// <summary>
/// 版本控制选择器
/// </summary>
public class VersionControllerSelector : IHttpControllerSelector
{
// Fields
private readonly HttpConfiguration _configuration;
private readonly Lazy<ConcurrentDictionary<string, HttpControllerDescriptor>> _controllerInfoCache;
private readonly Lazy<ConcurrentDictionary<string, HttpControllerDescriptor>> _actionInfoCache;
private const string ControllerKey = "controller";
private const string ActionKey = "action";
private const string DefaultVersion = "v1_2"; //默认版本号,因为之前的api我们没有版本号的概念
private const string DefaultNamespaces = "Manjinba.Communication.WebApi"; //为了演示方便,这里就用到一个命名空间
private const string RouteVersionKey = "version"; //路由规则中Version的字符串
private const string DictKeyFormat = "{0}.{1}";
private const string ActionDictKeyFormat = "{0}.{1}.{2}";
private readonly HashSet<string> _duplicates;
// Methods
/// <summary>
/// 初始化
/// </summary>
/// <param name="configuration"></param>
public VersionControllerSelector(HttpConfiguration configuration)
{
if (configuration == null)
{
throw new ArgumentNullException("configuration");
}
this._controllerInfoCache = new Lazy<ConcurrentDictionary<string, HttpControllerDescriptor>>(new Func<ConcurrentDictionary<string, HttpControllerDescriptor>>(this.InitializeControllerInfoCache));
this._actionInfoCache = new Lazy<ConcurrentDictionary<string, HttpControllerDescriptor>>(new Func<ConcurrentDictionary<string, HttpControllerDescriptor>>(this.InitializeActionInfoCache));
this._configuration = configuration;
this._duplicates = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
}
/// <summary>
/// 获取和筛选控制器路由映射
/// </summary>
/// <returns></returns>
public virtual IDictionary<string, HttpControllerDescriptor> GetControllerMapping()
{
var controllerMapping = new Dictionary<string, HttpControllerDescriptor>();
foreach (var p in _controllerInfoCache.Value)
{
var controllerKey = p.Key?.Split('.')?.Count() > 1 ? p.Key.Split('.')[1] : p.Key.Split('.').FirstOrDefault();
if (!controllerMapping.Keys.Any(a => a.Equals(controllerKey, StringComparison.OrdinalIgnoreCase)))
{
controllerMapping.Add(controllerKey, p.Value);
}
}
return controllerMapping;
}
/// <summary>
/// 获取控制器名称
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
public virtual string GetControllerName(HttpRequestMessage request)
{
if (request == null)
{
throw new ArgumentNullException("request");
}
IHttpRouteData routeData = request.GetRouteData();
if (routeData == null)
{
throw new ArgumentNullException("routeData");
}
object str = null;
routeData.Values.TryGetValue(ControllerKey, out str);
return str.ToString();
}
/// <summary>
/// 获取路由映射
/// </summary>
/// <returns></returns>
private ConcurrentDictionary<string, HttpControllerDescriptor> InitializeControllerInfoCache()
{
ConcurrentDictionary<string, HttpControllerDescriptor> dictionary = new ConcurrentDictionary<string, HttpControllerDescriptor>(StringComparer.OrdinalIgnoreCase);
var assemblies = _configuration.Services.GetAssembliesResolver();//解析程序集
var controllerResolver = _configuration.Services.GetHttpControllerTypeResolver();
var controllerTypes = controllerResolver.GetControllerTypes(assemblies);
foreach (var t in controllerTypes)
{
if (t.Namespace.Contains(DefaultNamespaces)) //符合NameSpace规则
{
var segments = t.Namespace.Split(Type.Delimiter);
var version = t.Namespace.Equals(DefaultNamespaces, StringComparison.OrdinalIgnoreCase) ?
DefaultVersion : segments[segments.Length - 1];
var controllerName = t.Name.Remove(t.Name.Length - DefaultHttpControllerSelector.ControllerSuffix.Length);
var key = string.Format(DictKeyFormat, version, controllerName);
if (!dictionary.ContainsKey(key))
{
dictionary.TryAdd(key, new HttpControllerDescriptor(_configuration, t.Name, t));
}
else
{
_duplicates.Add(key);
}
}
}
return dictionary;
}
/// <summary>
/// 获取路由映射
/// </summary>
/// <returns></returns>
private ConcurrentDictionary<string, HttpControllerDescriptor> InitializeActionInfoCache()
{
ConcurrentDictionary<string, HttpControllerDescriptor> dictionary = new ConcurrentDictionary<string, HttpControllerDescriptor>(StringComparer.OrdinalIgnoreCase);
var assemblies = _configuration.Services.GetAssembliesResolver();//解析程序集
var controllerResolver = _configuration.Services.GetHttpControllerTypeResolver();
var controllerTypes = controllerResolver.GetControllerTypes(assemblies);
foreach (var t in controllerTypes)
{
if (t.Namespace.Contains(DefaultNamespaces)) //符合NameSpace规则
{
var segments = t.Namespace.Split(Type.Delimiter);
var version = t.Namespace.Equals(DefaultNamespaces, StringComparison.OrdinalIgnoreCase) ?
DefaultVersion : segments[segments.Length - 1];
var controllerName = t.Name.Remove(t.Name.Length - DefaultHttpControllerSelector.ControllerSuffix.Length);
var methodTypes = t.GetMethods().Where(w =>
"JsonResult`1".Equals(w.GetRuntimeBaseDefinition()?.ReturnParameter?.ParameterType?.Name)
|| "Task`1".Equals(w.GetRuntimeBaseDefinition()?.ReturnParameter?.ParameterType?.Name)
)?.ToList();
foreach (var method in methodTypes)
{
var key = string.Format(ActionDictKeyFormat, version, controllerName, method.Name);
if (!dictionary.ContainsKey(key))
{
dictionary.TryAdd(key, new HttpControllerDescriptor(_configuration, t.Name, t));
}
}
}
}
return dictionary;
}
/// <summary>
/// 定位控制器
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
public virtual HttpControllerDescriptor SelectController(HttpRequestMessage request)
{
if (request == null)
{
throw new ArgumentNullException("request");
}
IHttpRouteData routeData = request.GetRouteData();
if (routeData.Values.Keys.Count == 1 && routeData.Values.Keys.FirstOrDefault().Equals("MS_SubRoutes"))
{
return new DefaultHttpControllerSelector(_configuration).SelectController(request);
}
string version = GetRouteVariable<string>(routeData, RouteVersionKey);
if (version == null)
{
version = DefaultVersion;
}
else
{
version = version.Replace('.', '_');
if (version.Equals("api", StringComparison.OrdinalIgnoreCase))
{
version = "V1_2";
}
}
string controllerName = GetRouteVariable<string>(routeData, ControllerKey);
var actionName = GetRouteVariable<string>(routeData, ActionKey);
if (controllerName.Equals("search", StringComparison.OrdinalIgnoreCase))
{
version = "V1_4";
}
if (controllerName.Equals("api", StringComparison.OrdinalIgnoreCase))
{
if (actionName.Equals("search", StringComparison.OrdinalIgnoreCase))
{
version = "V1_4";
controllerName = "Search";
}
else
{
controllerName = "IMMessage";
}
}
if (controllerName.Equals("Agreement", StringComparison.OrdinalIgnoreCase) || controllerName.Equals("Help", StringComparison.OrdinalIgnoreCase)
|| controllerName.Equals("App", StringComparison.OrdinalIgnoreCase))
{
return new DefaultHttpControllerSelector(_configuration).SelectController(request);
}
if (controllerName == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
var key = string.Empty;
var isMatched = false;// 路由是否匹配到
var isToLowerMatchStart = false;
version = version.ToUpper();
try
{
var versionList = GetAllVersion();
if (versionList != null && !versionList.Contains(version))// 如果请求的版本号不在版本列表中,则从最后版本向老版本追溯
{
isToLowerMatchStart = true;
}
foreach (var v in versionList)
{
if (v.Equals(version))
{
isToLowerMatchStart = true;
}
if (isToLowerMatchStart)// 只往更老版本追溯
{
// 匹配控制器
key = string.Format(DictKeyFormat, v, controllerName);
var actionKey = string.Format(ActionDictKeyFormat, v, controllerName, actionName);
HttpControllerDescriptor controllerDescriptor;
if (_actionInfoCache.Value.TryGetValue(actionKey, out controllerDescriptor))
{
if (_controllerInfoCache.Value.TryGetValue(key, out controllerDescriptor))
{
isMatched = true;
return controllerDescriptor;
}
}
else
{
continue;
}
}
}
}
catch (Exception e)
{
LogHelper.GetLog().Error(e.Message + e.StackTrace);
throw new HttpResponseException(
request.CreateErrorResponse(HttpStatusCode.InternalServerError,
"Multiple controllers were found that match this request."));
}
if (!isMatched && _duplicates.Contains(key))
{
throw new HttpResponseException(
request.CreateErrorResponse(HttpStatusCode.InternalServerError,
"Multiple controllers were found that match this request."));
}
else
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
}
/// <summary>
/// Get a value from the route data, if present.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="routeData"></param>
/// <param name="name"></param>
/// <returns></returns>
private static T GetRouteVariable<T>(IHttpRouteData routeData, string name)
{
object result = null;
if (routeData.Values.TryGetValue(name, out result))
{
return (T)result;
}
return default(T);
}
/// <summary>
/// 获取当前执行程序控制层的所有版本号
/// </summary>
/// <returns></returns>
private static List<string> GetAllVersion()
{
var versionList = new List<string>();
var assembly = Assembly.GetExecutingAssembly();
if (assembly != null)
{
var types = assembly.GetTypes();
foreach (var t in types)
{
if (t.FullName.StartsWith("Manjinba.Communication.WebApi.Controllers.V") || t.FullName.StartsWith("Manjinba.Communication.WebApi.Controllers.api.V"))
{
var cversion = t.Namespace.Substring(t.Namespace.LastIndexOf('.') + 1);
if (!versionList.Contains(cversion))
{
versionList.Add(t.Namespace.Substring(t.Namespace.LastIndexOf('.') + 1));
}
}
}
versionList.Sort();
versionList.Reverse();
}
return versionList;
}
}
}

using Manjinba.Communication.WebApi.Filters;
using Manjinba.Communication.WebApi.Selector;
using System.Configuration;
using System.Web.Http;
using System.Web.Http.Cors;
using System.Web.Http.Dispatcher;

namespace Manjinba.Communication.WebApi
{
/// <summary>
/// WebApi配置
/// </summary>
public static class WebApiConfig
{
/// <summary>
/// 注册
/// </summary>
/// <param name="config"></param>
public static void Register(HttpConfiguration config)
{

config.Formatters.JsonFormatter.SerializerSettings = new Newtonsoft.Json.JsonSerializerSettings()
{
NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore
};

//以下跨域请求在Nginx中配置  跨域访问可在WebApiConfig中配置,也可以在Web.config中全局配置
//config.EnableCors(new EnableCorsAttribute("*", "*", "*"));
//var allowOrigins = ConfigurationManager.AppSettings["cors_allowOrigins"] ?? "*";
//var allowHeaders = ConfigurationManager.AppSettings["cors_allowHeaders"];
//var allowMethods = ConfigurationManager.AppSettings["cors_allowMethods"];
//var globalCors = new EnableCorsAttribute(allowOrigins, allowHeaders, allowMethods);
//config.EnableCors(globalCors);
config.MapHttpAttributeRoutes();

#if !DEBUG

// ActionFilterAttribute
config.Filters.Add(new ApiSecurityFilter());
#endif

config.Filters.Add(new ValidateModelFilter());
config.Routes.MapHttpRoute(
name: "VersionApi",
routeTemplate: "{version}/{controller}/{action}",
defaults: new { id = RouteParameter.Optional }
);

config.Filters.Add(new ValidateModelFilter());
config.Routes.MapHttpRoute(
name: "SimpleApi",
routeTemplate: "{controller}/{action}",
defaults: new { id = RouteParameter.Optional }
);

config.Filters.Add(new ValidateModelFilter());
config.Routes.MapHttpRoute(
name: "DefaultVersionApi",
routeTemplate: "{version}/api/{controller}/{action}",
defaults: new { id = RouteParameter.Optional }
);

//版本信息在Uri中
config.Services.Replace(typeof(IHttpControllerSelector), new VersionControllerSelector(config));
}
}
}

原文地址:https://www.cnblogs.com/Nine4Cool/p/10491505.html