ASP.NET MVC,Url路由:最大路径(URL)长度

72

场景

我有一个应用程序,其中我们采用了老式的查询字符串URL结构:

?x=1&y=2&z=3&a=4&b=5&c=6

并将其转换为路径结构:
/x/1/y/2/z/3/a/4/b/5/c/6

我们正在使用ASP.NET MVC和(自然地)ASP.NET路由。
问题
问题在于我们的参数是动态的,理论上没有限制我们需要适应的参数数量。
这一切都很好,直到我们遭受以下错误:
HTTP错误400.0 - 错误请求 ASP.NET检测到URL中存在无效字符。
当我们的URL超过一定长度时,IIS会抛出此错误。
要点
以下是我们找到的内容:
这不是IIS的问题
IIS确实有最大路径长度限制,但上述错误不是这个问题。
了解dot iis dot net如何使用请求过滤器,部分“基于请求限制的过滤器”。
如果路径对IIS来说太长,它会抛出404.14而不是400.0。
此外,IIS的最大路径(和查询)长度是可配置的:
<requestLimits


   maxAllowedContentLength="30000000"


   maxUrl="260"


   maxQueryString="25" 


              />

这是一个ASP.NET的问题

调查后发现:

IIS论坛 主题:ASP.NET 2.0最大URL长度? http://forums.iis.net/t/1105360.aspx

事实证明,这是一个ASP.NET(其实是.NET)的问题。

关键在于,据我所知,ASP.NET无法处理超过260个字符的路径。

Phil the Haack本人也证实了这一点:

Stack Overflow ASP.NET url MAX_PATH limit 问题ID 265251

问题是什么?

那么问题是什么呢?

问题是,这有多大的限制性?

对于我的应用程序来说,这是致命的。对于大多数应用程序来说,可能并不重要。

公开披露呢?在提到ASP.NET Routing时,我从未听说过这种限制。ASP.NET MVC使用ASP.NET Routing使得这种影响更加严重。

你怎么看?

7个回答

47

我最终在 web.config 中使用以下内容解决了这个问题,使用了 Mvc2 和 .Net Framework 4.0。

<httpRuntime maxUrlLength="1000" relaxedUrlToFileSystemMapping="true" />

最大URL长度可以达到2097151。 - Amar Palsapure
1
不支持260段限制,同时http://support.microsoft.com/en-us/kb/820129 UrlSegmentMaxCount也不支持。 - edelwater
是的,relaxedUrlToFileSystemMapping 是唯一可行的方式。 - mike nelson
2
我不需要使用relaxedUrlToFileSystemMapping来纠正长URL的HTTP 400错误;只需设置maxUrlLength就可以解决问题。 - Jon Schneider
.NET MAXPATH 的限制现在已经在4.6.2中得到解决。(https://blogs.msdn.microsoft.com/dotnet/2016/08/02/announcing-net-framework-4-6-2/#bcl) - David
非常感谢,这是唯一有效的解决方案。我遇到了404错误页面,但当我将relaxedUrlToFileSystemMapping设置为“true”时,问题得到了解决。 - alnaji

35

Http.sys服务默认将每个URL段编码为最多260个字符。

在此上下文中,“URL段”是URL中“/”字符之间的内容。例如:

http://www.example.com/segment-one/segment-two/segment-three

可以通过更改注册表设置来更改允许的最大Url段长度:

  • 键: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\HTTP\Parameters
  • 值: UrlSegmentMaxLength
  • 类型: REG_DWORD
  • 数据:(您希望的新的Url段最大允许长度,例如4096)

有关http.sys设置更多信息: http://support.microsoft.com/kb/820129

最大允许值为32766。如果指定了更大的值,则会被忽略。(鸣谢:Juan Mendes)

更改此设置需要重新启动计算机才能生效。(鸣谢:David Rettenbacher,Juan Mendes)


1
值得注意的是,此设置需要重新启动才能生效。 - David Rettenbacher
3
重启机器还是只重启IIS? - Andrew Bullock
2
我不得不重新启动整个机器。警告:我最初将此值设置得太高(2097151),但它没有生效。当我查看答案中链接的文档并发现最大值为32766时,它最终起作用了。 - Ruan Mendes
我在Win Server 08的注册表中没有看到这个设置。我认为需要添加它。请澄清它是需要添加为键还是dword等类型。谢谢。 - AlexVPerl
1
在web.config中是否有类似的定义方式?我不想在执行此操作时重新启动同一服务器上的其他应用程序。 - Sudhir
显示剩余2条评论

33

要解决这个问题,请按照以下步骤操作:

在项目的根Web.config文件中,在system.web节点下进行如下配置:

<system.web>
    <httpRuntime maxUrlLength="10999" maxQueryStringLength="2097151" />
...

此外,我必须将此添加到system.webServer节点下,否则我的长查询字符串会出现安全错误:

<system.webServer>
    <security>
      <requestFiltering>
        <requestLimits maxUrl="10999" maxQueryString="2097151" />
      </requestFiltering>
    </security>
...

它对我有用,system.webServer节点很重要! - Rwing
1
@edelwater,这似乎与文件系统规则检查有关,但将relaxedUrlToFileSystemMapping设置为true也没有帮助。 - Jerther
在REST API应用程序中解决了处理非常长的GET查询的问题。 - Grengas

17

好的,我发布这篇文章的原因之一是因为我们找到了一个解决方法。

我希望这对将来的某些人有用 :D

解决方法

这个解决方法非常简单,也很好用。

既然我们知道网站的哪些部分需要使用动态参数(因此会具有动态路径和长度),我们就可以在它到达 ASP.NET 之前拦截它,从而避免将这个长URL发送给 ASP.NET 路由。

输入 IIS7 Url 重写(或任何等效的重写模块)。

我们设置了如下规则:

    <rewrite>
        <rules>
            <rule>
                <rule name="Remove Category Request Parameters From Url">
                <match url="^category/(\d+)/{0,1}(.*)$" />
                <action type="Rewrite" url="category/{R:1}" />
            </rule>
        </rules>
    </rewrite>

基本上,我们所做的只是保留足够的路径来调用下游的正确路由。我们要砍掉URL路径的其余部分。

剩下的URL路径去哪里了?

好吧,当重写规则被触发时,IIS7 URL Rewrite模块会自动在请求中设置此标题:

HTTP_X_ORIGINAL_URL

在应用程序的下游部分解析动态路径时,不要查看路径,而是:
HttpContext.Request.Url.PathAndQuery

我们来看一下那个标题:

HttpContext.Request.ServerVariables["HTTP_X_ORIGINAL_URL"]

问题解决了……几乎!

问题

访问标题

如果您需要知道,要访问IIS7 Rewrite模块标头,有两种方法:

HttpContext.Request.ServerVariables["HTTP_X_ORIGINAL_URL"]

或者
HttpContext.Request.Headers["X-ORIGINAL-URL"]

修复相对路径

您还会注意到,在上述设置中,所有相对路径(使用“~”定义的URL)都会失效。

这包括使用ASP.NET MVC的HtmlHelperUrlHelper方法定义的URL(例如Url.Route("Bla"))。

这就是可以访问ASP.NET MVC代码的好处所在。

System.Web.Mvc.PathHelper.GenerateClientUrlInternal()方法中,有一个检查是否存在相同的URL转发模块头(见上文):

// we only want to manipulate the path if URL rewriting is active, else we risk breaking the generated URL
NameValueCollection serverVars = httpContext.Request.ServerVariables;
bool urlRewriterIsEnabled = (serverVars != null && serverVars[_urlRewriterServerVar] != null);
if (!urlRewriterIsEnabled) {
    return contentPath;
}

如果发生URL改写,有些工作需要做来保留原始的URL。
在我们的情况下,由于我们没有以“正常”方式使用URL重写,我们希望绕过这个过程。
我们想要假装没有发生URL改写,因为我们不希望将相对路径视为原始URL上下文的一部分。
我能想到的最简单的方法是完全删除该服务器变量,这样ASP.NET MVC就找不到它了。
protected void Application_BeginRequest()
{
    string iis7UrlRewriteServerVariable = "HTTP_X_ORIGINAL_URL";

    string headerValue = Request.ServerVariables[iis7UrlRewriteServerVariable];

    if (String.IsNullOrEmpty(headerValue) == false)
    {
        Request.ServerVariables.Remove(iis7UrlRewriteServerVariable);

        Context.Items.Add(iis7UrlRewriteServerVariable, headerValue);
    }
}

(注意,在上面的方法中,我从Request.ServerVariables中删除了标头,但仍然保留它,并将其存储在Context.Items中。这样做的原因是我需要在请求管道的后续阶段访问标头值。)
希望这可以帮助您!

请将此设置为答案,好吗? - Dan Atkinson
根据您的要求,我将此设置为答案。 - Martin Suchanek
如果我这样做,是否还需要更改web.config文件?我想避免更改web.config文件,以便我可以仅允许一个特定URL的长请求,同时像往常一样阻止所有其他请求。 - ChrisFox

4

我曾在使用 ASP.NET Web API 4 时遇到了类似的 URL 最大长度问题,错误略有不同:

404 错误

对于我的情况,解决方法如上所述,需要更新 Web.config 文件中以下两个标签:

<system.web>
    <httpRuntime maxUrlLength="10999" maxQueryStringLength="2097151" />

并且

<system.webServer>
    <security>
      <requestFiltering>
        <requestLimits maxUrl="10999" maxQueryString="2097151" />
      </requestFiltering>
    </security>

1

看起来,在 .NET 4.0 中硬编码的最大 URL 长度已经被修复。 特别是现在有一个web.config部分:

<httpRuntime maxRequestPathLength="260" maxQueryStringLength="2048" /> 

这让您可以扩展允许的URL范围。


Joannes,感谢您更新此主题。这个问题计划在.NET 4中修复,幸运的是它已经解决了。 - Martin Suchanek
7
很抱歉,这是不正确的。微软改变了主意并采用了lmingle所给出的答案,即类似于<httpRuntime maxUrlLength="1024" relaxedUrlToFileSystemMapping="true"/>这样的东西。 - Tom Chantler
Dommer, Joannes,感谢您的跟进。我已经相应地更改了被接受的答案。不过我自己还没有能够验证。 - Martin Suchanek

1

我认为你过于努力地使用GET方法。尝试将请求方式更改为POST,并将这些查询字符串参数放入请求正文中。

长URL也不会对SEO有所帮助,对吗?


7
我使用GET方法有两个原因:1)这些URL需要被分享,而且需要保持不变。2)我没有使用POST方法。这是一个读取操作。
长的URL对SEO也没有帮助,对吗?
我不确定,但如果请求很复杂,查询也是如此......除非我使用非语义化的URL。
- Martin Suchanek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接