【问题标题】:How do I get Url.Action to use the right port number?如何让 Url.Action 使用正确的端口号?
【发布时间】:2011-12-09 09:39:09
【问题描述】:

我正在使用 MVC3 创建一个网站,我正在使用 razor 语法来创建视图,并且它都在 azure 下运行。

目前我在本地的天蓝色模拟器下运行。

我在 url 上有一个视图:'http://localhost:81/Blah/Foo'。

在那个视图中,我想获取另一个操作的 URL。

为此,我使用:Url.Action("SomeAction", "SomeController", null, this.Request.Url.Scheme)

但是,由于负载平衡,Azure 模拟器会根据端口号进行更改。

即虽然它在端口 81 上运行,但请求可能来自端口 82。

这会导致创建一个不正确的 url 'http://localhost:82/Blah/Bar' 并且我得到一个 400, bad hostname 错误。

按照这篇文章http://social.msdn.microsoft.com/Forums/en-US/windowsazure/thread/9142db8d-0f85-47a2-91f7-418bb5a0c675/ 中的信息,我发现我可以使用 HttpContext.Request.Headers["Host"] 获取正确的主机和端口号。

但我只能将主机名传递给 Url.Action,如果我尝试传递主机名和端口,那么它仍然会附加它认为正确的端口,所以我最终得到 localhost:81:82。

编辑:我发现有人有同样的问题。他们似乎收集了与我相同的信息(除了他们也包含了复制品),但他们没有有用的修复,因为我无法手动指定端口号。

http://social.msdn.microsoft.com/Forums/en-US/windowsazuredevelopment/thread/87c729e8-094c-4578-b9d1-9c8ff7311577/

我想一个解决方法是让我自己的 Url.Action 重载,让我指定端口。

【问题讨论】:

  • 很好奇,但您正在使用负载平衡,所以您试图强制所有用户连接到他们开始使用的同一台机器?
  • 天蓝色的模拟器是什么弄乱了端口号。如果它允许我继续在本地进行调试,我很乐意禁用它:) 问题的核心是天蓝色模拟器使用不同的端口,我从端口 81 开始工作,然后切换到端口82 并且这给了我一个 400 bad host http 错误。
  • @BuildStarted - 你在这里提出了一个很好的观点。我可能以错误的方式看待这个问题。问题不在于它使用了错误的端口号,问题在于虽然 Azure 模拟器在端口 81 和 82 之间进行负载平衡,但 mvc 应用程序(在 IIS 上运行,在 azure 模拟器后面)只接受端口 81。我应该试图找出如何纠正它。
  • 有趣的是,我们的负载均衡器的所有机器都在相同的端口上,但由于它们是不同的机器,所以没有冲突。这听起来像是 Azure 模拟器的限制,从技术角度来看是有道理的。

标签: asp.net asp.net-mvc-3 azure


【解决方案1】:

对于真正需要绝对路径并支持负载平衡系统的每个来到这里的人来说,这是我想出的:

//http://stackoverflow.com/questions/126242/how-do-i-turn-a-relative-url-into-a-full-url
public static string AbsoluteAction(this UrlHelper url, string actionName, string controllerName, object routeValues = null)
{
  Uri publicFacingUrl = GetPublicFacingUrl(url.RequestContext.HttpContext.Request, url.RequestContext.HttpContext.Request.ServerVariables);
  string relAction = url.Action(actionName, controllerName, routeValues);
  //this will always have a / in front of it.
  var newPort = publicFacingUrl.Port == 80 || publicFacingUrl.Port == 443 ? "" : ":"+publicFacingUrl.Port.ToString();
  return publicFacingUrl.Scheme + Uri.SchemeDelimiter + publicFacingUrl.Host + newPort + relAction;
}

然后,从https://github.com/aarnott/dotnetopenid/blob/v3.4/src/DotNetOpenAuth/Messaging/HttpRequestInfo.cshttp://go4answers.webhost4life.com/Example/azure-messing-port-numbers-creates-28516.aspx

   /// <summary>
    /// Gets the public facing URL for the given incoming HTTP request.
    /// </summary>
    /// <param name="request">The request.</param>
    /// <param name="serverVariables">The server variables to consider part of the request.</param>
    /// <returns>
    /// The URI that the outside world used to create this request.
    /// </returns>
    /// <remarks>
    /// Although the <paramref name="serverVariables"/> value can be obtained from
    /// <see cref="HttpRequest.ServerVariables"/>, it's useful to be able to pass them
    /// in so we can simulate injected values from our unit tests since the actual property
    /// is a read-only kind of <see cref="NameValueCollection"/>.
    /// </remarks>
internal static Uri GetPublicFacingUrl(HttpRequestBase request, NameValueCollection serverVariables)
{
  //Contract.Requires<ArgumentNullException>(request != null);
  //Contract.Requires<ArgumentNullException>(serverVariables != null);

  // Due to URL rewriting, cloud computing (i.e. Azure)
  // and web farms, etc., we have to be VERY careful about what
  // we consider the incoming URL.  We want to see the URL as it would
  // appear on the public-facing side of the hosting web site.
  // HttpRequest.Url gives us the internal URL in a cloud environment,
  // So we use a variable that (at least from what I can tell) gives us
  // the public URL:
  if (serverVariables["HTTP_HOST"] != null)
  {
    //ErrorUtilities.VerifySupported(request.Url.Scheme == Uri.UriSchemeHttps || request.Url.Scheme == Uri.UriSchemeHttp, "Only HTTP and HTTPS are supported protocols.");
    string scheme = serverVariables["HTTP_X_FORWARDED_PROTO"] ?? request.Url.Scheme;
    Uri hostAndPort = new Uri(scheme + Uri.SchemeDelimiter + serverVariables["HTTP_HOST"]);
    UriBuilder publicRequestUri = new UriBuilder(request.Url);
    publicRequestUri.Scheme = scheme;
    publicRequestUri.Host = hostAndPort.Host;
    publicRequestUri.Port = hostAndPort.Port; // CC missing Uri.Port contract that's on UriBuilder.Port
    return publicRequestUri.Uri;
  }
  // Failover to the method that works for non-web farm enviroments.
  // We use Request.Url for the full path to the server, and modify it
  // with Request.RawUrl to capture both the cookieless session "directory" if it exists
  // and the original path in case URL rewriting is going on.  We don't want to be
  // fooled by URL rewriting because we're comparing the actual URL with what's in
  // the return_to parameter in some cases.
  // Response.ApplyAppPathModifier(builder.Path) would have worked for the cookieless
  // session, but not the URL rewriting problem.
  return new Uri(request.Url, request.RawUrl);
}

【讨论】:

  • 我发现的最全面的解决方案。谢谢!
【解决方案2】:

如果只使用 Url.Action("Action", "Controller") 会发生什么?这应该只生成一个相对 URL,它应该可以工作。

(或者更好的问题是:你为什么不使用那个重载?)

【讨论】:

  • 过去我们似乎在生成 http 和 https 链接时遇到了麻烦,这会导致显式使用协议参数。但是在这种情况下,只生成相对链接正是我想要的,我相信它会解决所有问题。非常感谢!
【解决方案3】:

我发现这对我有用...

var request = HttpContext.Request;
string url = request.Url.Scheme + "://" +
             request.UserHostAddress +  ":" +
             request.Url.Port;

【讨论】:

  • 为此目的使用引荐来源网址是非常糟糕的主意。使用当前网址,而不是引荐网址。 Referrer是以前的url,可以为null。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-01-28
  • 2011-05-24
  • 2011-08-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多