You've already forked linux-packaging-mono
166 lines
6.7 KiB
C#
166 lines
6.7 KiB
C#
![]() |
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
|
|||
|
|
|||
|
using System.Collections.Concurrent;
|
|||
|
using System.Collections.Generic;
|
|||
|
using System.Diagnostics.CodeAnalysis;
|
|||
|
using System.Diagnostics.Contracts;
|
|||
|
using System.Linq;
|
|||
|
using System.Net;
|
|||
|
using System.Net.Http;
|
|||
|
using System.Text;
|
|||
|
using System.Web.Http.Controllers;
|
|||
|
using System.Web.Http.Properties;
|
|||
|
using System.Web.Http.Routing;
|
|||
|
|
|||
|
namespace System.Web.Http.Dispatcher
|
|||
|
{
|
|||
|
/// <summary>
|
|||
|
/// Default <see cref="IHttpControllerSelector"/> instance for choosing a <see cref="HttpControllerDescriptor"/> given a <see cref="HttpRequestMessage"/>
|
|||
|
/// A different implementation can be registered via the <see cref="HttpConfiguration.Services"/>.
|
|||
|
/// </summary>
|
|||
|
public class DefaultHttpControllerSelector : IHttpControllerSelector
|
|||
|
{
|
|||
|
public static readonly string ControllerSuffix = "Controller";
|
|||
|
|
|||
|
private const string ControllerKey = "controller";
|
|||
|
|
|||
|
private readonly HttpConfiguration _configuration;
|
|||
|
private readonly HttpControllerTypeCache _controllerTypeCache;
|
|||
|
private readonly Lazy<ConcurrentDictionary<string, HttpControllerDescriptor>> _controllerInfoCache;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Initializes a new instance of the <see cref="DefaultHttpControllerSelector"/> class.
|
|||
|
/// </summary>
|
|||
|
/// <param name="configuration">The configuration.</param>
|
|||
|
public DefaultHttpControllerSelector(HttpConfiguration configuration)
|
|||
|
{
|
|||
|
if (configuration == null)
|
|||
|
{
|
|||
|
throw Error.ArgumentNull("configuration");
|
|||
|
}
|
|||
|
|
|||
|
_controllerInfoCache = new Lazy<ConcurrentDictionary<string, HttpControllerDescriptor>>(InitializeControllerInfoCache);
|
|||
|
_configuration = configuration;
|
|||
|
_controllerTypeCache = new HttpControllerTypeCache(_configuration);
|
|||
|
}
|
|||
|
|
|||
|
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Caller is responsible for disposing of response instance.")]
|
|||
|
public virtual HttpControllerDescriptor SelectController(HttpRequestMessage request)
|
|||
|
{
|
|||
|
if (request == null)
|
|||
|
{
|
|||
|
throw Error.ArgumentNull("request");
|
|||
|
}
|
|||
|
|
|||
|
string controllerName = GetControllerName(request);
|
|||
|
if (String.IsNullOrEmpty(controllerName))
|
|||
|
{
|
|||
|
throw new HttpResponseException(request.CreateResponse(HttpStatusCode.NotFound));
|
|||
|
}
|
|||
|
|
|||
|
HttpControllerDescriptor controllerDescriptor;
|
|||
|
if (_controllerInfoCache.Value.TryGetValue(controllerName, out controllerDescriptor))
|
|||
|
{
|
|||
|
return controllerDescriptor;
|
|||
|
}
|
|||
|
|
|||
|
ICollection<Type> matchingTypes = _controllerTypeCache.GetControllerTypes(controllerName);
|
|||
|
|
|||
|
// ControllerInfoCache is already initialized.
|
|||
|
Contract.Assert(matchingTypes.Count != 1);
|
|||
|
|
|||
|
if (matchingTypes.Count == 0)
|
|||
|
{
|
|||
|
// no matching types
|
|||
|
throw new HttpResponseException(request.CreateResponse(
|
|||
|
HttpStatusCode.NotFound,
|
|||
|
Error.Format(SRResources.DefaultControllerFactory_ControllerNameNotFound, controllerName)));
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// multiple matching types
|
|||
|
throw new HttpResponseException(request.CreateResponse(
|
|||
|
HttpStatusCode.InternalServerError,
|
|||
|
CreateAmbiguousControllerExceptionMessage(request.GetRouteData().Route, controllerName, matchingTypes)));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public virtual IDictionary<string, HttpControllerDescriptor> GetControllerMapping()
|
|||
|
{
|
|||
|
return _controllerInfoCache.Value.ToDictionary(c => c.Key, c => c.Value, StringComparer.OrdinalIgnoreCase);
|
|||
|
}
|
|||
|
|
|||
|
public virtual string GetControllerName(HttpRequestMessage request)
|
|||
|
{
|
|||
|
if (request == null)
|
|||
|
{
|
|||
|
throw Error.ArgumentNull("request");
|
|||
|
}
|
|||
|
|
|||
|
IHttpRouteData routeData = request.GetRouteData();
|
|||
|
if (routeData == null)
|
|||
|
{
|
|||
|
return null;
|
|||
|
}
|
|||
|
|
|||
|
// Look up controller in route data
|
|||
|
string controllerName = null;
|
|||
|
routeData.Values.TryGetValue(ControllerKey, out controllerName);
|
|||
|
return controllerName;
|
|||
|
}
|
|||
|
|
|||
|
private static string CreateAmbiguousControllerExceptionMessage(IHttpRoute route, string controllerName, ICollection<Type> matchingTypes)
|
|||
|
{
|
|||
|
Contract.Assert(route != null);
|
|||
|
Contract.Assert(controllerName != null);
|
|||
|
Contract.Assert(matchingTypes != null);
|
|||
|
|
|||
|
// Generate an exception containing all the controller types
|
|||
|
StringBuilder typeList = new StringBuilder();
|
|||
|
foreach (Type matchedType in matchingTypes)
|
|||
|
{
|
|||
|
typeList.AppendLine();
|
|||
|
typeList.Append(matchedType.FullName);
|
|||
|
}
|
|||
|
|
|||
|
return Error.Format(SRResources.DefaultControllerFactory_ControllerNameAmbiguous_WithRouteTemplate, controllerName, route.RouteTemplate, typeList);
|
|||
|
}
|
|||
|
|
|||
|
private ConcurrentDictionary<string, HttpControllerDescriptor> InitializeControllerInfoCache()
|
|||
|
{
|
|||
|
var result = new ConcurrentDictionary<string, HttpControllerDescriptor>(StringComparer.OrdinalIgnoreCase);
|
|||
|
var duplicateControllers = new HashSet<string>();
|
|||
|
Dictionary<string, ILookup<string, Type>> controllerTypeGroups = _controllerTypeCache.Cache;
|
|||
|
|
|||
|
foreach (KeyValuePair<string, ILookup<string, Type>> controllerTypeGroup in controllerTypeGroups)
|
|||
|
{
|
|||
|
string controllerName = controllerTypeGroup.Key;
|
|||
|
|
|||
|
foreach (IGrouping<string, Type> controllerTypesGroupedByNs in controllerTypeGroup.Value)
|
|||
|
{
|
|||
|
foreach (Type controllerType in controllerTypesGroupedByNs)
|
|||
|
{
|
|||
|
if (result.Keys.Contains(controllerName))
|
|||
|
{
|
|||
|
duplicateControllers.Add(controllerName);
|
|||
|
break;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
result.TryAdd(controllerName, new HttpControllerDescriptor(_configuration, controllerName, controllerType));
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
foreach (string duplicateController in duplicateControllers)
|
|||
|
{
|
|||
|
HttpControllerDescriptor descriptor;
|
|||
|
result.TryRemove(duplicateController, out descriptor);
|
|||
|
}
|
|||
|
|
|||
|
return result;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|