gpt4 book ai didi

c# - 为什么我的区域特定 Web API 可以从所有其他区域访问?

转载 作者:太空狗 更新时间:2023-10-29 17:36:00 25 4
gpt4 key购买 nike

我目前正在开发一个 ASP.NET MVC 4 Web 应用程序项目,该项目必须遵循以下设计决策:

  • 主要的 MVC 应用程序驻留在解决方案的根目录中。
  • 所有管理员功能都位于一个单独的区域。
  • 每个外部方(例如供应商)都有自己的区域。
  • 每个区域,包括根,都构成了一个分离良好的区域功能 block 。来自一个区域的功能可能不会暴露给另一个区域。这是为了防止未经授权访问数据。
  • 每个区域,包括根区域,都有自己的 RESTfull API(Web API)。

所有区域中的所有正常 Controller ,包括根 Controller ,都按预期工作。但是,我的某些 Web API Controller 表现出意外行为。例如,有两个名称相同但位于不同区域的 Web API Controller 会产生以下异常:

Multiple types were found that match the controller named ‘clients’.This can happen if the route that services this request(‘api/{controller}/{id}’) found multiple controllers defined with thesame name but differing namespaces, which is not supported.

The request for ‘clients’ has found the following matching controllers:MvcApplication.Areas.Administration.Controllers.Api.ClientsControllerMvcApplication.Controllers.Api.ClientsController

这看起来很奇怪,因为我有不同的路线应该将两者分开。这是我的管理部分的 AreaRegistration:

public class AdministrationAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "Administration";
}
}

public override void RegisterArea(AreaRegistrationContext context)
{
context.Routes.MapHttpRoute(
name: "Administration_DefaultApi",
routeTemplate: "Administration/api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);

context.MapRoute(
"Administration_default",
"Administration/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
}
}

此外,我注意到我可以访问区域特定的 Web API,同时从调用中省略区域名称。

这是怎么回事?如何让我的 Web API Controller 像普通的 ASP.NET MVC Controller 一样运行?

最佳答案

ASP.NET MVC 4 does not support the partitioning of Web API controllers across Areas.

您可以将 WebApi Controller 放在不同区域的不同 Api 文件夹中,但 ASP.NET MVC 会将它们视为都在同一位置。

幸运的是,您可以通过重写 ASP.NET MVC 基础结构的一部分来克服此限制。有关限制和解决方案的更多信息,请阅读我的博客文章“ASP.NET MVC 4 RC: Getting WebApi and Areas to play nicely” '.如果您只对解决方案感兴趣,请继续阅读:

第 1 步。让您的路线区域感知

将以下扩展方法添加到您的 ASP.NET MVC 应用程序并确保它们可以从您的 AreaRegistration 类访问:

public static class AreaRegistrationContextExtensions
{
public static Route MapHttpRoute(this AreaRegistrationContext context, string name, string routeTemplate)
{
return context.MapHttpRoute(name, routeTemplate, null, null);
}

public static Route MapHttpRoute(this AreaRegistrationContext context, string name, string routeTemplate, object defaults)
{
return context.MapHttpRoute(name, routeTemplate, defaults, null);
}

public static Route MapHttpRoute(this AreaRegistrationContext context, string name, string routeTemplate, object defaults, object constraints)
{
var route = context.Routes.MapHttpRoute(name, routeTemplate, defaults, constraints);
if (route.DataTokens == null)
{
route.DataTokens = new RouteValueDictionary();
}
route.DataTokens.Add("area", context.AreaName);
return route;
}
}

要使用新的扩展方法,请从调用链中删除 Routes 属性:

context.MapHttpRoute( /* <-- .Routes removed */
name: "Administration_DefaultApi",
routeTemplate: "Administration/api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);

第 2 步。使 Web API Controller 选择器区域感知

将以下类添加到您的 ASP.NET MVC 应用程序并确保它可以从 Global.asax 访问

namespace MvcApplication.Infrastructure.Dispatcher
{
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Dispatcher;

public class AreaHttpControllerSelector : DefaultHttpControllerSelector
{
private const string AreaRouteVariableName = "area";

private readonly HttpConfiguration _configuration;
private readonly Lazy<ConcurrentDictionary<string, Type>> _apiControllerTypes;

public AreaHttpControllerSelector(HttpConfiguration configuration)
: base(configuration)
{
_configuration = configuration;
_apiControllerTypes = new Lazy<ConcurrentDictionary<string, Type>>(GetControllerTypes);
}

public override HttpControllerDescriptor SelectController(HttpRequestMessage request)
{
return this.GetApiController(request);
}

private static string GetAreaName(HttpRequestMessage request)
{
var data = request.GetRouteData();
if (data.Route.DataTokens == null)
{
return null;
}
else
{
object areaName;
return data.Route.DataTokens.TryGetValue(AreaRouteVariableName, out areaName) ? areaName.ToString() : null;
}
}

private static ConcurrentDictionary<string, Type> GetControllerTypes()
{
var assemblies = AppDomain.CurrentDomain.GetAssemblies();

var types = assemblies
.SelectMany(a => a
.GetTypes().Where(t =>
!t.IsAbstract &&
t.Name.EndsWith(ControllerSuffix, StringComparison.OrdinalIgnoreCase) &&
typeof(IHttpController).IsAssignableFrom(t)))
.ToDictionary(t => t.FullName, t => t);

return new ConcurrentDictionary<string, Type>(types);
}

private HttpControllerDescriptor GetApiController(HttpRequestMessage request)
{
var areaName = GetAreaName(request);
var controllerName = GetControllerName(request);
var type = GetControllerType(areaName, controllerName);

return new HttpControllerDescriptor(_configuration, controllerName, type);
}

private Type GetControllerType(string areaName, string controllerName)
{
var query = _apiControllerTypes.Value.AsEnumerable();

if (string.IsNullOrEmpty(areaName))
{
query = query.WithoutAreaName();
}
else
{
query = query.ByAreaName(areaName);
}

return query
.ByControllerName(controllerName)
.Select(x => x.Value)
.Single();
}
}

public static class ControllerTypeSpecifications
{
public static IEnumerable<KeyValuePair<string, Type>> ByAreaName(this IEnumerable<KeyValuePair<string, Type>> query, string areaName)
{
var areaNameToFind = string.Format(CultureInfo.InvariantCulture, ".{0}.", areaName);

return query.Where(x => x.Key.IndexOf(areaNameToFind, StringComparison.OrdinalIgnoreCase) != -1);
}

public static IEnumerable<KeyValuePair<string, Type>> WithoutAreaName(this IEnumerable<KeyValuePair<string, Type>> query)
{
return query.Where(x => x.Key.IndexOf(".areas.", StringComparison.OrdinalIgnoreCase) == -1);
}

public static IEnumerable<KeyValuePair<string, Type>> ByControllerName(this IEnumerable<KeyValuePair<string, Type>> query, string controllerName)
{
var controllerNameToFind = string.Format(CultureInfo.InvariantCulture, ".{0}{1}", controllerName, AreaHttpControllerSelector.ControllerSuffix);

return query.Where(x => x.Key.EndsWith(controllerNameToFind, StringComparison.OrdinalIgnoreCase));
}
}
}

通过将以下行添加到 Global.asax 中的 Application_Start 方法来覆盖 DefaultHttpControllerSelector

GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerSelector), new AreaHttpControllerSelector(GlobalConfiguration.Configuration));

恭喜,您的 Web API Controller 现在将像您的普通 MVC Controller 一样遵守您所在区域的规则!

更新:2012 年 9 月 6 日

一些开发人员就他们遇到的场景联系了我,其中路由变量的 DataTokens 属性为 null。我的实现假定 DataTokens 属性始终已初始化,并且如果此属性为 null 则将无法正常运行。此行为很可能是由 ASP.NET MVC 框架最近的更改引起的,实际上可能是框架中的错误。我已经更新了我的代码来处理这种情况。

关于c# - 为什么我的区域特定 Web API 可以从所有其他区域访问?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12025347/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com