MVC自定义路由子域 [英] MVC Custom Routing Subdomain

查看:135
本文介绍了MVC自定义路由子域的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图建立一个连接到一个MVC区一个租户子域的路线。在这种情况下,我有一个名为房客,它有两个控制器的区域;公共和Admin。我的自定义路由是用来抢子域,如果它,然后将它们路由匹配到合适的控制器,操作片区。

本项目的基地下面就
http://www.matrichard.com/post/asp .NET的MVC-5-路由与子域

我遇到的问题是在自定义子域路由。当我打的公共/指数路线,该是的RouteData返回null,我看到下面的错误。虽然如果路由/管理它返回正确的RouteData。


  

中的服务器错误'/'应用。


  
  

匹配的路线不包括控制器路线的价值,这是必需的。


这也似乎总是使用RouteDebugger工具的匹配,这是一个线索,我的问题?

例子路线:

控制器=公共行动=指数,面积=租客

http://tenant1.mydomain.com:8080/

http://tenant1.mydomain.com:8080/logon

控制器=管理行动=指数,面积=租客

http://tenant1.mydomain.com:8080/admin

http://tenant1.mydomain.com:8080/admin/edit

-

SubdomainRouteP.cs

 公共类SubdomainRouteP:路线
{
    公共字符串域{搞定;组; }    公共SubdomainRouteP(字符串域名,URL字符串,RouteValueDictionary默认):这个(域名,URL,默认值,新MvcRouteHandler())
    {
    }    公共SubdomainRouteP(字符串域名,URL字符串,对象默认):这个(域名,网址,新RouteValueDictionary(默认),新MvcRouteHandler())
    {
    }    公共SubdomainRouteP(字符串域名,URL字符串,对象违约,IRouteHandler routeHandler):这个(域名,网址,新RouteValueDictionary(默认),routeHandler)
    {
    }    公共SubdomainRouteP(字符串域名,URL字符串,RouteValueDictionary违约,IRouteHandler routeHandler):基地(URL,默认,routeHandler)
    {
        this.Domain =域;
    }    公众覆盖的RouteData GetRouteData(HttpContextBase的HttpContext)
    {
        //
        //的RouteData对象返回在某些情况下空
        //
        VAR的RouteData = base.GetRouteData(HttpContext的);        VAR子= httpContext.Request.Url.Host.Split('。')第一()。        字符串[] =黑名单{WWW,MYDOMAIN,本地主机};        //这会忽略任何不属于客户端租客preFIX
        如果(blacklist.Contains(子))
        {
            返回null; //继续下一个路线
        }        //这是为什么空?
        如果(的RouteData == NULL)
        {            的RouteData =新的RouteData(这一点,新MvcRouteHandler());        }        routeData.DataTokens [区] =房客;
        routeData.DataTokens [UseNamespaceFallback] = bool.FalseString;
        routeData.Values​​.Add(子域,子域);        //重要提示:始终返回null,如果没有匹配。
        //这告诉.NET路由检查登记的下一个路由。
        返回的RouteData;
    }}

RouteConfig.cs

  routes.Add(Admin_Subdomain,新SubdomainRouteP(
            {}客户.mydomain.com来,//当然这应该重新present的真正意图......就像我说的在本地IIS暴殄天物示范项目
            管理/ {}动作/(编号),
            新{控制器=管理,行动=索引,ID = UrlParameter.Optional}));        routes.Add(Public_Subdomain,新SubdomainRouteP(
            {}客户.mydomain.com来,//当然这应该重新present的真正意图......就像我说的在本地IIS暴殄天物示范项目
            {控制器} / {行动} / {ID}
            新{控制器=公共,行动=索引,ID = UrlParameter.Optional}));        //这是MVC默认路由
        routes.MapRoute(
            默认,
            {控制器} / {行动} / {ID}
            新{控制器=家,行动=索引,ID = UrlParameter.Optional});

以下链接给我从RouteDebugger以下结果。在测试1和2的路径仍然匹配/管理。

失败测试1: http://tenant.mydomain.com/

失败测试2: http://tenant.mydomain.com/logon

3成功: http://tenant.mydomain.com/admin


  

匹配地址默认


  
  

系统管理员/ {行动} / {ID}控制器=管理员,行动=指数


  
  

{控制器} / {行动} / {ID}控制器=公用,行动=指数



解决方案

这是您链接到该职位有一个bug:当限制或URL不匹配, base.GetRouteData 方法将返回。在这种情况下,添加的子域名来路由字典将明显抛出异常。应该有在该行之前的空后卫条款。

 公众覆盖的RouteData GetRouteData(HttpContextBase的HttpContext)
{
    VAR的RouteData = base.GetRouteData(HttpContext的);
    如果(的RouteData!= NULL)
    {
        routeData.Values​​.Add(客户,httpContext.Request.Url.Host.Split()一()'。');
    }
    返回的RouteData;
}

由于应与路线的情况。你需要确保你的情况下返回null,其中的基类返回null(表示URL或约束不匹配,我们需要跳过处理这条路线)。

另外,我不知道,如果它使不是直接添加数据到 DataTokens ,但MVC框架有一个 IRouteWithArea <任何区别/ code>可以实现路由的配置方法适用于该地区。

 公共类SubdomainRouteP:路线,IRouteWithArea
{
    公共字符串面积{搞定;私人集; }    公共SubdomainRouteP(串区,字符串URL,RouteValueDictionary默认):这个(区,网址,默认值,新MvcRouteHandler())
    {
    }    公共SubdomainRouteP(串区,字符串的URL,对象默认):这个(区,网址,新RouteValueDictionary(默认),新MvcRouteHandler())
    {
    }    公共SubdomainRouteP(串区,字符串的URL,对象违约,IRouteHandler routeHandler):这个(区,网址,新RouteValueDictionary(默认),routeHandler)
    {
    }    公共SubdomainRouteP(串区,字符串URL,RouteValueDictionary违约,IRouteHandler routeHandler):基地(URL,默认,routeHandler)
    {
        this.Area =区域;
    }    公众覆盖的RouteData GetRouteData(HttpContextBase的HttpContext)
    {
        VAR的RouteData = base.GetRouteData(HttpContext的);        //这将忽略任何其中的URL或约束不匹配
        //在调用base.GetRouteData()。
        如果(的RouteData!= NULL)
        {
            VAR子= httpContext.Request.Url.Host.Split('。')第一()。            字符串[] =黑名单{WWW,MYDOMAIN,本地主机};            //这会忽略任何不属于客户端租客preFIX
            如果(blacklist.Contains(子))
            {
                返回null; //继续下一个路线
            }            routeData.DataTokens [UseNamespaceFallback] = bool.FalseString;
            routeData.Values​​.Add(子域,子域);
        }        //重要提示:始终返回null,如果没有匹配。
        //这告诉.NET路由检查登记的下一个路由。
        返回的RouteData;
    }}

我无法弄清楚你正在尝试用参数来完成。该URL将最有可能回归的的东西的域。因此,它好像你应该​​在第一个{控制器} / {行动} / {ID}路线或你将永远不会有一个案例,将通过约束通过对缺省路由。或者,你可以使用一个明确的段中的网址,以便您能够区分它(你与你的管理途径也以同样的方式)。

  routes.Add(Admin_Subdomain,新SubdomainRouteP(
    承租人,
    管理/ {}动作/(编号),
    新{控制器=管理,行动=索引,ID = UrlParameter.Optional}));routes.Add(Public_Subdomain,新SubdomainRouteP(
    承租人,
    公共/ {}动作/(编号),
    新{控制器=公共,行动=索引,ID = UrlParameter.Optional}));//这是MVC默认路由
routes.MapRoute(
    默认,
    {控制器} / {行动} / {ID}
    新{控制器=家,行动=索引,ID = UrlParameter.Optional});

另一种选择是添加另一个构造函数的参数在有效域的一个明确的列表传递给核对。

I'm trying to build a "Tenant" Subdomain route that attaches to a MVC Area. In this case I have an Area called "Tenant" which has two controllers; Public and Admin. My custom Route is used to grab the Subdomain if it matches then route them to the proper Controller-Action-Area.

The base of this project came from the following http://www.matrichard.com/post/asp.net-mvc-5-routing-with-subdomain

The problem I'm having is in the custom Subdomain Route. When I hit the Public/Index Route, the routeData is returning null and I see the following error. Although if the route is /admin it returns the correct routeData.

Server Error in '/' Application.

The matched route does not include a 'controller' route value, which is required.

It also seems to be always matching using RouteDebugger tool, is this a clue to my problem?

Examples Routes:

controller=Public action=Index, area=Tenant

http://tenant1.mydomain.com:8080/

http://tenant1.mydomain.com:8080/logon

controller=Admin action=Index, area=Tenant

http://tenant1.mydomain.com:8080/admin

http://tenant1.mydomain.com:8080/admin/edit

--

SubdomainRouteP.cs

public class SubdomainRouteP : Route
{
    public string Domain { get; set; }

    public SubdomainRouteP(string domain, string url, RouteValueDictionary defaults): this(domain, url, defaults, new MvcRouteHandler())
    {
    }

    public SubdomainRouteP(string domain, string url, object defaults): this(domain, url, new RouteValueDictionary(defaults), new MvcRouteHandler())
    {
    }

    public SubdomainRouteP(string domain, string url, object defaults, IRouteHandler routeHandler): this(domain, url, new RouteValueDictionary(defaults), routeHandler)
    {
    }

    public SubdomainRouteP(string domain, string url, RouteValueDictionary defaults, IRouteHandler routeHandler): base(url, defaults, routeHandler)
    {
        this.Domain = domain;
    }

    public override RouteData GetRouteData(HttpContextBase httpContext)
    {
        // 
        // routeData object returns null in some cases 
        // 
        var routeData = base.GetRouteData(httpContext);

        var subdomain = httpContext.Request.Url.Host.Split('.').First();

        string[] blacklist = { "www", "mydomain", "localhost" };

        // This will ignore anything that is not a client tenant prefix
        if (blacklist.Contains(subdomain))
        {
            return null; // Continue to the next route
        }

        // Why is this NULL?
        if (routeData == null)
        {

            routeData = new RouteData(this, new MvcRouteHandler());

        }

        routeData.DataTokens["Area"] = "Tenant";
        routeData.DataTokens["UseNamespaceFallback"] = bool.FalseString;
        routeData.Values.Add("subdomain", subdomain);

        // IMPORTANT: Always return null if there is no match.
        // This tells .NET routing to check the next route that is registered.
        return routeData;
    }

}

RouteConfig.cs

        routes.Add("Admin_Subdomain", new SubdomainRouteP(
            "{client}.mydomain.com", //of course this should represent the real intent…like I said throwaway demo project in local IIS
            "admin/{action}/{id}",
            new { controller = "Admin", action = "Index", id = UrlParameter.Optional }));

        routes.Add("Public_Subdomain", new SubdomainRouteP(
            "{client}.mydomain.com", //of course this should represent the real intent…like I said throwaway demo project in local IIS
            "{controller}/{action}/{id}",
            new { controller = "Public", action = "Index", id = UrlParameter.Optional }));

        // This is the MVC default Route
        routes.MapRoute(
            "Default",
            "{controller}/{action}/{id}",
            new { controller = "Home", action = "Index", id = UrlParameter.Optional });

The Url below gives me the following results from RouteDebugger. During test 1 and 2 the route still matches /admin.

Failed Test 1: http://tenant.mydomain.com/

Failed Test 2: http://tenant.mydomain.com/logon

Successful 3: http://tenant.mydomain.com/admin

Matches Url Defaults

True admin/{action}/{id} controller = Admin, action = Index

True {controller}/{action}/{id} controller = Public, action = Index

解决方案

The post that you linked to has a bug: When a constraint or the URL does not match, the base.GetRouteData method will return null. In this case, adding the subdomain name to the route dictionary will obviously throw an exception. There should be a null guard clause before that line.

public override RouteData GetRouteData(HttpContextBase httpContext)
{
    var routeData = base.GetRouteData(httpContext);
    if (routeData != null)
    {
        routeData.Values.Add("client", httpContext.Request.Url.Host.Split('.').First());
    }
    return routeData;
}

As should be the case with your route. You need to ensure you return null in the case where the base class returns null (which indicates either the URL or a constraint did not match, and we need to skip processing this route).

Also, I am not sure if it makes any difference than adding the data directly to the DataTokens, but the MVC framework has an IRouteWithArea that can be implemented to configure the Area the route applies to.

public class SubdomainRouteP : Route, IRouteWithArea
{
    public string Area { get; private set; }

    public SubdomainRouteP(string area, string url, RouteValueDictionary defaults): this(area, url, defaults, new MvcRouteHandler())
    {
    }

    public SubdomainRouteP(string area, string url, object defaults): this(area, url, new RouteValueDictionary(defaults), new MvcRouteHandler())
    {
    }

    public SubdomainRouteP(string area, string url, object defaults, IRouteHandler routeHandler): this(area, url, new RouteValueDictionary(defaults), routeHandler)
    {
    }

    public SubdomainRouteP(string area, string url, RouteValueDictionary defaults, IRouteHandler routeHandler): base(url, defaults, routeHandler)
    {
        this.Area = area;
    }

    public override RouteData GetRouteData(HttpContextBase httpContext)
    {
        var routeData = base.GetRouteData(httpContext);

        // This will ignore anything where the URL or a constraint doesn't match
        // in the call to base.GetRouteData().
        if (routeData != null)
        {
            var subdomain = httpContext.Request.Url.Host.Split('.').First();

            string[] blacklist = { "www", "mydomain", "localhost" };

            // This will ignore anything that is not a client tenant prefix
            if (blacklist.Contains(subdomain))
            {
                return null; // Continue to the next route
            }

            routeData.DataTokens["UseNamespaceFallback"] = bool.FalseString;
            routeData.Values.Add("subdomain", subdomain);
        }

        // IMPORTANT: Always return null if there is no match.
        // This tells .NET routing to check the next route that is registered.
        return routeData;
    }

}

I can't figure out what you are trying to do with the domain parameter. The URL will most likely return something for domain. So, it seems like you should have a constraint in the first "{controller}/{action}/{id}" route or you will never have a case that will pass through to the default route. Or, you could use an explicit segment in the URL so you can differentiate it (the same way you did with your admin route).

routes.Add("Admin_Subdomain", new SubdomainRouteP(
    "Tenant",
    "admin/{action}/{id}",
    new { controller = "Admin", action = "Index", id = UrlParameter.Optional }));

routes.Add("Public_Subdomain", new SubdomainRouteP(
    "Tenant",
    "public/{action}/{id}",
    new { controller = "Public", action = "Index", id = UrlParameter.Optional }));

// This is the MVC default Route
routes.MapRoute(
    "Default",
    "{controller}/{action}/{id}",
    new { controller = "Home", action = "Index", id = UrlParameter.Optional });

Another option would be to add another constructor parameter to pass in an explicit list of valid domains to check against.

这篇关于MVC自定义路由子域的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆