如何在一段URL中插入參數

有一個很簡單的需求以下:javascript

給你一段URL,在其中插入一些參數,並返回新的URL,如何實現?html

這裏的參數,有時候稱爲query,有時候稱爲params,通常稱爲search,指的是vue

http://www.example.com:80/path/to/myfile.html?key1=value1&key2=value2#SomewhereInTheDocumentjava

中的key1key2ios

最近正在對接阿里雲的金融級實人認證,在傳遞認證成功回調頁時就遇到了這樣一個問題。vue-router

第一層: 直接拼

import qs from 'query-string'
function resolve (url) {
  const params = qs.stringify({ a: 1, b: 2 }) // a=1&b=2
  return url + '?' + params
}

實際業務中,90%的場景這樣寫沒問題,但若是url的值是這樣的:axios

http://taobao.com/?c=3&d=4

最終結果就是後端

http://taobao.com/?c=3&d=4?a=1&b=2

將這段URL中的search解析,獲得的結果是api

{
  c: '3',
  d: '4?a=1',
  b: '2',
}

顯然不符合預期。ui

第二層: 兼容已存在的search

import qs from 'query-string'
function resolve (url) {
  const params = qs.stringify({ a: 1, b: 2 }) // a=1&b=2
  return url + url.includes('?') ? '&' : '?' + params
}

看上去問題彷佛解決了,不少人也只考慮到這一層,但如今還有這樣一種url:

http://taobao.com/#/xxx

尤爲是單頁應用,這個形式的hash路由很是常見。

若是隻是簡單地拼接到URL尾部:

http://taobao.com/#/xxx?a=1&b=2

將這段URL中的search解析,獲得的結果是

{}

能夠看到拼接的參數根本沒跑到search裏面去。

也就是說,只要URL中出現了#,這以後出現的?就不會被視做search的起始標誌,而是hash的一部分。

若是URL再複雜一點,好比:

http://taobao.com/?c=3&d=4#/xxx

上面的拼接會變成:

http://taobao.com/?c=3&d=4#/xxx&a=1&b=2

不但沒有按照預期插入參數,還破壞了本來的hash結構。

實際上,以vue-router爲例,它的路由系統中剛好就用到了hash中的?

好比:

http://example.com/user/:foo/info?c=3&d=4#/xxx?a=1&b=2

在vue-router裏,xxx是路由的path,foo被稱做params,ab被稱做query,分別能夠經過
route.paramsroute.query獲取。

#以後能夠接任意字符串,?a=1&b=2只是路由本身定義的一套內部規則,爲路由傳參服務。

cd纔是search,須要從location.search中解析。

若是後端接收了這樣一段GET請求,hash後面的東西都會被拋棄,只有search能夠被接收和解析。

所以,插入參數不能出如今#以後,也就是說,簡單地在URL後面拼接字符串是不行的。

第三層: 只處理search

import qs from 'query-string'
function resolve (url) {
  const params = qs.stringify({ a: 1, b: 2 }) // a=1&b=2
  const urlObj = new URL(url)
  urlObj.search += urlObj.search.startsWith('?') ? '&' : '?' + params
  return urlObj.href
}

這樣就能夠處理上面的複雜狀況了。

使用URL對象是一個討巧的辦法,將url字符串解析爲URL對象後,能夠只修改它的search屬性而不影響其餘部分。

axios的源碼中,咱們能夠看到另外一種標準實現:

function buildURL(url, params) {
  if (!params) {
    return url;
  }
  
  // params序列化過程略
  var hashmarkIndex = url.indexOf('#');
  if (hashmarkIndex !== -1) {
    url = url.slice(0, hashmarkIndex);  
  }
 
  url += (url.indexOf('?') === -1 ? '?' : '&') + params;

  return url;
};

思路很簡單,就是把序列化後的params插入到URL的末尾,但若存在hash,則插到hash以前,若存在search,則鏈接符改用&

相關連接:

What is a URL

URL對象

Vue-router 路由對象

相關文章
相關標籤/搜索