Субдомен MVC Custom Routing

Я пытаюсь построить маршрут субдомена "Арендатор", который присоединяется к области MVC. В этом случае у меня есть зона под названием "Арендатор", в которой есть два контроллера; Public и Admin. Мой пользовательский маршрут используется для захвата субдомена, если он совпадает, а затем направляет их в соответствующую область действий контроллера.

Основой этого проекта послужило следующее http://www.matrichard.com/post/asp.net-mvc-5-routing-with-subdomain

Проблема у меня в пользовательском маршруте субдомена. Когда я ударил Public/Index Маршрут routeData возвращает ноль, и я вижу следующую ошибку. Хотя, если маршрут /admin он возвращает правильный 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

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 });

URL ниже дает мне следующие результаты от RouteDebugger. Во время теста 1 и 2 маршрут все еще совпадает /admin.

Неудачный тест 1: http://tenant.mydomain.com/

Неудачный тест 2: http://tenant.mydomain.com/logon

Успешный 3: http://tenant.mydomain.com/admin

Соответствует Url по умолчанию

Правда admin/{action}/{id}controller = Admin, action = Index

Правда {controller}/{action}/{id}controller = Public, action = Index

1 ответ

Решение

Сообщение, на которое вы ссылались, содержит ошибку: если ограничение или URL не совпадают, base.GetRouteData метод вернется null, В этом случае добавление имени субдомена в словарь маршрутизации, очевидно, вызовет исключение. Перед этой строкой должно быть нулевое защитное предложение.

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;
}

Как и должно быть в случае с вашим маршрутом. Необходимо убедиться, что вы возвращаете null в случае, когда базовый класс возвращает null (что указывает на то, что URL-адрес или ограничение не совпадают, и нам нужно пропустить обработку этого маршрута).

Кроме того, я не уверен, если это имеет какое-либо значение, чем добавление данных непосредственно в DataTokens, но у платформы MVC есть IRouteWithArea это может быть реализовано для настройки Района, к которому применяется маршрут.

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;
    }

}

Я не могу понять, что вы пытаетесь сделать с domain параметр. URL, скорее всего, вернет что-то для домена. Итак, похоже, что вы должны иметь ограничение в первом "{controller}/{action}/{id}" маршрут или у вас никогда не будет случая, который будет проходить по маршруту по умолчанию. Или вы можете использовать явный сегмент в URL, чтобы вы могли различать его (так же, как вы делали это с вашим маршрутом администратора).

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 });

Другой вариант - добавить еще один параметр конструктора, чтобы передать явный список допустимых доменов для проверки.

Другие вопросы по тегам