vuejs/vue-router

Routes with non-ascii characters are not matched in SSR

Opened this issue · 4 comments

ejez commented

Version

3.1.5

Reproduction link

https://github.com/ejez/vue-router-ssr-issue

Steps to reproduce

  1. Create a basic vue project with ssr and router:
git clone https://github.com/ejez/vue-router-ssr-issue
cd vue-router-ssr-issue && yarn
  1. start project:
yarn start
  1. open following link in browser:
    http://localhost:8080/à-propos

What is expected?

Route should match and browser should show page contents

What is actually happening?

404 error in terminal


When on client, the previous route is matched and works normally, this can be checked by initially visiting http://localhost:8080 , and then clicking on the link "Go To à-propos".

Another route without "non-ascii characters" is provided to show that it works normally both in ssr and client:
http://localhost:8080/about

posva commented

I'm closing #838 in favor of this.

As a note (because that issue is quite outdated). URL have specific encoding rules that make a non valid url. The valid url is its url-encoded version /%C3%A9. Most browsers automatically encode the url before sending the request (as it should be) and also depending of the part of the request, but some don't (Internet Explorer and Safari do not always encode what should be), giving an invalid URL to your server. For the moment, you have will have to add an alias to your record with the decoded version, that way, it will work even when the URL is invalid:

{ path: '/%C3%A9', alias: '/é' }

However, this shouldn't be necessary because a URL like is a malformed URL and should not be sent to a server. When writing the URL directly into the address bar, it is automatically encoded by the browser before sending it

A possible workaround to this problem is to automatically duplicate the records, one encoded, and the other one decoded

ejez commented

Thanks @posva for the quick reply,

Vue-router should be able to handle both encoded and decoded url versions.

As the server might receive the encoded version, then vue-router should compare/match the request url with the declared route path after encoding it also.

Example:

route:

{ path: '/é', component: MyComponent }

Requested url: http://localhost:8080/é

vue-router running in node might receive http://localhost:8080/%C3%A9

The comparison/matching done by vue-router should be done also between http://localhost:8080/%C3%A9 and path: '/%C3%A9' (ie: encoded version of route path)

This will result in successful matching instead of 404.

The current ssr behaviour is as follows:

if route is: { path: '/é', component: MyComponent }

both curl -v http://localhost:8080/é and curl -v http://localhost:8080/%C3%A9 fail with 404

if route is: { path: '/%C3%A9', component: MyComponent }

curl -v http://localhost:8080/é fails, and curl -v http://localhost:8080/%C3%A9 succeeds.

However when in the browser, route { path: '/%C3%A9', component: MyComponent } will give error: The client-side rendered virtual DOM tree is not matching server-rendered content

I suggest modification of vue-router code to cater for this issue by comparing the request url with both versions of the route path (encoded and decoded) and if either one matches then route should be used. (considering vue-router code that runs in server and also code that runs in client)

Suggested workaround (alias or duplication) works, but cumbersome.

I'll be happy to submit a PR if you help me locate the relevant code.

Supporting non-ascii urls is important cos most of the people in the world are non-english speakers!

ejez commented

Another thinking might be:
in file src/create-route-map.js check if the encoded route.path is different from route.path (ie: non-ascii characters exist) then add an alias.

ejez commented

However, this shouldn't be necessary because a URL like /é is a malformed URL

As mentioned above, using only { path: '/%C3%A9', component: MyComponent } (without alias or duplication) will result in hydration error The client-side rendered virtual DOM tree is not matching server-rendered content.

I assume this is because the server is receiving /%C3%A9 while the javascript client code is getting

So at the moment we are forced to add aliases or duplicates which is cumbersome and counter-productive (and might add a performance impact for large number of routes)

As a side note, for code readability we prefer to use é not /%C3%A9 in route path definitions, and let vue-router handle needed encoding.

@posva can you clarify if this would be easy to implement or there is some complexities that should be considered.