我正在尝试在 AppHarbor 上托管 MVC 3 应用程序 (FunnelWeb)。由于我仍然不清楚的原因,当我的路线只是控制器+操作时(例如,mysite/admin 是 Admin+Index,mysite/login 是 Admin+login),一切正常,但如果我的路线中有其他内容(例如像{*page}这样的变量)我的URL将是mysite:12345/mypage(其中12345是AppHarbor分配的端口号,mypage是我请求的页面的名称)。这使得请求失败,因为端口 12345 没有公开暴露。
AppHarbor 使用负载均衡在之间分配请求多个 IIS。这是他们做事的方式,这就是为什么在内部请求被路由到一些非标准端口。我对此没有任何问题,但我对尝试将我路由到该内部 URL 的 MVC 有问题。
我不是在这里指手画脚;这不是任何人的错:)所以让我们转向这个问题:
- 为什么仅使用 Controller+Action 请求路由与使用 {*page} 等变量请求路由之间存在差异?请讲技术:)
- 这里是一个如何在 AppHarbor 中处理请求的示例,但是,似乎它要求我修改我的所有控制器(OMG)。有没有办法在不修改我的控制器的情况下实现这一点?
- 欢迎任何其他建议:)
提前致谢。
更新:巧合的是,我观察到的行为与我得出的结论相符。但是,该问题与 ASP.Net MVC 路由无关。简而言之,FunnelWeb 强制使用小写 URL,因此,每当它收到对资源的请求时,它都会将其转换为小写(如果需要),并发出 301 响应。问题是,在为 301 响应创建 URL 时,请求 URL(绝对 URL)现在是从负载均衡器向 IIS 发出请求时使用的 URL,而不是从客户端发出的 URL;因此,请求失败。
I'm trying to host an MVC 3 application (FunnelWeb) on AppHarbor. For a reason that's still not clear to me, when my route is only a Controller+Action (e.g. mysite/admin is Admin+Index and mysite/login is Admin+login) everything works fine, but if I have anything else in the route (e.g. a variable like {*page}) my URL will be mysite:12345/mypage (where 12345 is a port number assigned by AppHarbor and mypage is the name of the page I'm requesting). This makes the request fail as the port 12345 is not publicly exposed.
AppHarbor uses load balancing to distribute the request between multiple IIS's. This is their way of doing stuff and this is why internally the requests are routed to some non-standard ports. I don't have a problem with that, but I have problem with MVC that tries to route me to that internal URL.
I'm not pointing fingers here; it's nobody's fault :) so let's move to the question:
- Why there is a difference between requesting a route with Controller+Action only and requesting a route with a variable like {*page}? Be technical please :)
- Here is an example of how to handle requests in AppHarbor, however, it seems that it requires me to modify all my controllers (OMG). Is there any way to implement this without modifying my controllers?
- Any other suggestions are welcomed :)
Thanks in advance.
UPDATE: Coincidentally, the behaviour that I observed matches the conclusion that I reached. However, the issue has nothing to do with ASP.Net MVC routing. The short story is, FunnelWeb forces lowercase URL's, so, whenever it receives a request to a resource it convert it to lowercase, if needed, and issue a 301 response. The problem is, when creating the URL for the 301 response, the request URL (absolute URL) is now the URL used when the request made from the load balancer to IIS and not the one made from the client; hence, the request fails.
发布评论
评论(3)
这是 AppHarbor 上 FunnelWeb url 生成的已知问题。当使用标准 MVC 方法生成相对 URL 时,这不是问题。 AppHarbor 有一个简短指南和示例关于如何在知识库中生成公共 URL。
This is known issue with FunnelWeb url generation on AppHarbor. When using standard MVC methods to generate relative URLs, this is not a problem. AppHarbor has a short guide and sample on how the generate public URLs in the knowledge base.
现在您可能需要以下内容:
这是 AppHarbor 支持页面上的更新,网址为 http://support.appharbor.com/kb/getting-started/workaround-for-generate-absolute-urls-without-port-number
MSDN 表示以下 关于 UseHostHeaderForRequestUrl:
It's possible that the following is now all you need:
This is noted as an update on AppHarbor's support page at http://support.appharbor.com/kb/getting-started/workaround-for-generating-absolute-urls-without-port-number
MSDN says the following about UseHostHeaderForRequestUrl:
有一种方法,但需要几个课程。
当 ASP.NET MVC 注册路由时,它定义了一个路由处理程序。该路由处理程序返回一个处理请求的 HTTP 处理程序。如果您使用返回自定义 HTTP 处理程序的自定义路由处理程序,则可以使用几个装饰器类重写 HTTP 上下文。
首先创建一个
HttpContextProxy
和HttpRequestProxy
,它们派生自基类,并将所有方法和属性包装到内部实例。我已经提供了艰苦的工作。接下来创建装饰器,首先是 HTTP 上下文装饰器:
HTTP 请求装饰器:
如上所述,您还需要重写 MVC 类 - 这里是 HTTP 处理程序:
然后是路由处理程序:
最后,您需要替换关联的处理程序所有已注册的路线(或从一开始就正确映射它们):
There is a way, but it requires a couple of classes.
When ASP.NET MVC registers a route, it defines a route handler. This route handler returns a HTTP handler that handles the request. If you use a custom route handler that returns a custom HTTP handler, you can rewrite the HTTP context by using a couple decorator classes.
Start by creating a
HttpContextProxy
andHttpRequestProxy
that derives from the base classes and wraps all methods and properties to an inner instance. I've made the hard work available.Next create the decorators, first the HTTP context decorator:
The HTTP request decorator:
As mentioned, you also need to override the MVC classes - here the HTTP handler:
Then the route handler:
Finally, you'll need to replace the associated handler for all registered routes (or map them properly from the beginning):