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

Impossible to encode slashes as path parameters using URI encoding #2598

Closed
christopherthielen opened this issue Mar 2, 2016 · 19 comments
Closed
Labels

Comments

@christopherthielen
Copy link
Contributor

UI-Router has had troubles encoding slashes in path parameters for quite a while now.

example:
Given: a url with parameters: /path/:param1/:param2
If: param1 is foo/bar and param2 is baz/qux
The browser URL should be: /path/foo%2Fbar/baz%2Fqux

We've gone through a few iterations to encode those slashes, including attempting double encoding (3045e41) and eventually a custom encoding (02e9866).

I wanted to revert any special handling of slashes, and simply url-encode/decode. I dug into this recently and I've come to the conclusion that it's actually impossible, using the $location service.

For posterity, I am leaving my notes here:


We are currently calling $location.url(url) to apply a new URL. The $location.url implementation decodes the path portion of the url before applying it as the $location.$$path. Naively passing a pre-encoded URL such as /path/foo%2Fbar/baz%2Fqux immediately get decoded to /path/foo/bar/baz/qux, the param separation are lost. Internally $$path is set to /path/foo/bar/baz/qux

Since .url() pre-decodes the incoming URL, we can't use it. Calling $location.path() directly seemed like a possible alternative since it applies the path without much processing. The $$path value is set the way we want with our parameters encoded: /path/foo%2Fbar/baz%2Fqux. However, then the $$url is built from $$compose() by calling encodePath which splits the path on the slashes, then encodes each segment. Because of this, $location.url() is double encoded as /path/foo%252%bar/baz%252Fquz.

screen shot 2016-03-01 at 8 53 08 pm

We landed on purposefully double encoding the slashes, but this caused quite a few issues (most linked to this ticket #1645).

As of 0.2.16, we're custom encoding slashes as ~2F (using the string typed parameter) so that the $location service doesn't try to handle them specially.

This is working fine for most of our users. If a user requires a slug (non-encoded slashes) we can support that using a different parameter type that doesn't explicitly encode/decode.


In query parameters, however, we also currently default to the string parameter type, which is unnecessarily encoding slashes, such as ?queryParam=foo~2Fbar. We should choose a different default parameter type for query parameters than for path parameters to avoid this. I don't have a ticket for this yet.

@ozzi-
Copy link

ozzi- commented May 3, 2016

Thank you so much! Now i can remove the dirty hack i have been using

@DavideCordella
Copy link

As far as I understod the actual issue is in the $location.url method.

I need to provide my clients with valid urls eg.: http://xyz.com/books/bookId%2FwithSlash/title , that can be shared.

If I just disable encoding/decoding of urls using a parameter custom type, the code %2F is translated to "/" in the address bar, but this leed to a different url from the one I am supposed to provide (with correct URI encoding).

How can this be fixed?

@christopherthielen
Copy link
Contributor Author

@Songissimo Unfortunately, If I remember correctly, thats the crux of this issue:

The $location.url implementation decodes the path portion of the url before applying it as the $location.$$path. Naively passing a pre-encoded URL such as /path/foo%2Fbar/baz%2Fqux immediately get decoded to /path/foo/bar/baz/qux, the param separation are lost. Internally $$path is set to /path/foo/bar/baz/qux

this might be impossible to fix when $location is being used.

@DavideCordella
Copy link

DavideCordella commented Nov 14, 2016

Then, do you think is problem on $location service? Why can't we use another function to set the URL? This is a crucial issue as make impossible to deliver valid URLs.

@christopherthielen
Copy link
Contributor Author

@Songissimo unfortunately I think the root problem is in $location service.

Why can't we use another function to set the URL?

The problem is primarily related to reading the URL. You might be able to work around the issue by monkey patching $location, but that's something that ui-router cannot support.

@christopherthielen
Copy link
Contributor Author

christopherthielen commented Dec 16, 2016

Update:

Here's a plunker showing various methods of setting the URL and how it's interpreted by angular 1: http://plnkr.co/edit/T34potPhkRLj6OyLOTKy?p=preview

Here's a issue to track refactoring of this ~2F code in ui-router-core: ui-router/core#14

This doesn't fix the issue with $location, but allows ng2 and react to avoid the weird encoding.

Ng1 Workaround

In ui-router 1.0 betas, you can have slashes in parameters by declaring a param type as { raw: true } but the behavior is undefined when decoding those values.

Example:

  $urlMatcherFactoryProvider.type('pageParam', {
    encode: (pageValue) => (pageValue ? ('page/' + pageValue) : ''),
    equals: (val1, val2) => (val1 === val2),
    decode: (paramString) =>  
        (paramString ? parseInt(/page\/(\d+)/.exec(paramString)[1]) : 0),
    pattern: /page\/\d+/,
    is: pageValue => (typeof pageValue === 'number'),
    raw: true
  });

http://plnkr.co/edit/2E2bdGZqncJKTJ1UGOLv?p=preview

@DavideCordella
Copy link

What do you mean by "behavior is undefined when decoding those values"?

@christopherthielen
Copy link
Contributor Author

It's basically a warning that using raw params is not guaranteed to work bi-directionally for arbitrary data.

For example, a url pattern like '/foo/:param1/:param2' with parameter values { param1: 'a/b', param2: 'c/d' } serializes fine to /foo/a/b/c/d but there's no way to deserialize it and extract the original param values.


Actually, I think the only guarantee we can make for raw is that ui-router will not url-encode the parameter value. We're still at the mercy of the $location service (in ng1) for example. Developers will be responsible to ensure their parameter values appear in the URL and are deserialized properly.

Here's a plunk showing some edge cases that I don't think we can perform successfully in ng1 : http://plnkr.co/edit/bjDtVkdHaXUZe3p6NGJ7?p=preview

@felixfbecker
Copy link

@christopherthielen any way to prevent %-encoding of query param values in ng1? Could ui-router circumvent $location?

@christopherthielen
Copy link
Contributor Author

It might be possible in ui-router 1.0+ by specifying raw: true

url: '/foo?param1',
params: {
 param1: { raw: true }
}

Can you try and report back?

@bobKasbi
Copy link

I'am facing the same issue and just none of the provided solution help.
.state('userData', { url: '/profile/:id', templateUrl: '../templates/profile/userData.jsp', controller: 'detailsCtrl' })
The URL looks so: http://....../index.jsp#!#%2Fprofile%2F954 instead: http://....../index.jsp#/profile/954

@felixfbecker
Copy link

@christopherthielen raw: true has no effect in ng1 because $location.$$compose %-encodes the URL.

@bobKasbi
Copy link

bobKasbi commented May 13, 2017

I fixed the encoding issue... just by passing the state in href with exclamation mark: !
e.g. <a href="#!/profile/">xxxxx</a>.
I'm not sure if some feature has been added/changed with ui-router. Without exclamation mark it causes the encoding issue.

@jbadeau
Copy link

jbadeau commented May 19, 2017

I have also noticed that the exclamation mark is also required

@abhijeetahuja
Copy link

abhijeetahuja commented Jul 3, 2017

replace
url: 'products/{slugPath:.*}'
with
url: 'products/{slugPath:any}'

@bobKasbi
Copy link

bobKasbi commented Jul 6, 2017

@abhijeetahuja: No way... before I tried it using exclamation mark '!', i tried almost all options with . None of them worked. Something is buggy out there.

@pmowrer
Copy link

pmowrer commented Mar 26, 2018

Hi @christopherthielen! We're stuck on ui-router 0.4.x and were hoping that angular 1.6.7+ would resolve this issue for us, but we're still seeing the problem. Do we need to be on ui-router 1.x for the angular fix to work?

@stale
Copy link

stale bot commented Jan 24, 2020

This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs.

This does not mean that the issue is invalid. Valid issues
may be reopened.

Thank you for your contributions.

@stale stale bot added the stale label Jan 24, 2020
@stale stale bot closed this as completed Feb 7, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

8 participants