c# - ASP.NET MVC 中继承的 RoutePrefixAttribute

标签 c# asp.net-mvc routes asp.net-mvc-routing attributerouting

目前,我的 ASP.NET MVC 网站使用 RouteConfig.RegisterRoutes 来映射路由。我想将其转换为新的属性路由系统,但我遗漏了一件小事。

该网站是多语言的,因此每个 URL 都需要以两个字母的 ISO 语言代码作为前缀。 在当前的实现中,该网站使用自定义 RouteHandler 来处理所有路由的文化部分。注册所有路由后,循环将自定义处理程序和文化约束添加到每个路由。

请参阅下面的当前实现:

public class RouteConfig
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.RouteExistingFiles = true; 

        // various calls to routes.MapRoute()

        foreach (Route r in routes)
        {
            if (!(r.RouteHandler is SingleCultureMvcRouteHandler))
            {
                r.RouteHandler = new MultiCultureMvcRouteHandler();
                r.Url = "{culture}/" + r.Url;

                // Add the default language
                if (r.Defaults == null)
                    r.Defaults = new RouteValueDictionary();
                r.Defaults.Add("culture", Culture.nl.ToString());

                // Add language constraints
                if (r.Constraints == null)
                    r.Constraints = new RouteValueDictionary();
                r.Constraints.Add("culture", new CultureConstraint(Culture.nl.ToString(), Culture.de.ToString(), Culture.en.ToString(), Culture.fr.ToString()));
            }
        }
    }
}

文化枚举:

public enum Culture
{
    nl = 1,
    en = 2,
    de = 3,
    fr = 4
}

自定义路由处理器:

public class SingleCultureMvcRouteHandler : MvcRouteHandler { }

public class MultiCultureMvcRouteHandler : MvcRouteHandler
{
    protected override IHttpHandler GetHttpHandler(RequestContext requestContext)
    {
        var culture = requestContext.RouteData.Values["culture"].ToString();
        var ci = new CultureInfo(culture);
        Thread.CurrentThread.CurrentUICulture = ci;
        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("nl-NL");

        return base.GetHttpHandler(requestContext);
    }
}

文化限制

public class CultureConstraint : IRouteConstraint
{
    private string[] _values;
    public CultureConstraint(params string[] values)
    {
        this._values = values;
    }

    public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
    {
        string value = values[parameterName].ToString();
        return _values.Contains(value);
    }
}

为了将其转换为新的 AttributeRouting,我只是想向我的基本 Controller 添加一个 RoutePrefix 属性,它会处理文化部分。 但是 似乎不支持继承路由属性...

release notes ASP.NET MVC 5.2 声明,通过实现自定义 RouteAttributeDirectRouteProvider,该版本支持继承的路由属性。然而,这是针对 RouteAttribute,而不是针对 RoutePrefixAttribute

我尝试过的:

我尝试通过从 RoutePrefixAttribute 继承来创建我自己的 InheritedRoutePrefixAttribute,但是为了真正支持继承的路由,我仍然需要创建一个 InheritedDirectRouteProvider 并覆盖 GetControllerRouteFactories() 方法,该方法返回 IDirectRouteFactory 的集合。
所以为了做到这一点,我的 InheritedRoutePrefixAttribute 需要实现 IDirectRouteFactory 接口(interface),但是在 CreateRoute() 方法中做什么呢?

到目前为止我的代码:

[AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)]
public class InheritedRoutePrefixAttribute : RoutePrefixAttribute, IDirectRouteFactory
{
    public RouteEntry CreateRoute(DirectRouteFactoryContext context)
    {
        // ...
    }
}

自定义路由提供者

// Custom direct route provider which supports attribute route inheritance.
public class InheritedDirectRouteProvider : DefaultDirectRouteProvider
{
    protected override IReadOnlyList<IDirectRouteFactory> GetControllerRouteFactories(ControllerDescriptor controllerDescriptor)
    {
        return controllerDescriptor.GetCustomAttributes(typeof(InheritedRoutePrefixAttribute), inherit: true).Cast<IDirectRouteFactory>().ToArray();
    }
}

最佳答案

我目前正在从事一个 ASP.NET Web Api 项目,并且一直在研究属性路由(我认为这与 MVC 路由类似)。我想为我的 Controller 定义一个全局路由前缀。我为我的 Controller 创建了一个基类,我用 RoutePrefixAttribute 对其进行了修饰。

想法是在 Controller 基类中定义全局路由前缀,但也允许从它继承的 Controller 拥有自己的路由前缀。

例如:

[RoutePrefix("api")]
public abstract class BaseController : ApiController
{
}

[RoutePrefix("values")]
public class ValuesController : BaseController
{
    [Route("{id}")]
    public int Get(int id)
    {
        return id;
    }
}

调用 Get 的路由将是:http://{base}:{port}/api/values/{id}

为此,我还继承自 DefaultDirectRouteProvider 类,但我重写了 GetRoutePrefix 方法:

public class InheritedRoutePrefixDirectRouteProvider : DefaultDirectRouteProvider
{
    protected override string GetRoutePrefix(HttpControllerDescriptor controllerDescriptor)
    {
        var sb = new StringBuilder(base.GetRoutePrefix(controllerDescriptor));
        var baseType = controllerDescriptor.ControllerType.BaseType;

        for (var t = baseType; typeof(ApiController).IsAssignableFrom(t); t = t.BaseType)
        {
            var a = (t as MemberInfo).GetCustomAttribute<RoutePrefixAttribute>(false);
            if (a != null)
            {
                sb.Insert(0, $"{a.Prefix}{(sb.Length > 0 ? "/": "")}");
            }
        }

        return sb.ToString();
    }
}

所以它所做的是在 Controller 继承链中将路由前缀链接在一起。

关于c# - ASP.NET MVC 中继承的 RoutePrefixAttribute,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30071772/

相关文章:

c# - 如何查找使用设备的所有进程

c# - Web API - 复杂类型枚举的表单编码输入允许无效值

java - 解码 IBM/360 列二进制格式的十六进制数

c# - 访问从 Controller 传递过来的数据

C# Web 应用程序 -> 代理所有请求 -> 从另一个 Web 应用程序返回内容(反向代理)

c# - 从 ASP.NET MVC Web 应用程序访问 android Activity

javascript - 在模块之间注入(inject)路由配置

c# 优先考虑扩展方法(仅限)

algorithm - 两个顶点之间具有平行边的 Dijkstra

c# - MVC3 路由问题