Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Different output from encodeURIComponent vs URLSearchParams

I built an oauth2 url with query params using URLSearchParmas API. However, the output URL didn't return an expected url. Can anyone help me understand the difference between those two APIs and how can I get the same result as the output of encodeURIComponent, using URLSearchParams? Thanks!

const expected = encodeURIComponent('code id_token'); // code%20id_token

const search = new URLSearchParams();
search.set('response_type', 'code id_token');
search.toString(); // code+id_token
like image 252
supergentle Avatar asked Jan 24 '20 01:01

supergentle


People also ask

What is the difference between encodeURI and encodeURIComponent?

encodeURI and encodeURIComponent are used for different purposes. encodeURI is used to encode a full URL whereas encodeURIComponent is used for encoding a URI component such as a query string. There are 11 characters which are not encoded by encodeURI, but encoded by encodeURIComponent. List: ? ? encodeURIComponent does not encode -_.!~*' ().

What is the difference between decodeuri and decodeuricomponent in PHP?

decodeURI (): It takes encodeURI (url) string as parameter and returns the decoded string. decodeURIComponent (): It takes encodeURIComponent (url) string as parameter and returns the decoded string.

Why do I need to encode a character in a URL?

URLs can only have certain characters from the standard 128 character ASCII set. Reserved characters that do not belong to this set must be encoded. This means that we need to encode these characters when passing into a URL.

What is the difference between escape() and encodeURI() methods?

Use of the encodeURI () method is a bit more specialized than escape () in that it encodes for URIs as opposed to the querystring, which is part of a URL. Use this method when you need to encode a string to be used for any resource that uses URIs and needs certain characters to remain un-encoded.


1 Answers

According to WHATWG, URLSearchParams uses application/x-www-form-urlencoded format. While it's suitable for decoding URL queries, for encoding it can lead to unexpected results such as spaces being encoded as + and extra characters such as ~ being percent-encoded. It's better to use encodeURIComponent instead:

Having an object:

const params = {
  text1: 'aaa bbb',
  text2: '-._*~()'
}

Instead of:

url.search = (new URLSearchParams(params)).toString()

Use:

url.search = Object.entries(params)
  .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
  .join('&')

Also, according to MDN even encodeURIComponent doesn't conform to newer RFC 3986 which defines more characters to escape, for example *. While it's probably safe not to escape these additional characters if you aren't using them as field separators, if you want to be strictly conformant to latest RFC, use this updated implementation from MDN:

function fixedEncodeURIComponent(str) {
  return encodeURIComponent(str).replace(/[!'()*]/g, function(c) {
    return '%' + c.charCodeAt(0).toString(16).toUpperCase();
  });
}

A playground for experimenting:

const params = {
  text1: 'aaa bbb',
  text2: '-._*~()'
}

const url1 = new URL('http://example.com')
const search1 = new URLSearchParams(params)
url1.search = search1 // Incorrect
console.log('URLSearchParams', url1.toString())

function fixedEncodeURIComponent(str) {
    return encodeURIComponent(str).replace(/[!'()*]/g, function(c) {
        return '%' + c.charCodeAt(0).toString(16).toUpperCase()
    })
}
const url2 = new URL('http://example.com')
const search2 = Object
  .entries(params)
  .map(([key, value]) => `${fixedEncodeURIComponent(key)}=${fixedEncodeURIComponent(value)}`)
  .join('&')
url2.search = search2 // Correct
console.log('fixedEncodeURIComponent', url2.toString())
like image 91
Zmey Avatar answered Nov 04 '22 17:11

Zmey