在ASP.NET 4.0中使用%20和%3F处理URL错误的正确方法



我的网站上90%的页面都遵循以下语法

http://www.thisismysite.com/ShowProduct.aspx?ID=29
http://www.thisismysite.com/BrowseProducts.aspx?CatID=58

我检查了谷歌网站管理员的工具,发现我的网站正在为以下网址的生成错误

http://www.thisismysite.com/ShowProduct.aspx%20ID=50
http://www.thisismysite.com/BrowseProducts.aspx%20CatID=58
http://www.thisismysite.com/ShowProduct.aspx%3FID%3D900
http://www.thisismysite.com/ShowProduct.aspx%3FID=727
http://www.thisismysite.com/ShowProduct.aspx%3FID=64
http://www.thisismysite.com/GetProductsRss.aspx%3FCatID%3D60

当我浏览这些网址时,我得到了错误

"/"应用程序中的服务器错误

运行时错误描述:处理您的请求时发生异常。此外,在执行第一个异常的自定义错误页时发生了另一个异常。请求已终止

如何修复这些错误?我知道relaxedUrlToFileSystemMapping="true",但那不是黑客吗?处理这些url的的正确方法是什么

我的web.config的一部分看起来像这个

<system.web>
<httpRuntime requestValidationMode="2.0" />
<authentication mode="Forms">
    <forms cookieless="UseCookies" loginUrl="~/AccessDenied.aspx" name="TBFORMAUTH" />
</authentication>
<pages theme="TemplateM" masterPageFile="~/Template.master"   maintainScrollPositionOnPostBack="false" validateRequest="false"   enableEventValidation="false" viewStateEncryptionMode="Never"   controlRenderingCompatibilityVersion="3.5" clientIDMode="AutoID">  
</pages>
<!--
Possible modes are "transitional", "strict", and "legacy".
<xhtmlConformance mode="transitional" />
-->
<compilation debug="false" targetFramework="4.0">           
    <assemblies>
        <add assembly="System.Transactions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C53R34E089" /></assemblies></compilation>

<sessionState mode="InProc" cookieless="false" timeout="15" />
<roleManager enabled="true" cacheRolesInCookie="true" cookieName="TBROLES" defaultProvider="TB_RoleProvider">
    <providers>
        <add connectionStringName="LocalSqlServer" applicationName="/" name="TB_RoleProvider" type="System.Web.Security.SqlRoleProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f22f50a3a" />
    </providers>
</roleManager>
<anonymousIdentification cookieless="UseCookies" enabled="false" />
<profile defaultProvider="TB_ProfileProvider">
    <providers>
        <add name="TB_ProfileProvider" connectionStringName="LocalSqlServer" applicationName="/" type="System.Web.Profile.SqlProfileProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f64f1d50a3a" />
    </providers>
    <properties>
        <add name="FirstName" type="String" />
        <add name="LastName" type="String" />
        <add name="Gender" type="String" />
        <add name="BirthDate" type="DateTime" />
        <add name="Occupation" type="String" />
        <add name="Website" type="String" />
        <group name="Forum">
            <add name="Posts" type="Int32" />
            <add name="AvatarUrl" type="String" />
            <add name="Signature" type="String" />
        </group>
        <group name="Address">
            <add name="Street" type="String" />
            <add name="PostalCode" type="String" />
            <add name="City" type="String" />
            <add name="State" type="String" />
            <add name="Country" type="String" />
        </group>
        <group name="Contacts">
            <add name="Phone" type="String" />
            <add name="Fax" type="String" />
        </group>
        <group name="Preferences">
            <add name="Theme" type="String" allowAnonymous="false" />
            <add name="Culture" type="String" defaultValue="en-US" />
        </group>
    </properties>
</profile>
<webParts enableExport="true">
    <personalization defaultProvider="TB_PersonalizationProvider">
        <providers>
            <add name="TB_PersonalizationProvider" connectionStringName="LocalSqlServer" type="System.Web.UI.WebControls.WebParts.SqlPersonalizationProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
        </providers>
        <authorization>
            <allow roles="Administrators,Editors" verbs="enterSharedScope" />
        </authorization>
    </personalization>
</webParts>
<machineKey validationKey="287C5D125D6B7E7223E1F719E3D58D17BB9677030175D6B7E7223E1F719E3D58D17BBC7E59800B5D4C2EDD5B5D6B7E7223E1F719E3D58D17BBBAF260D9D374A74C76CB741803" decryptionKey="5C1D8BD9DF3E1B4E1D05C1D8BD9DF616E0D5C1D8BD9DF" validation="SHA1" />
<customErrors defaultRedirect="~/Error.aspx" redirectMode="ResponseRewrite">
</customErrors>
<urlMappings>
    <add url="~/articles/beer.aspx" mappedUrl="~/BrowseProducts.aspx?CatID=28" />
    <add url="~/articles/events.aspx" mappedUrl="~/BrowseProducts.aspx?CatID=41" />
    <add url="~/articles/news.aspx" mappedUrl="~/BrowseProducts.aspx?CatID=31" />
    <add url="~/articles/photos.aspx" mappedUrl="~/BrowseProducts.aspx?CatID=40" />
    <add url="~/articles/blog.aspx" mappedUrl="~/BrowseProducts.aspx?CatID=29" />
    <add url="~/articles/faq.aspx" mappedUrl="~/BrowseProducts.aspx?CatID=42" />
</urlMappings>
<healthMonitoring heartbeatInterval="10800">
    <providers>
        <remove name="SqlWebEventProvider" />
        <add name="SqlWebEventProvider" connectionStringName="LocalSqlServer" buffer="false" bufferMode="Notification" maxEventDetailsLength="1073741823" type="System.Web.Management.SqlWebEventProvider,System.Web,Version=2.0.0.0,Culture=neutral,PublicKeyToken=b03f5f7fse350a3a" />
    </providers>
    <eventMappings>
        <add name="TB Events" type="MB.Customs.WebCustomEvent, MB.Customs.CustomEvents" />
    </eventMappings>
    <rules>
        <clear />
        <add name="TB Events" eventName="TB Events" provider="SqlWebEventProvider" profile="Critical" />
        <add name="All Errors" eventName="All Errors" provider="SqlWebEventProvider" profile="Critical" />
        <add name="Failure Audits" eventName="Failure Audits" provider="SqlWebEventProvider" profile="Critical" />
        <add name="Heartbeats" eventName="Heartbeats" provider="SqlWebEventProvider" profile="Critical" />
    </rules>
</healthMonitoring>
</system.web>

由于这些外部链接是URL编码的,ASP.NET将尝试查找具有该名称的文件或路由。您可以尝试打乱路由框架,以便在应用程序中明智地查找以路由开头的URL。然而,这可能意味着每个页面都需要额外的逻辑来解析编码不正确的请求。另一个解决方案是为404添加自定义错误处理,当您知道如何最好地重定向用户时,ASP.NET将生成404。

处理此错误的最佳方法可能是拦截应用程序Global.asax.cs文件中的错误。您可以在Application_Error方法中尝试以下方法:

protected void Application_Error(Object sender, EventArgs e) 
{
    bool httpError = Context.Error is HttpException;
    if (httpError && ((HttpException)Context.Error).GetHttpCode() == 404)
    {
        // Convert the path to lowercase. This should ONLY be used to make finding indices easier, but NOT used when generating the redirect path since case could be important.
        string absolutePath = Request.Url.AbsolutePath.ToLowerInvariant();
        int extensionLength = ".aspx".Length;
        int questionMarkIdx = absolutePath.IndexOf("?");
        int encodedQuestionMarkIdx = absolutePath.IndexOf(".aspx%3F") + extensionLength;
        int encodedSpaceIdx = absolutePath.IndexOf(".aspx%20") + extensionLength;
        // Handle encoded question mark
        if ((questionMarkIdx == -1) && (encodedQuestionMarkIdx > extensionLength))
        {
            string correctPath = Request.Url.AbsolutePath.Substring(0, absolutePath.Length - encodedQuestionMarkIdx);
            // Add 3 here to exclude the "%3F" in the result
            string encodedQueryString = Request.Url.AbsolutePath.Substring(encodedQuestionMarkIdx + 3);
            Response.Redirect(correctPath + "?" + HttpUtility.UrlDecode(encodedQueryString));
        }
        // Handle encoded space
        if ((questionMarkIdx == -1) && (encodedSpaceIdx > extensionLength))
        {
            string correctPath = Request.Url.AbsolutePath.Substring(0, absolutePath.Length - encodedSpaceIdx);
            // Add 3 here to exclude the "%20" in the result
            string encodedQueryString = Request.Url.AbsolutePath.Substring(encodedSpaceIdx + 3);
            Response.Redirect(correctPath + "?" + HttpUtility.UrlDecode(encodedQueryString));
        }
    }
}

我在一台非Windows机器上写了这个答案,所以我没有办法测试它,但这应该足以让你朝着解决这个问题的大致方向前进。

重要的一点是要区分因URL编码不当而发生的404和因其他无法处理的一般原因而发生的404。由于URL编码问题,您只想处理404。所有其他错误和其他404消息都应该传播回您的默认处理程序。

同样需要检查的一件重要事情是,在这些对坏URL的请求中,引用者是什么。你应该看看谷歌网站管理员的工具,看看他们是否告诉你。如果他们不这样做,你需要检查你的日志(或者确保你做了一些!)来找出它们的来源。一旦你知道了来源,你应该联系网站所有者,让他们知道他们正在提供坏的URL。试图弥补断开的外部联系并不是万无一失的,而且是一个非常困难的挑战,它可能会成为一份全职工作,所以如果你有能力做到这一点,你最好从一开始就阻止它们的产生。

最新更新