Search code examples
c#asp.net.net-3.5system.webhttpexception

Why do I get an HttpException from VirtualPathUtility ToAbsolute with some paths?


I'm trying to use VirtualPathUtility.ToAbsolute to resolve app-relative paths, such as ~/MyPage.aspx, to application-absolute paths, such as /MySite/MyApp/MyPage.aspx. However, with some paths, I receive an HttpException saying that my path is "not a valid virtual path". Examples:

// This works:
var abs1 = VirtualPathUtility.ToAbsolute("~/MyPage.aspx#anchor");

// This errors:
var abs2 = VirtualPathUtility.ToAbsolute("~/MyPage.aspx?key=value");

What's going on?


Solution

  • Because you're using .NET 3.5, you're using the 2.0 System.Web assembly, which has the defect that ? is considered an illegal path character by this method. This is mentioned in the community comments on the version-specific MSDN page.

    By disassembling, it can be seen that the call ends up in the (internal) VirtualPath.Create, which has:

      else if (VirtualPath.ContainsIllegalVirtualPathChars(virtualPath))
      {
        throw new HttpException(System.Web.SR.GetString("Invalid_vpath", new object[1]
        {
          (object) virtualPath
        }));
      }
    

    which references

    private static char[] s_illegalVirtualPathChars = new char[4]
    {
      ':',
      '?',
      '*',
      char.MinValue
    };
    

    Some of these can reasonably be regarded as bad characters for a path, but ? shouldn't really be so rejected.

    Disassembly of the 4.0 System.Web shows that VirtualPath.Create has been rewritten to be more discerning.

    This web.archive capture of a now-defunct blogs.msdn post shows one of the earliest mentions of this problem. The MS employee responds:

    Sunday, February 26, 2006 11:49 PM by DmitryR Exception on ~/path?qs is a bug that I'll need to fix...

    The easiest fix is to save/restore query string in ResolveAppRelativeLinkToUrl around the call to VirtualPathUtility.ToAbsolute.

    A workaround is to use fully qualified UTLs instead of "~/...".

    Thanks,

    Dmitry

    where ResolveAppRelativeLinkToUrl refers to the reporter's code's method name.

    Another workaround would be to replace ? with a safe token before the call to VirtualPathUtility.ToAbsolute, and reverse the replacement afterwards:

    public static string SafeToAbsolute(string path)
    {
        var madeSafe = path.Replace("?", "UNLIKELY_TOKEN");
        var absolute = VirtualPathUtility.ToAbsolute(madeSafe);
        var restored = absolute.Replace("UNLIKELY_TOKEN", "?");
        return restored;
    }
    

    choosing a suitably unlikely token for your application.