encodeURIComponent 与 URLSearchParams 的不同输出

Different output from encodeURIComponent vs URLSearchParams

我使用 URLSearchParmas API 构建了一个带有查询参数的 oauth2 url。但是,输出 URL 没有 return 预期的 url。谁能帮助我理解这两个 API 之间的区别,以及如何使用 URLSearchParams 获得与 encodeURIComponent 的输出相同的结果?谢谢!

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

根据 WHATWGURLSearchParams 使用 application/x-www-form-urlencoded 格式。虽然它适用于解码 URL 查询,但对于编码它可能会导致意想不到的结果,例如空格被编码为 + 和额外字符(例如 ~ 被百分比编码)。最好使用 encodeURIComponent 代替:

有对象:

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

而不是:

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

使用:

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

另外,根据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();
  });
}

试验场:

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())