1 /* ****************************************************************************
\r
3 * Copyright (c) Microsoft Corporation. All rights reserved.
\r
5 * This software is subject to the Microsoft Public License (Ms-PL).
\r
6 * A copy of the license can be found in the license.htm file included
\r
7 * in this distribution.
\r
9 * You must not remove this notice, or any other, from this software.
\r
11 * ***************************************************************************/
\r
13 namespace System.Web.Mvc {
\r
15 using System.Collections.Specialized;
\r
18 internal static class PathHelpers {
\r
20 private const string _urlRewriterServerVar = "HTTP_X_ORIGINAL_URL";
\r
22 // this method can accept an app-relative path or an absolute path for contentPath
\r
23 public static string GenerateClientUrl(HttpContextBase httpContext, string contentPath) {
\r
24 if (String.IsNullOrEmpty(contentPath)) {
\r
28 // many of the methods we call internally can't handle query strings properly, so just strip it out for
\r
31 contentPath = StripQuery(contentPath, out query);
\r
33 return GenerateClientUrlInternal(httpContext, contentPath) + query;
\r
36 private static string GenerateClientUrlInternal(HttpContextBase httpContext, string contentPath) {
\r
37 if (String.IsNullOrEmpty(contentPath)) {
\r
41 // can't call VirtualPathUtility.IsAppRelative since it throws on some inputs
\r
42 bool isAppRelative = contentPath[0] == '~';
\r
43 if (isAppRelative) {
\r
44 string absoluteContentPath = VirtualPathUtility.ToAbsolute(contentPath, httpContext.Request.ApplicationPath);
\r
45 string modifiedAbsoluteContentPath = httpContext.Response.ApplyAppPathModifier(absoluteContentPath);
\r
46 return GenerateClientUrlInternal(httpContext, modifiedAbsoluteContentPath);
\r
49 // we only want to manipulate the path if URL rewriting is active, else we risk breaking the generated URL
\r
50 NameValueCollection serverVars = httpContext.Request.ServerVariables;
\r
51 bool urlRewriterIsEnabled = (serverVars != null && serverVars[_urlRewriterServerVar] != null);
\r
52 if (!urlRewriterIsEnabled) {
\r
56 // Since the rawUrl represents what the user sees in his browser, it is what we want to use as the base
\r
57 // of our absolute paths. For example, consider mysite.example.com/foo, which is internally
\r
58 // rewritten to content.example.com/mysite/foo. When we want to generate a link to ~/bar, we want to
\r
59 // base it from / instead of /foo, otherwise the user ends up seeing mysite.example.com/foo/bar,
\r
60 // which is incorrect.
\r
61 string relativeUrlToDestination = MakeRelative(httpContext.Request.Path, contentPath);
\r
62 string absoluteUrlToDestination = MakeAbsolute(httpContext.Request.RawUrl, relativeUrlToDestination);
\r
63 return absoluteUrlToDestination;
\r
66 public static string MakeAbsolute(string basePath, string relativePath) {
\r
67 // The Combine() method can't handle query strings on the base path, so we trim it off.
\r
69 basePath = StripQuery(basePath, out query);
\r
70 return VirtualPathUtility.Combine(basePath, relativePath);
\r
73 public static string MakeRelative(string fromPath, string toPath) {
\r
74 string relativeUrl = VirtualPathUtility.MakeRelative(fromPath, toPath);
\r
75 if (String.IsNullOrEmpty(relativeUrl) || relativeUrl[0] == '?') {
\r
76 // Sometimes VirtualPathUtility.MakeRelative() will return an empty string when it meant to return '.',
\r
77 // but links to {empty string} are browser dependent. We replace it with an explicit path to force
\r
78 // consistency across browsers.
\r
79 relativeUrl = "./" + relativeUrl;
\r
84 private static string StripQuery(string path, out string query) {
\r
85 int queryIndex = path.IndexOf('?');
\r
86 if (queryIndex >= 0) {
\r
87 query = path.Substring(queryIndex);
\r
88 return path.Substring(0, queryIndex);
\r