使用Fetch GET请求设置查询字符串

465

我正在尝试使用新的Fetch API:

我正在以如下方式进行GET请求:

var request = new Request({
  url: 'http://myapi.com/orders',
  method: 'GET'
});
fetch(request);

然而,我不确定如何向GET请求中添加查询字符串。理想情况下,我希望能够向类似于以下URL的地址发出GET请求:

'http://myapi.com/orders?order_id=1'

jQuery 中,我可以通过将 {order_id: 1} 作为 $.ajax()data 参数来实现这一点。使用新的 Fetch API 是否有等效的方式?

14个回答

695
简洁、现代的方法:
fetch('https://example.com?' + new URLSearchParams({
    foo: 'value',
    bar: 2,
}))

工作原理:当一个字符串(例如URL)与URLSearchParams的实例连接时,它的toString()方法将自动被调用,将实例转换为字符串表示形式,这恰好是一个正确编码的查询字符串。如果自动调用toString()对您来说太神奇了,您可以选择显式调用它,如下所示:fetch('https://...' + new URLSearchParams(...).toString())

带有查询参数的fetch请求的完整示例:

// Real example you can copy-paste and play with.
// jsonplaceholder.typicode.com provides a dummy rest-api
// for this sort of purpose.
async function doAsyncTask() {
  const url = (
    'https://jsonplaceholder.typicode.com/comments?' +
    new URLSearchParams({ postId: 1 }).toString()
  );

  const result = await fetch(url)
    .then(response => response.json());

  console.log('Fetched from: ' + url);
  console.log(result);
}

doAsyncTask();


如果你正在使用/支持...
  • IE: Internet Explorer不支持URLSearchParams或fetch的本地支持,但有polyfills available

  • Node: 从Node 18开始,原生支持fetch API(在版本17.5中,它在--experimental-fetch标志后面)。在旧版本中,您可以通过像node-fetch这样的包添加fetch API。URLSearchParams随Node一起提供,并且自版本10以来可以作为全局对象找到。在旧版本中,您可以在require('url').URLSearchParams中找到它。

  • TypeScript: 提供给URLSearchParams的值将自动转换为字符串,这就是为什么TypeScript对URLSearchParams的定义要求您将所有值都提供为字符串的原因。不使用TypeScript的人可能会在此函数中提供其他值类型,如果他们认为强制转换行为是直观的。例如,数字将按预期行为运行,而数组的行为可能会对未来的读者造成困惑。

  • Node + TypeScript: 如果您同时使用Node和TypeScript,您会发现由于某些技术限制,TypeScript不提供全局URLSearchParams的类型定义。最简单的解决方法是从url模块中导入它。有关更多信息,请参见here


12
谢谢分享。我认为这应该是被接受的答案。问题是如何将参数传递给fetch API,虽然这不可能,但是这个答案在结构上非常接近于实现这个目标。 - Jesse Reza Khorasanee
1
这似乎不能正确处理同一键的多个值。我本来希望能够编写 new URLSearchParams({foo: ['bar', 'baz']}),但它并不是 foo=bar&foo=baz 而是被转义为 foo=bar%Cbaz - jymbob
2
要使@jymbob具有多个值,您必须在URLSearchParams上使用.append()方法。例如s = new URLSearchParams({foo: 'bar'}); s.append('foo', 'baz'); s.toString()。或者,构造函数可以接受一个列表而不是对象。new URLSearchParams([['foo', 'bar'], ['foo', 'baz']]).toString()请参阅文档页面以获取更多使用信息:https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams - Scotty Jamison
5
更加简洁的方法是使用字符串插值:`https://example.com?${new URLSearchParams({foo: 'value'})}`参考链接:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/template_strings - RavenHursT
7
请注意,在使用 TypeScript 时,您只能使用字符串作为值。因此 bar: 2 应改为 bar: '2' - Matthew Darton
显示剩余4条评论

347

2017年3月更新:

URL.searchParams 已正式在Chrome 51中得到支持,但其他浏览器仍需要 polyfill


与查询参数一起工作的官方方法就是将它们添加到URL上。根据规范,以下是一个例子:

var url = new URL("https://geo.example.org/api"),
    params = {lat:35.696233, long:139.570431}
Object.keys(params).forEach(key => url.searchParams.append(key, params[key]))
fetch(url).then(/* … */)

然而,我不确定Chrome是否支持URL的searchParams属性(截至撰写本文之时),因此您可能需要使用第三方库自己编写解决方案

更新于2018年4月:

通过使用URLSearchParams构造函数,您可以将2D数组或对象赋值给url.search,而不是循环遍历所有键并附加它们。

var url = new URL('https://sl.se')

var params = {lat:35.696233, long:139.570431} // or:
var params = [['lat', '35.696233'], ['long', '139.570431']]

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

fetch(url)

注意:在 NodeJS 中也可以使用 URLSearchParams

const { URL, URLSearchParams } = require('url');

1
还有https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams/toString,尽管在撰写本文时它仍在通过规范,并且尚未得到很好的支持。而且该API更像Java而不是JS。 :/ - ericsoco
1
请参考http://caniuse.com/#feat=urlsearchparams了解对`URLSearchParams`接口的支持情况;我会*假设*(尽管我不确定)那里标为红色的浏览器正是没有`.searchParams`属性的`URL`对象所适用的浏览器。重要的是,Edge仍然没有支持。 - Mark Amery
1
从文档中可以看到:"请注意,使用URLSearchParams实例已被弃用;不久浏览器将只使用USVString进行初始化。" 来源:https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams/URLSearchParams - pakman
4
使用new URLSearchParams似乎无法正确处理数组属性。我原本期望它将属性array: [1, 2]转换为array[]=1&array[]=2,但实际上它转换成了array=1,2。手动使用其append方法可以得到array=1&array=2的结果,但我需要迭代参数对象,并仅对数组类型执行此操作,这不是很方便。 - Guido Tarsia
1
确实是错误添加的 :) https://github.com/mdn/sprints/issues/2856 - CodingIntrigue
显示剩余8条评论

47
let params = {
  "param1": "value1",
  "param2": "value2"
};

let query = Object.keys(params)
             .map(k => encodeURIComponent(k) + '=' + encodeURIComponent(params[k]))
             .join('&');

let url = 'https://example.com/search?' + query;

fetch(url)
  .then(data => data.text())
  .then((text) => {
    console.log('request succeeded with JSON response', text)
  }).catch(function (error) {
    console.log('request failed', error)
  });

你需要对密钥进行编码吗?我从未这样做过。 - chovy
1
@chovy - 如果你的键包含特殊字符,比如"&",那么你需要这样做。大多数情况下不会出现这种情况,但有时候会出现。 - Scotty Jamison
注意:encodeURIComponent 可能会产生错误的结果,例如将空格错误地编码为“%20”而不是“+” - 当您进行百分比编码时,这种行为是可以接受的,例如 URL 的路径(一种预期的用例),但查询参数通常是表单编码的,这遵循旧版本的百分比编码规范 - 您可以使用 URLSearchParams() 来正确编码/解码查询参数。请参见 此 S.O. 问题 以了解更多信息。 - Scotty Jamison

33

正如已经回答的那样,在当前规范下,使用fetch API是不可能实现的。但是我必须指出:

如果你在使用node,则可以使用querystring包。它可以将对象/查询字符串转换为字符串和解析:

var querystring = require('querystring')
var data = { key: 'value' }
querystring.stringify(data) // => 'key=value'

...然后将其附加到url以请求。


然而,以上方法的问题在于,你总是需要在url前面添加一个问号 (?)。因此,另一种方法是使用Node.js的url包中的parse方法,并按照以下方式执行:

var url = require('url')
var data = { key: 'value' }
url.format({ query: data }) // => '?key=value'

请参见query

这是可能的,因为它内部实际上只是执行了此操作

search = obj.search || (
    obj.query && ('?' + (
        typeof(obj.query) === 'object' ?
        querystring.stringify(obj.query) :
        String(obj.query)
    ))
) || ''

13
你可以使用query-string中的stringify
import { stringify } from 'query-string';

fetch(`https://example.org?${stringify(params)}`)

10

encodeQueryString - 将对象编码为查询字符串参数

/**
 * Encode an object as url query string parameters
 * - includes the leading "?" prefix
 * - example input — {key: "value", alpha: "beta"}
 * - example output — output "?key=value&alpha=beta"
 * - returns empty string when given an empty object
 */
function encodeQueryString(params) {
    const keys = Object.keys(params)
    return keys.length
        ? "?" + keys
            .map(key => encodeURIComponent(key)
                + "=" + encodeURIComponent(params[key]))
            .join("&")
        : ""
}

encodeQueryString({key: "value", alpha: "beta"})
 //> "?key=value&alpha=beta"

9

我知道这听起来是非常显然的,但我觉得它值得成为一个答案,因为这是最简单的:

const orderId = 1;
fetch('http://myapi.com/orders?order_id=' + orderId);

13
值得说明的是,这仅适用于整数类型的可靠运作。如果使用字符串,尤其是用户提供的字符串(如搜索条件),则必须对字符串进行转义,否则如果出现 /+& 等字符,则可能会得到奇怪的结果。 - Malvineous
使用Request对象可以帮助你,特别是如果你想使用一个函数来构建请求,然后将其交给fetch()调用,但我不认为使用它是“绝对明显”的。此外,url不应该在配置选项的对象字面量中指定;它应该作为第一个参数单独传递给Request构造函数 (https://developer.mozilla.org/en-US/docs/Web/API/Request/Request)。 - Gen1-1
@Gen1-1,谢谢。我做了一些编辑。我的示例是基于OP的片段,但我稍微简化了一下。 - Evert

7
你可以在你的URL的searchParams上设置参数:
const url = new URL('http://myapi.com/orders');
url.searchParams.set('order_id', '1');
fetch(url);

这样做的好处是可以显式地构建请求,同时允许原始URL以任何有效格式存在。


在发表评论之前,我在Firefox中进行了测试,但它没有起作用。 - Quentin
1
这里有一个关于JSBin的在线演示,我在其中将fetch替换为console.log。当我在Firefox、Safari或Chrome中测试时,它会记录第一行的原始URL,而不包括你尝试添加的第二行查询字符串。 - Quentin
对于多个查询参数 const searchParams = new URLSearchParams({ one : '1' , two : '2' }); const url = new URL(${process.env.API_URL}/messages?${searchParams.toString()}); fetch(url); - Pash

5
也许这样更好:
const withQuery = require('with-query');

fetch(withQuery('https://api.github.com/search/repositories', {
  q: 'query',
  sort: 'stars',
  order: 'asc',
}))
.then(res => res.json())
.then((json) => {
  console.info(json);
})
.catch((err) => {
  console.error(err);
});

5

无需外部包的解决方案

使用fetch API执行GET请求,我编写了此解决方案,不需要安装包。

这是调用谷歌地图API的示例。

// encode to scape spaces
const esc = encodeURIComponent;
const url = 'https://maps.googleapis.com/maps/api/geocode/json?';
const params = { 
    key: "asdkfñlaskdGE",
    address: "evergreen avenue",
    city: "New York"
};
// this line takes the params object and builds the query string
const query = Object.keys(params).map(k => `${esc(k)}=${esc(params[k])}`).join('&')
const res = await fetch(url+query);
const googleResponse = await res.json()

请随意复制此代码并将其粘贴到控制台以查看其工作方式!

生成的URL类似于:

https://maps.googleapis.com/maps/api/geocode/json?key=asdkf%C3%B1laskdGE&address=evergreen%20avenue&city=New%20York

这正是我在决定写下这篇文章之前一直在寻找的内容,希望你能享受它 :D


谢谢Carlos。我忘记转义查询参数了。 - Jyoti Duhan
2
与三年半前Sudharshan提供的答案在功能上完全相同。 - Sebastian Simon
const esc = encodeURIComponent 的意义是什么,除了让代码更难阅读之外还有其他作用吗? - bfontaine

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接