using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Dispatcher;
using System.Web.Http.Routing;
namespace Lottomat.SOA.API.Selector
{
///
/// WebApi版本控制
///
public class WebApiControllerSelector : IHttpControllerSelector
{
private const string VersionKey = "version";
private const string ControllerKey = "controller";
private readonly HttpConfiguration _configuration;
private readonly Lazy> _controllers;
private readonly HashSet _duplicates;
private WebApiControllerSelector()
{
}
///
/// 构造
///
///
public WebApiControllerSelector(HttpConfiguration config)
{
_configuration = config;
_duplicates = new HashSet(StringComparer.OrdinalIgnoreCase);
_controllers = new Lazy>(InitializeControllerDictionary);
}
///
/// 初始化字典
///
///
private Dictionary InitializeControllerDictionary()
{
var dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase);
// Create a lookup table where key is "namespace.controller". The value of "namespace" is the last
// segment of the full namespace. For example:
// MyApplication.Controllers.V1.ProductsController => "V1.Products"
IAssembliesResolver assembliesResolver = _configuration.Services.GetAssembliesResolver();
IHttpControllerTypeResolver controllersResolver = _configuration.Services.GetHttpControllerTypeResolver();
ICollection controllerTypes = controllersResolver.GetControllerTypes(assembliesResolver);
foreach (Type t in controllerTypes)
{
if (t.Namespace != null)
{
var segments = t.Namespace.Split(Type.Delimiter);
// For the dictionary key, strip "Controller" from the end of the type name.
// This matches the behavior of DefaultHttpControllerSelector.
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);
}
// Check for duplicate keys.
if (dictionary.Keys.Contains(key))
{
_duplicates.Add(key);
}
else
{
dictionary[key] = new HttpControllerDescriptor(_configuration, t.Name, t);
}
}
}
// Remove any duplicates from the dictionary, because these create ambiguous matches.
// For example, "Foo.V1.ProductsController" and "Bar.V1.ProductsController" both map to "v1.products".
foreach (string s in _duplicates)
{
dictionary.Remove(s);
}
return dictionary;
}
///
/// 取路由相应值
///
///
///
///
///
private static T GetRouteVariable(IHttpRouteData routeData, string name)
{
if (routeData.Values.TryGetValue(name, out object 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 = GetVersionFromAcceptHeaderVersion(request);
}
//获取控制器名称
string controllerName = GetRouteVariable(routeData, ControllerKey);
if (string.IsNullOrEmpty(controllerName))
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
else
{
// Find a matching controller.
string key = String.Format(CultureInfo.InvariantCulture, "{0}", controllerName);
if (!string.IsNullOrEmpty(version))
{
key = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", version.ToUpper(), controllerName);
}
if (_controllers.Value.TryGetValue(key, out HttpControllerDescriptor controllerDescriptor))
{
return controllerDescriptor;
}
else if (_duplicates.Contains(key))
{
throw new HttpResponseException(
request.CreateErrorResponse(HttpStatusCode.InternalServerError,
"找到了与此请求相匹配的多个控制器."));
}
else
{
throw new HttpResponseException(
request.CreateErrorResponse(HttpStatusCode.NotFound,
"找不到请求资源,请检查请求地址是否正确."));
//throw new HttpResponseException(HttpStatusCode.NotFound);
}
}
}
///
/// 获取映射信息
///
///
public IDictionary GetControllerMapping()
{
return _controllers.Value;
}
///
/// 从请求头里面获取版本信息
///
///
///
private string GetVersionFromAcceptHeaderVersion(HttpRequestMessage request)
{
if (request.Headers.Contains(VersionKey))
{
string 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")
{
NameValueHeaderValue version = mime.Parameters
.FirstOrDefault(v => v.Name.Equals(VersionKey, StringComparison.OrdinalIgnoreCase));
if (version != null)
{
return version.Value;
}
return string.Empty;
}
}
return string.Empty;
}
}
}