ASP.NET Web Api - Route
Web Api 不支持多路由
就是说 如果你创建了 多个 Area
就算在 route 中注册了 {area} web api 也是不认得
所以需要重写规则

public class NamespaceHttpControllerSelector : DefaultHttpControllerSelector { private const string NamespaceRouteVariableName = "namespaceName"; private readonly HttpConfiguration _configuration; private readonly Lazy<ConcurrentDictionary<string, Type>> _apiControllerCache; public NamespaceHttpControllerSelector(HttpConfiguration configuration) : base(configuration) { _configuration = configuration; _apiControllerCache = new Lazy<ConcurrentDictionary<string, Type>>(InitializeApiControllerCache); } private ConcurrentDictionary<string, Type> InitializeApiControllerCache() { IAssembliesResolver assembliesResolver = this._configuration.Services.GetAssembliesResolver(); Dictionary<string, Type> types = this._configuration.Services.GetHttpControllerTypeResolver() .GetControllerTypes(assembliesResolver).ToDictionary(t => t.FullName, t => t); return new ConcurrentDictionary<string, Type>(types); } public IEnumerable<string> GetControllerFullName(HttpRequestMessage request, string controllerName) { object namespaceName; IHttpRouteData data = request.GetRouteData(); IEnumerable<string> keys = _apiControllerCache.Value.ToDictionary(t => t.Key, t => t.Value, StringComparer.CurrentCultureIgnoreCase).Keys.ToList(); if (!data.Values.TryGetValue(NamespaceRouteVariableName, out namespaceName)) { return from k in keys where k.EndsWith($".{controllerName}{ControllerSuffix}", StringComparison.CurrentCultureIgnoreCase) select k; } return from n in (string[])namespaceName join k in keys on $"{n}.{controllerName}{ControllerSuffix}".ToLower() equals k.ToLower() select k; } public override HttpControllerDescriptor SelectController(HttpRequestMessage request) { Type type; if (request == null) { throw new ArgumentNullException(nameof(request)); } string controllerName = this.GetControllerName(request); if (string.IsNullOrEmpty(controllerName)) { throw new HttpResponseException(request.CreateErrorResponse(HttpStatusCode.NotFound, string.Format("No route providing a controller name was found to match request URI '{0}'", new object[] { request.RequestUri }))); } IEnumerable<string> fullNames = GetControllerFullName(request, controllerName); if (!fullNames.Any()) { throw new HttpResponseException(request.CreateErrorResponse(HttpStatusCode.NotFound, string.Format("No route providing a controller name was found to match request URI '{0}'", new object[] { request.RequestUri }))); } if (this._apiControllerCache.Value.TryGetValue(fullNames.First(), out type)) { return new HttpControllerDescriptor(_configuration, controllerName, type); } throw new HttpResponseException(request.CreateErrorResponse(HttpStatusCode.NotFound, string.Format("No route providing a controller name was found to match request URI '{0}'", new object[] { request.RequestUri }))); } }

public static void Register(HttpConfiguration config) { // Web API configuration and services // Web API routes config.MapHttpAttributeRoutes(); config.Services.Replace(typeof(IHttpControllerSelector), new NamespaceHttpControllerSelector(config)); config.Routes.MapHttpRoute( "AdminApi", "api/Admin/{controller}/{action}/{id}", new { action = "Index", id = RouteParameter.Optional, namespaceName = new[] { "RouteDemo.Areas.Admin.Controllers" } } ); config.Routes.MapHttpRoute( "SiteApi", "api/Site/{controller}/{action}/{id}", new { action = "Index", id = RouteParameter.Optional, namespaceName = new[] { "RouteDemo.Areas.Site.Controllers" } } ); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{area}/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); }