IIS Rewrite changing ASP.Net HyperLink Backend Bind

2020-04-01 c# asp.net

I'm going crazy, maybe someone here can sort this for me.

Simple rewrite rule:

<rule name="Pages Rewrite" enabled="true" stopProcessing="true">
                <match url="^(.*?)/$" />
                <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
                    <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
                    <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
                </conditions>
                <action type="Rewrite" url="pages/{R:1}.aspx" />
            </rule>

It works fine.

So we have example.com/pages/apage.aspx It's rewritten to example.com/apage/

All working as expected. On this page we have an ASP HyperLink (or a standard A link with runat=server)

But when binding:

HL_CustomizeCollapse.NavigateUrl = "#sample";

Instead of the href being example.com/apage/#sample The href is being set to: example.com/pages/#sample

So the rewrite rule is affecting the href when the link is set from codebehind. Any links set on the front side are fine. Outbound rules will change those as expected.

This tag is unaffected by outbound rules, and is affected by inbound rules.

I have no idea how to solve this issue.

Answers

Both HyperLink.NavigateUrl and a.href setters use Control.ResolveClientUrl() to determine the absolute path for a relative path.

Control.ResolveUrl(), on the other hand, uses the current template's source virtual directory (which is /aPage) to build the absolute path.

Thus, when the request path /pages/apage.aspx is re-written to /apage/, and the following code is executed as the result:

/aPage/default.aspx

<div>
    <asp:HyperLink ID="asp_hyperlink_to_default_aspx" runat="server">
        asp_hyperlink_to_default_aspx
    </asp:HyperLink><br />
    <asp:HyperLink ID="asp_hyperlink_to_resolve_client_url_default_aspx" runat="server">
        asp_hyperlink_to_resolve_client_url_default_aspx
    </asp:HyperLink><br />
    <asp:HyperLink ID="asp_hyperlink_to_resolve_url_default_aspx" runat="server">
        asp_hyperlink_to_resolve_url_default_aspx
    </asp:HyperLink><br />
</div>

/aPage/default.aspx.cs:

    Response.Write("<pre>");

    Response.Write("ResolveClientUrl(\"Default.aspx\") = " + ResolveClientUrl("Default.aspx") + "<br/>");
    Response.Write("ResolveUrl(\"Default.aspx\") = " + ResolveUrl("Default.aspx") + "<br/>");
    Response.Write("<br/>");
    Response.Write("Request.RawUrl = " + Request.RawUrl + "<br/>");
    Response.Write("Request.Url.AbsolutePath = " + Request.Url.AbsolutePath + "<br/>");
    Response.Write("Request.AppRelativeCurrentExecutionFilePath = " + Request.AppRelativeCurrentExecutionFilePath + "<br/>");

    asp_hyperlink_to_default_aspx.NavigateUrl = "Default.aspx";
    asp_hyperlink_to_resolve_client_url_default_aspx.NavigateUrl = ResolveClientUrl("Default.aspx");
    asp_hyperlink_to_resolve_url_default_aspx.NavigateUrl = ResolveUrl("Default.aspx");

    Response.Write("</pre>");

The following output is generated:

ResolveClientUrl("Default.aspx") = Default.aspx
ResolveUrl("Default.aspx") = /aPage/Default.aspx

Request.RawUrl = /pages/apage.aspx
Request.Url.AbsolutePath = /aPage/default.aspx
Request.AppRelativeCurrentExecutionFilePath = ~/aPage/default.aspx

<div>
    <a id="ContentPlaceHolder1_asp_hyperlink_to_default_aspx" href="Default.aspx">
        asp_hyperlink_to_default_aspx
    </a><br />
    <a id="ContentPlaceHolder1_asp_hyperlink_to_resolve_client_url_default_aspx" href="Default.aspx">
        asp_hyperlink_to_resolve_client_url_default_aspx
    </a><br />
    <a id="ContentPlaceHolder1_asp_hyperlink_to_resolve_url_default_aspx" href="/aPage/Default.aspx">
        asp_hyperlink_to_resolve_url_default_aspx
    </a><br />
</div>

Therefore, to be on the safe side, it is best to use ResolveUrl() to set the NavigationUrl and href properties of HyperLink and A, respectively.

Hope this helps.

Good luck.

Related