Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

$location does not parse query portion of URL correctly when in non-html5 mode. #15856

Closed
2 tasks
ed-at-work opened this issue Mar 24, 2017 · 4 comments
Closed
2 tasks

Comments

@ed-at-work
Copy link

I'm submitting a ...

  • [x ] bug report
  • feature request
  • other (Please do not submit support requests here (see above))

Current behavior:
When the $locationProvider has set html5 mode to false, URLs that follow the RFC for URIs (RFC 3986) and include both a query string portion and a fragment portion in the URI will not have the query string portion set, so $location.search('foo') is empty. In fact even the documentation for $location is wrong, since it gives an example like this:

// given URL http://example.com/#/some/path?foo=bar&baz=xoxo

even though the RFC is perfectly clear that a well formed URL looks like this:

URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ] (see https://tools.ietf.org/html/rfc3986#appendix-A).

Expected / new behavior:
AngularJS should respect the specification and work properly when the query portion of the URL appears before the fragment portion.

Minimal reproduction of the problem with instructions:
Reproducing the problem is simple on any browser that follows the specification. Attempt to navigate to a URL such as http://example.com/foo?query=bar&baz=topaz#/path/to/angular/route and you'll find that the $location service does not know about the query parameters in this URL.

Angular version: 1.x.y
I have noticed this problem in 1.5.7 but the same patch applies cleanly to master as of this writing so I assume the problem continues to exist in master as of this issue report.

Browser: [all | Chrome XX | Firefox XX | IE XX | Safari XX | Mobile Chrome XX | Android X.X Web Browser | iOS XX Safari | iOS XX UIWebView | iOS XX WKWebView ]
iOS WKWebView

Anything else:
I have a PR ready to go for this.

@gkalpak
Copy link
Member

gkalpak commented Mar 24, 2017

$location works as expected. When in hashbang mode (non-html5), AngularJS is only concerned about the part after the # (i.e. the fragment). See #6172 (comment) for more info.

Note that according to RFC 3986, the fragment can contain ?, so the URL are perfectly valid.

@gkalpak gkalpak closed this as completed Mar 24, 2017
@ed-at-work
Copy link
Author

I disagree pretty strongly with this understanding of the RFC and in particular point to the code itself to show why. While it's true that the RFC allows the ? character in the fragment portion of the URL and gives wide latitude in the semantics of the contents of the fragment, the RFC also makes clear that the contents of the fragment are not the URI And that URI semantics don't apply to fragment contents.

However, the query portion of a URL is well-defined, and has a well-defined location in the context of the overall URI-- specifically NOT in the fragment. Your design effectively overwrites the semantics of a URL from something well-defined (both in terms of the spec and in terms of browser behaviour) to something arbitrary and in conflict with the RFC and browser behaviour. Your code itself makes this clear in the function urlResolve(url) where it uses an href DOM node to parse out the URL's components. After the $locationObj is created by this function, the query/search portion of the URL will be populated by the query portion of the URL that appears before the fragment in a well-defined URL, not by the query portion in the fragment portion.

Fundamentally, the AngularJS code misunderstands the original URL because it falsely construes the base url portion to include the non-fragment query portion of the URL instead of including the query portion in the parsed app url.

Perhaps a change here that you would accept is to copy the non-fragment query portion of the URL into the faux query portion left behind in the parsed app url? so in that scenario a URL like

http://foo.com/index.html?baz=topaz&moo=bar#/path?a=b&c

would become an app url of

/path?a=b&c&baz=topaz&moo=bar

?

@gkalpak
Copy link
Member

gkalpak commented Mar 25, 2017

I disagree with you disagreeing 😁

This is not about the understanding of the RFC. This is about implementing client-side routing and deep-linking for Single Page Applications (something that is well outside the scope of the RFC). In order to enable these features, we need to embed the part that represents the client-side path/routing state into the actual URL. There are two alternatives:

  1. In HTML5 mode:
    The server will ignore the path/query and let the client side handle it.
    The client will interpret the actual path/query/fragment as representing the client-side path/query/fragment.
  2. In hashbang mode:
    The server will handle the URL as usual, taking the path and query into account.
    The client will ignore the actual path/query (which are not considered part of the client-side URL) and only handle the fragment. In this case, the fragment

HTML5 mode Pros:

  • Nicer-looking URLS.
  • Ability to manipulate the URL via the standard APIs even for client-side purposes.

HTML5 mode Cons:

  • Requires server-side configuration.

Hahbang mode Pros:

  • Does not require server-side configuration.

Hashbang mode Cons:

  • Uglier URLs.
  • You need to use framework-specific APIs to manipulate the URL (e.g. $location).

It is important that the server-side and client-side parts of the URL are kept separated (and copying the actualy query into the fragment would break that).

Hashbang mode is one fo the alternatives, it is optional (so don't have to use it if it is not suitable for your usecase) and does not contradict with any RFC.

(This is just my undertanding and I respect that there can be deferring opinions. That's a good thing. Not everyone has to agree on everything 😁)

ed-at-work pushed a commit to ed-at-work/angular.js that referenced this issue Mar 26, 2017
ed-at-work pushed a commit to ed-at-work/angular.js that referenced this issue Mar 26, 2017
Angular does not currently include the non-fragment query portion
Of URLs in non-html5 $location mode. This commit merges the fragment
Query, if present, with the non-fragment query if it exists.

See angular#15856
@ed-at-work
Copy link
Author

I don't want to continue to argue about this much longer, but I do want you to consider that if I were to offer up a PR that includes karma tests, that you would seriously consider merging it into angular. While obviously the benefit of OSS is that we can maintain our own fork, that can end up being a big pain.

Also I'm sure you've been developing software for far longer than I have, and I appreciate you taking the time to respond to my posts here.

But the truth is that I think that there is some misunderstanding of what these URLs and RFC and client side routing is all about. Let's take a step back for a second. A URL locates a resource, and that location can point to a lot of different places. Sometimes it's a file:// URL, sometimes it's an http:// URL, sometimes even esoteric or defunct protocols can be specified in the scheme, like gopher://.

And I'm starting with the scheme to point out that you might be mistaken when you say things like

the server-side and client-side parts of the URL

because the idea of a "server-side" or "client-side" of the URL is probably a misunderstanding of what a URL is and what the RFC says. For example, the RFC talks mostly about "user agents" when it comes to parsing URLs. It's up to the user agent to parse the URL and to do something with it. In the case of a file:// URL, there isn't any server involved and as a matter of fact the bug that I'm facing has to do with file:// URLs.

Particularly, let me ask you this: what do you think the semantics should be for a URL that uses a proxy pattern to modify just the scheme part of a url from http:// to file:// and then back again? And have you considered the possibility that the same url should work correctly no matter whether or not it's used in a user agent that follows the html5 history API (html5 mode) or a user agent that relies on the non-html5 hashbang mode? Because right now from what I can tell, the same URL would have different semantics in the $location service depending on the mode of the user agent, and that strikes me as another way to describe this issue.

In my case, if angularjs's $location service doesn't understand the actual query portion of the URL to also be the query portion of the parsed $location in the non-html5 mode but it does understand the query portion of the URL to be part of the parsed $location when it's in html5 mode, that should be considered as a bug.

My approach to copy the query portion of the URL into the fragment of the URL only in the non-html5 hashbang mode should be backwards compatible because apps should ignore query parameters for which they aren't specifically looking.

I've updated my branch to includes karma tests for this change and ensured that the solution handles all existing test cases. I would ask you to consider accepting this change to fix the underlying issue that the semantics of a URL in html5 mode vs non-html5 mode is different in angular. URLs should mean the same to the application regardless of the modes available in the user agent.

Thanks,
--Ed

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

No branches or pull requests

2 participants