asp.net web api 版本控制
阿新 • • 發佈:2022-05-03
版本控制
版本控制的方法有很多,這裡提供一種將Odata與普通web api版本控制機制統一的方法,但也可以單獨控制,整合控制與單獨控制主要的不同是:整合控制通過VersionControllerSelector來選擇控制器過濾器,而不是直接選擇控制器。
採用此機制來控制版本,應按照如下規則命名控制器:
自定義識別符號+版本+Controller
自定義識別符號:能體現控制器含義的字串
版本:表示版本的字串,例如:V1,V1.0;不建議使用V1.0這樣的寫法,因為這樣控制器名稱會相當怪異,如果表示小版本號,那麼可以使用V1D0,這種寫法,即用一個字母代替句號。
名稱空間對應了專案檔案的組織形式,控制器的名稱空間為:
1 Odata版本控制
擴充套件DefaultHttpControllerSelector
public class ODataVersionControllerSelector : DefaultHttpControllerSelector { public Dictionary<string, string> RouteVersionSuffixMapping { get; set; } public ODataVersionControllerSelector(HttpConfiguration configuration) : base(configuration) { if (RouteVersionSuffixMapping == null) { RouteVersionSuffixMapping = new Dictionary<string, string>(); } } public override string GetControllerName(HttpRequestMessage request) { var controllerName = base.GetControllerName(request); if (string.IsNullOrEmpty(controllerName)) { return controllerName; } var routeName = request.ODataProperties().RouteName; if (string.IsNullOrEmpty(routeName)) { return controllerName; } var mapping = GetControllerMapping(); if (!RouteVersionSuffixMapping.ContainsKey(routeName)) { return controllerName; } var versionControllerName = controllerName + RouteVersionSuffixMapping[routeName]; return mapping.ContainsKey(versionControllerName) ? versionControllerName : controllerName; } }
修改WebApiConfig.Register方法
public static class WebApiConfig { public static void Register(HttpConfiguration config) { ...... //odata路由 config.MapODataServiceRoute( routeName: "V1OdataRouteVersioning", routePrefix: "Odata/V1", model: GetEdmModel()); config.Count().Filter().OrderBy().Expand().Select().MaxTop(null); config.AddODataQueryFilter(); config.Services.Replace(typeof(IHttpControllerSelector), new ODataVersionControllerSelector (config)); var controllerSelector = config.Services.GetService(typeof(IHttpControllerSelector)) as ODataVersionControllerSelector ; controllerSelector.RouteVersionSuffixMapping.Add("V1OdataRouteVersioning", "V1"); ...... } } private static IEdmModel GetEdmModel() { ODataConventionModelBuilder builder = new ODataConventionModelBuilder(); #region Publication var publicationsSet = builder.EntitySet<Publication>("Publications").EntityType.Collection; var getPublicationsFunction = publicationsSet.Function("GetPublications").Returns<PublicationDTO>(); getPublicationsFunction.Parameter<int>("userId"); publicationsSet.Action("AddPublication").Returns<int>().Parameter<PublicationAddBindingModel>("publicationAddBM"); publicationsSet.Action("DeletePublication").Returns<IHttpActionResult>().Parameter<PublicationDelBindingModel>("publicationDelBM"); #endregion builder.Namespace = "Service"; return builder.GetEdmModel(); }
2 普通Api版本控制
擴充套件IHttpControllerSelector
public class NormalVersionControllerSelector : IHttpControllerSelector
{
private const string Version = "version";
private const string ControllerKey = "controller";
private readonly HttpConfiguration _configuration;
private readonly Lazy<Dictionary<string, HttpControllerDescriptor>> _controllers;
private readonly HashSet<string> _duplicates;
public NormalVersionControllerSelector(HttpConfiguration config)
{
_configuration = config;
_duplicates = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
_controllers = new Lazy<Dictionary<string, HttpControllerDescriptor>>(InitializeControllerDictionary);
}
private Dictionary<string, HttpControllerDescriptor> InitializeControllerDictionary()
{
var dictionary = new Dictionary<string, HttpControllerDescriptor>(StringComparer.OrdinalIgnoreCase);
IAssembliesResolver assembliesResolver = _configuration.Services.GetAssembliesResolver();
IHttpControllerTypeResolver controllersResolver = _configuration.Services.GetHttpControllerTypeResolver();
ICollection<Type> controllerTypes = controllersResolver.GetControllerTypes(assembliesResolver);
foreach (Type t in controllerTypes)
{
var segments = t.Namespace.Split(Type.Delimiter);
//去掉HY_WebApi.V1.Controllers.KeyController中的HY_WebApi.
//去掉HY_WebApi.HYDB.V1.Controllers.HYSearchController中的HY_WebApi.HYDB.
//因此,保留V1.Controllers.KeyController這三部分
//鍵值格式如:V1.Controllers.KeyController
string[] items = t.FullName.Split(new char[]{'.'},StringSplitOptions.RemoveEmptyEntries);
int count = items.Count();
var key = string.Format("{0}.{1}.{2}", items[count - 3], items[count - 2], items[count - 1]);
// Check for duplicate keys.
if (dictionary.ContainsKey(key))
{
_duplicates.Add(key);
}
else
{
dictionary[key] = new HttpControllerDescriptor(_configuration, t.Name, t);
}
}
return dictionary;
}
// Get a value from the route data, if present.
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);
}
public HttpControllerDescriptor SelectController(HttpRequestMessage request)
{
IHttpRouteData routeData = request.GetRouteData();
if (routeData == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
// Get the namespace and controller variables from the route data.
string version = GetRouteVariable<string>(routeData, Version);
if (version == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
string controllerName = GetRouteVariable<string>(routeData, ControllerKey);
if (controllerName == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
// 匹配控制器
string key = String.Format("{0}.Controllers.{1}{2}Controller", version, controllerName,version);
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<string, HttpControllerDescriptor> GetControllerMapping()
{
return _controllers.Value;
}
}
}
修改WebApiConfig.Register方法
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
......
// Web API 路由
config.Routes.MapHttpRoute(
name: "defaultRoute",
routeTemplate: "api/{version}/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Services.Replace(typeof(IHttpControllerSelector), new NormalVersionControllerSelector(config));
......
}
}
3 同時支援Odata,與普通Web Api版本控制
擴充套件DefaultHttpControllerSelector
public class VersionControllerSelector : DefaultHttpControllerSelector
{
public Dictionary<string, string> RouteVersionSuffixMapping { get; set; }
private HttpConfiguration configuration;
public VersionControllerSelector(HttpConfiguration configuration)
: base(configuration)
{
this.configuration = configuration;
if (RouteVersionSuffixMapping == null)
{
RouteVersionSuffixMapping = new Dictionary<string, string>();
}
}
public override string GetControllerName(HttpRequestMessage request)
{
return SelectController(request).ControllerName;
}
public override HttpControllerDescriptor SelectController(HttpRequestMessage request)
{
var routeName = request.ODataProperties().RouteName;
if (!string.IsNullOrWhiteSpace(routeName))
{//odata路由
var selector = new ODataVersionControllerSelector(configuration);
selector.RouteVersionSuffixMapping = RouteVersionSuffixMapping;
return selector.SelectController(request);
}
else
{//普通路由
var selector = new NormalVersionControllerSelector(configuration);
return selector.SelectController(request);
}
}
}
修改WebApiConfig.Register方法
public static class WebApiConfig
public static void Register(HttpConfiguration config)
{
// Web API 路由
config.Routes.MapHttpRoute(
name: "defaultRoute",
routeTemplate: "api/{version}/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
//odata路由
config.MapODataServiceRoute(
routeName: "V1OdataRouteVersioning",
routePrefix: "Odata/V1",
model: GetEdmModel());
config.Count().Filter().OrderBy().Expand().Select().MaxTop(null);
config.AddODataQueryFilter();
config.Services.Replace(typeof(IHttpControllerSelector), new VersionControllerSelector(config));
var controllerSelector = config.Services.GetService(typeof(IHttpControllerSelector)) as VersionControllerSelector;
controllerSelector.RouteVersionSuffixMapping.Add("V1OdataRouteVersioning", "V1");
}
}
其中GetEdmModel()方法與前述方法相同。