Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Relative URLs for images, css, JavaScript #286

Closed
amainiii opened this issue Aug 5, 2016 · 13 comments
Closed

Relative URLs for images, css, JavaScript #286

amainiii opened this issue Aug 5, 2016 · 13 comments

Comments

@amainiii
Copy link

amainiii commented Aug 5, 2016

When resources (image, css, javascript) are embedded using relative paths, the
browser will request them with the language tag. Because these resources would typically be excluded due to filtering, the language tag would not be removed from the URL. The only solution we can find to handle relative paths to resources is to set the QuickUrlExclusionFilter to null.

Because the application might not be installed directly underneath a host name, recommended best practice for ASP.NET is to use "~/images/resource.png" or "images/resource.png" rather than "/images/resource.png".

If I'm understanding this correctly, the QuickUrlExclusionFilter would not be useful in these cases and perhaps a caveat should be added to the manual/wiki to help future adopters.

@turquoiseowl
Copy link
Owner

Hi, I'm not totally with you. Would you try explaining again.

On the point about ~/images/resource.png: doesn't that get resolved to an absolute URL or can it still be resolved to a relative one? But anyhow i18n should work with relative paths okay.

@turquoiseowl
Copy link
Owner

BTW, it really helps me to talk in terms of HTTP and requests/responses. Thanks.

@amainiii
Copy link
Author

amainiii commented Aug 8, 2016

On the point about ~/images/resource.png: doesn't that get resolved to an absolute URL or can it still be resolved to a relative one? But anyhow i18n should work with relative paths okay.

ASP.NET converts ~/images/resource.png to a relative path: "images/resource.png". The browser thinks that the web page that contains the reference to the image is (for example) "http://host.domain.tld/en/Default.aspx". So when the browser does a GET on the image, it requests "http://host.domain.tld/en/images/resource.png". Because the QuickUrlExclusionFilter bypasses these image files, the language tag (en) is not removed from the path, and the result is 404, not found.

I think ASP.NET used to convert tilde paths to absolute URLs prior to .NET 4.0, but I can't swear to it. I'll provide a sample ASP.NET solution for Visual Studio 2015, if that helps.

@turquoiseowl
Copy link
Owner

Checking a site here (ASP.NET v4.0), for this markup (in Razor):

<img src="~/Content/img/dslogo-full-inverse-32.png" />

I get in the response:

<img src="/Content/img/dslogo-full-inverse-32.png"  />

(Note the leading slash.)

So your leading slash is being dropped? I.e. "~/" goes to "" and not "/"?

@turquoiseowl
Copy link
Owner

It's hard to find any hard and fast documentation on what the tilde means, but the best I can find is that it represents the application root, in which case I'm struggling to see how/why the original slash is being dropped from "images". What is your version of ASP.NET and IIS? This must be something new in those versions.

@amainiii
Copy link
Author

amainiii commented Aug 8, 2016

Our current theory is that MVC is converting "~" paths to "rooted" paths, but Web Forms is converting everything to relative paths. The corresponding .NET method calls are ResolveUrl (which "roots" everything) and ResolveClientUrl which generates relative paths. We've been looking at this all morning, and would like to debug the i18n in visual studio, but have never implemented/tested a System.Web.IHttpModule, so are not sure exactly how to proceed to debug.

@amainiii
Copy link
Author

amainiii commented Aug 8, 2016

Attached is a Visual Studio 2015 ASP.NET 4.5 Web Forms solution that illustrates the problems we are having with relative URLs.
Samplei18n.zip

@turquoiseowl
Copy link
Owner

If it helps, some notes here on how to debug i18n: #109

@turquoiseowl
Copy link
Owner

Here's a hack on the web:

<a href="<%= Page.ResolveUrl("~/contactus.aspx")%>" >Contact us</a>

@amainiii
Copy link
Author

amainiii commented Aug 8, 2016

We have added/modified the following code near the end of LocalizeUrl in i18n/Concrete/EarlyUrlLocalizer.cs and it seems to resolve our issues with ASP.NET web forms (at least so far). Not sure if this has an adverse impact on MVC so we don't feel comfortable forking/pushing/pulling yet.

// root the url relative to the application root, eliminating "../..", "./", etc.
Uri newUri = new Uri(requestUrl, url);
String newUrl = newUri.PathAndQuery;

// Localize the URL.
return m_urlLocalizer.SetLangTagInUrlPath(context, newUrl, 
     UriKind.RelativeOrAbsolute, langtag);

@turquoiseowl
Copy link
Owner

turquoiseowl commented Aug 8, 2016

Yeah, that looks good. I've tweaked slightly in the hope of making it optimally efficient. Let me know if it still works or not. If it does I can patch it in:

            // If url is un-rooted...make it absolute based on the current request URL.
            // By un-rooted we mean it is not absolute (i.e. it starts from the path component),
            // and that that path component itself is a relative path (i.e. it doesn't start with a /).
            // In doing so we also eliminate any "../..", "./", etc.
            // Examples:
            //      url (before)                            requestUrl                  url (after)
            //      -------------------------------------------------------------------------------------------------------------------
            //      content/fred.jpg                        http://example.com/blog     /blog/content/fred.jpg              (changed)
            //      /content/fred.jpg                       http://example.com/blog     /content/fred.jpg                   (unchanged)
            //      http://example.com/content/fred.jpg     http://example.com/blog     http://example.com/content/fred.jpg (unchanged)
            //
            if (!url.StartsWith("/")
                && !Uri.IsWellFormedUriString(url, UriKind.Absolute)) {
                Uri newUri = new Uri(requestUrl, url);
                url = newUri.PathAndQuery;
            }

            // Localize the URL.
            return m_urlLocalizer.SetLangTagInUrlPath(context, url, UriKind.RelativeOrAbsolute, langtag);

@amainiii
Copy link
Author

We've tested your changes and they work fine. We had some concerns about the performance of the Uri.IsWellFormedUriString method http://referencesource.microsoft.com/#System/net/System/UriExt.cs so we tried performance testing with several other alternatives, but none were clearly superior. So, if you wouldn't mind making the change next time its convenient, we'd appreciate it. - Thanks!

turquoiseowl added a commit that referenced this issue Aug 10, 2016
@turquoiseowl
Copy link
Owner

Good discovery about Uri.IsWellFormedUriString! I should have suspected. Anyhow it occurred to me we only need call it if the url doesn't contain a colon (in which case maybe we don't need to call it at all -- bit late for me to work that through).

Anyhow, you may want to check the commit. I've added some test cases as well so hopefully it's cracked.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants