Обработка URL с помощью AppHarbor без изменения всех моих контроллеров - PullRequest
3 голосов
/ 19 августа 2011

Я пытаюсь разместить приложение MVC 3 (FunnelWeb) в AppHarbor.По причине, которая все еще не ясна для меня, когда мой маршрут - только Controller + Action (например, mysite / admin - это Admin + Index и mysite / login - это Admin + login), все работает нормально, но если у меня есть что-нибудь еще в маршруте(например, переменная типа {* page}) мой URL будет mysite: 12345 / mypage (где 12345 - это номер порта, назначенный AppHarbor, а mypage - это имя запрашиваемой страницы).Это приводит к сбою запроса, поскольку порт 12345 не является общедоступным.

AppHarbor использует балансировка нагрузки для распределения запроса между несколькими IIS.Это их способ работы, и именно поэтому внутренние запросы направляются на некоторые нестандартные порты.У меня нет проблем с этим, но у меня есть проблема с MVC, который пытается перенаправить меня на этот внутренний URL.

Я не указываю здесь пальцами;никто не виноват :) так что давайте перейдем к вопросу:

  1. Почему существует разница между запросом маршрута только с помощью Controller + Action и запросом маршрута с переменной типа {* page}?Будьте технически, пожалуйста:)
  2. Здесь - пример того, как обрабатывать запросы в AppHarbor, однако, кажется, что мне нужно изменить все мои контроллеры (OMG).Есть ли способ реализовать это без изменения моих контроллеров?
  3. Любые другие предложения приветствуются:)

Заранее спасибо.

ОБНОВЛЕНИЕ : По совпадению, поведение, которое я наблюдал, соответствует заключению, к которому я пришел.Однако проблема не имеет ничего общего с маршрутизацией ASP.Net MVC.Коротко говоря, FunnelWeb использует строчные URL-адреса, поэтому всякий раз, когда он получает запрос к ресурсу, он преобразует его в строчные, если необходимо, и выдает ответ 301.Проблема заключается в том, что при создании URL-адреса для ответа 301 URL-адрес запроса (абсолютный URL-адрес) теперь является URL-адресом, используемым при выполнении запроса от балансировщика нагрузки к IIS, а не запроса клиента;следовательно, запрос не выполняется.

Ответы [ 3 ]

3 голосов
/ 20 августа 2011

Это известная проблема с генерацией URL FunnelWeb в AppHarbor.При использовании стандартных методов MVC для генерации относительных URL-адресов это не проблема.AppHarbor содержит краткое руководство и пример того, как генерировать общедоступные URL в базе знаний.

2 голосов
/ 12 августа 2014

Возможно, теперь все, что вам нужно:

<appSettings>
  <!-- AppHarbor Setting to stop AppHb load balancer internal port numbers from showing up in URLs-->
  <add key="aspnet:UseHostHeaderForRequestUrl" value="true" />
</appSettings>

Это отмечено как обновление на странице поддержки AppHarbor по адресу http://support.appharbor.com/kb/getting-started/workaround-for-generating-absolute-urls-without-port-number

MSDN сообщает следующее about UseHostHeaderForRequestUrl :

aspnet: UseHostHeaderForRequestUrl - если этот атрибут значения равен false [по умолчанию], свойство Url динамически создается из хоста, порта и пути, предоставленных веб-сервером.Если этот атрибут значения имеет значение true, свойство Url динамически создается с использованием хоста и порта, предоставленных входящим заголовком «Host», и пути, предоставленного веб-сервером.

2 голосов
/ 20 августа 2011

Есть способ, но он требует пару классов.

Когда ASP.NET MVC регистрирует маршрут, он определяет обработчик маршрута.Этот обработчик маршрута возвращает обработчик HTTP, который обрабатывает запрос.Если вы используете настраиваемый обработчик маршрута, который возвращает настраиваемый обработчик HTTP, вы можете переписать контекст HTTP, используя пару классов декораторов.

Начните с создания HttpContextProxy и HttpRequestProxy, производных от базовых классов.и оборачивает все методы и свойства во внутренний экземпляр.Я сделал тяжелую работу доступной .

Затем создайте декораторы, сначала декоратор HTTP-контекста:

using System.Web;

public class HttpContextDecorator : HttpContextProxy
{
    public HttpContextDecorator(HttpContextBase innerHttpContext)
        : base(innerHttpContext)
    {
    }

    public override HttpRequestBase Request
    {
        get
        {
            return new HttpRequestDecorator(base.Request);
        }
    }
}

Декоратор HTTP-запроса:

using System;
using System.Web;

public class HttpRequestDecorator : HttpRequestProxy
{
    public HttpRequestDecorator(HttpRequestBase innerHttpRequest)
        : base(innerHttpRequest)
    {
    }

    public override bool IsSecureConnection
    {
        get
        {
            return string.Equals(Headers["X-Forwarded-Proto"], "https", StringComparison.OrdinalIgnoreCase);
        }
    }

    public override Uri Url
    {
        get
        {
            var url = base.Url;
            var urlBuilder = new UriBuilder(url);

            if (IsSecureConnection)
            {
                urlBuilder.Port = 443;
                urlBuilder.Scheme = "https";
            }
            else
            {
                urlBuilder.Port = 80;
            }

            return urlBuilder.Uri;
        }
    }

    public override string UserHostAddress
    {
        get
        {
            const string forwardedForHeader = "HTTP_X_FORWARDED_FOR";
            var forwardedFor = ServerVariables[forwardedForHeader];
            if (forwardedFor != null)
            {
                return forwardedFor;
            }

            return base.UserHostAddress;
        }
    }
}

Как уже упоминалось, вам также необходимо переопределить классы MVC - здесь обработчик HTTP:

using System;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

public class CustomMvcHandler : MvcHandler
{
    public CustomMvcHandler(RequestContext requestContext)
        : base(requestContext)
    {
        requestContext.HttpContext = new HttpContextDecorator(requestContext.HttpContext);
    }

    protected override IAsyncResult BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, object state)
    {
        httpContext = new HttpContextDecorator(httpContext);
        return base.BeginProcessRequest(httpContext, callback, state);
    }

    protected override void ProcessRequest(HttpContextBase httpContext)
    {
        httpContext = new HttpContextDecorator(httpContext);
        base.ProcessRequest(httpContext);
    }
}

Затем обработчик маршрута:

using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

public class CustomMvcRouteHandler : MvcRouteHandler
{
    protected override IHttpHandler GetHttpHandler(RequestContext requestContext)
    {
        return new CustomMvcHandler(requestContext);
    }
}

Наконец, вынеобходимо заменить связанный обработчик для всех зарегистрированных маршрутов (или правильно отобразить их с самого начала):

var routes = RouteTable.Routes.OfType<Route>().Where(x => x.RouteHandler is MvcRouteHandler);
foreach (var route in routes)
{
    route.RouteHandler = new CustomMvcRouteHandler();
}
...