1 namespace System.Web.Mvc {
3 using System.Collections.Specialized;
6 internal static class PathHelpers {
8 private readonly static UrlRewriterHelper _urlRewriterHelper = new UrlRewriterHelper();
10 // this method can accept an app-relative path or an absolute path for contentPath
11 public static string GenerateClientUrl(HttpContextBase httpContext, string contentPath) {
12 if (String.IsNullOrEmpty(contentPath)) {
16 // many of the methods we call internally can't handle query strings properly, so just strip it out for
19 contentPath = StripQuery(contentPath, out query);
21 return GenerateClientUrlInternal(httpContext, contentPath) + query;
24 private static string GenerateClientUrlInternal(HttpContextBase httpContext, string contentPath) {
25 if (String.IsNullOrEmpty(contentPath)) {
29 // can't call VirtualPathUtility.IsAppRelative since it throws on some inputs
30 bool isAppRelative = contentPath[0] == '~';
32 string absoluteContentPath = VirtualPathUtility.ToAbsolute(contentPath, httpContext.Request.ApplicationPath);
33 string modifiedAbsoluteContentPath = httpContext.Response.ApplyAppPathModifier(absoluteContentPath);
34 return GenerateClientUrlInternal(httpContext, modifiedAbsoluteContentPath);
37 // we only want to manipulate the path if URL rewriting is active for this request, else we risk breaking the generated URL
38 bool wasRequestRewritten = _urlRewriterHelper.WasRequestRewritten(httpContext);
39 if (!wasRequestRewritten) {
43 // Since the rawUrl represents what the user sees in his browser, it is what we want to use as the base
44 // of our absolute paths. For example, consider mysite.example.com/foo, which is internally
45 // rewritten to content.example.com/mysite/foo. When we want to generate a link to ~/bar, we want to
46 // base it from / instead of /foo, otherwise the user ends up seeing mysite.example.com/foo/bar,
47 // which is incorrect.
48 string relativeUrlToDestination = MakeRelative(httpContext.Request.Path, contentPath);
49 string absoluteUrlToDestination = MakeAbsolute(httpContext.Request.RawUrl, relativeUrlToDestination);
50 return absoluteUrlToDestination;
53 public static string MakeAbsolute(string basePath, string relativePath) {
54 // The Combine() method can't handle query strings on the base path, so we trim it off.
56 basePath = StripQuery(basePath, out query);
57 return VirtualPathUtility.Combine(basePath, relativePath);
60 public static string MakeRelative(string fromPath, string toPath) {
61 string relativeUrl = VirtualPathUtility.MakeRelative(fromPath, toPath);
62 if (String.IsNullOrEmpty(relativeUrl) || relativeUrl[0] == '?') {
63 // Sometimes VirtualPathUtility.MakeRelative() will return an empty string when it meant to return '.',
64 // but links to {empty string} are browser dependent. We replace it with an explicit path to force
65 // consistency across browsers.
66 relativeUrl = "./" + relativeUrl;
71 private static string StripQuery(string path, out string query) {
72 int queryIndex = path.IndexOf('?');
73 if (queryIndex >= 0) {
74 query = path.Substring(queryIndex);
75 return path.Substring(0, queryIndex);