如何使用Fetch发送x-www-form-urlencoded格式的POST请求?

360

我有一些参数想要以表单编码的方式POST到我的服务器:

{
    'userName': 'test@gmail.com',
    'password': 'Password!',
    'grant_type': 'password'
}

我像这样发送我的请求(目前没有参数)

var obj = {
  method: 'POST',
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
  },
};
fetch('https://example.com/login', obj)
  .then(function(res) {
    // Do stuff with result
  }); 

我该如何在请求中包含表单编码的参数?

17个回答

483

你需要自己组装 x-www-form-urlencoded 格式的负载,就像这样:

var details = {
    'userName': 'test@gmail.com',
    'password': 'Password!',
    'grant_type': 'password'
};

var formBody = [];
for (var property in details) {
  var encodedKey = encodeURIComponent(property);
  var encodedValue = encodeURIComponent(details[property]);
  formBody.push(encodedKey + "=" + encodedValue);
}
formBody = formBody.join("&");

fetch('https://example.com/login', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
  },
  body: formBody
})

请注意,如果您在(足够现代的)浏览器中使用的是fetch而不是React Native,则可以创建一个URLSearchParams对象并将其用作请求体。因为Fetch标准规定,如果请求体是一个URLSearchParams对象,则应将其序列化为application/x-www-form-urlencoded。但是,在React Native中无法这样做,因为React Native没有实现URLSearchParams


88
ES6的写法:const formBody = Object.keys(details).map(key => encodeURIComponent(key) + '=' + encodeURIComponent(details[key])).join('&');该行代码用于将JavaScript对象details转换为URL编码的字符串格式。具体实现是使用ES6中的箭头函数和模板字符串,以及Object.keys()方法和Array.prototype.map()方法对对象属性进行遍历和操作,最终通过Array.prototype.join()方法将结果拼接成一个字符串。 - Eric Burel
1
这个针对URLSearchParams的polyfill https://github.com/WebReflection/url-search-params 可能适用于React Native或旧版浏览器。 - bucabay
21
另一种类似的方法是:const formBody = Object.entries(details).map(([key, value]) => encodeURIComponent(key) + '=' + encodeURIComponent(value)).join('&') - Flynn Hou
2
它将JSON数组参数转换为字符串。 - atulkhatri
我已经尝试了所有建议的方法。无论我做什么,fetch都会在字符串中直接注入不必要的引号-开放和关闭引号。这会导致参数被解析,例如:'"mykey': 'myvalue"'。这使得调用API变得不可能,因为当然会导致400错误(服务器识别mykey而不是"mykey")。还有人遇到这个问题吗?令人困惑。 - Dave Munger

454

4
这不会自动添加头部 'Content-Type': 'application/x-www-form-urlencoded',对吗? - Lars Blumberg
2
在传递给 body 之前,您需要调用 .toString() - lasseschou
8
@lasseschou 不确定是否必要:fetch文档说body可以是一个URLSearchParams。 - alex_1948511
3
您也可以传递FormData对象,因此在使用HTML表单时,可以更简洁地写为new URLSearchParams(formData) - Adam
1
@B''H(上帝保佑),没有库,一切都内置。 - alex_1948511
显示剩余2条评论

80

10
现在它是React Native的一部分。在将数据传递给请求正文之前,请确保对其调用toString() - phatmann
1
即使RN表示他们已经实现了URLSearchParams,我仍然遇到问题。我认为它没有按照规范实现,并且它不是一个简单的解决方案。如果您尝试使用URLSearchParams并仍然遇到问题,请考虑阅读URLSearchParams 'Error: not implemented' - zero298

32

我刚做了这个,使用UrlSearchParams就解决了问题。如果有帮助的话,这是我的代码:

import 'url-search-params-polyfill';
const userLogsInOptions = (username, password) => {



// const formData = new FormData();
  const formData = new URLSearchParams();
  formData.append('grant_type', 'password');
  formData.append('client_id', 'XXXX-app');
  formData.append('username', username);
  formData.append('password', password);
  return (
    {
      method: 'POST',
      headers: {
        // "Content-Type": "application/json; charset=utf-8",
        "Content-Type": "application/x-www-form-urlencoded",
    },
      body: formData.toString(),
    json: true,
  }
  );
};


const getUserUnlockToken = async (username, password) => {
  const userLoginUri = `${scheme}://${host}/auth/realms/${realm}/protocol/openid-connect/token`;
  const response = await fetch(
    userLoginUri,
    userLogsInOptions(username, password),
  );
  const responseJson = await response.json();
  console.log('acces_token ', responseJson.access_token);
  if (responseJson.error) {
    console.error('error ', responseJson.error);
  }
  console.log('json ', responseJson);
  return responseJson.access_token;
};

用户登录选项的返回值为 json:true - JJS
不使用库是最好的方式。 - B''H Bi'ezras -- Boruch Hashem
记得用%20替换'+'。 - Erik Martino

23

不需要使用jQuery、querystring或手动组装负载。 URLSearchParams是一种方法,这里是一个最简洁的答案,带有完整的请求示例:

fetch('https://example.com/login', {
  method: 'POST',
  body: new URLSearchParams({
    param: 'Some value',
    anotherParam: 'Another value'
  })
})
  .then(response => {
    // Do stuff with the response
  });

使用 async / await 相同的技术。

const login = async () => {
  const response = await fetch('https://example.com/login', {
    method: 'POST',
    body: new URLSearchParams({
      param: 'Some value',
      anotherParam: 'Another value'
    })
  })

  // Do stuff with the response
}

是的,你可以使用 Axios 或者其他任何 HTTP 客户端库代替原生的 fetch


16
你可以使用UrlSearchParams,然后像这样执行toString():
以下是一个简单的方法:
fetch('https://example.com/login', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
    },
    body: new URLSearchParams({
        'userName': 'test@gmail.com',
        'password': 'Password!',
        'grant_type': 'password'
    })
    .toString()
})
.then(res => {
    //Deal with response:
})

1
应该是 new URLSearchParams(... - PDStat

12
var details = {
    'userName': 'test@gmail.com',
    'password': 'Password!',
    'grant_type': 'password'
};

var formBody = [];
for (var property in details) {
  var encodedKey = encodeURIComponent(property);
  var encodedValue = encodeURIComponent(details[property]);
  formBody.push(encodedKey + "=" + encodedValue);
}
formBody = formBody.join("&");

fetch('http://identity.azurewebsites.net' + '/token', {
  method: 'POST',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/x-www-form-urlencoded'
  },
  body: formBody
})

对我来说非常有帮助,而且没有任何错误。

参考:https://gist.github.com/milon87/f391e54e64e32e1626235d4dc4d16dc8


10

您可以使用FormDataURLSearchParams来像下面的示例一样以application/x-www-form-urlencoded的格式进行提交:

如果您有一个表单:

<form>
    <input name="username" type="text" />
    <input name="password" type="password" />
    <button type="submit">login</button>
</form>

你可以使用下面的JS代码提交表单。

const form = document.querySelector("form");

form.addEventListener("submit", async () => {
    const formData = new FormData(form);
    try {
        await fetch("https://example.com/login", {
            method: "POST",
            headers: {
                "Content-Type": "application/x-www-form-urlencoded",
            },
            body: new URLSearchParams(formData),
        });
    } catch (err) {
        console.log(err);
    }
});

3
请注意,您无需设置 content-type 标头。请参见 此答案 - Phil

8
*/ import this statement */
import qs from 'querystring'

fetch("*your url*", {
            method: 'POST',
            headers: {'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'},
            body: qs.stringify({ 
                username: "akshita",
                password: "123456",
            })
    }).then((response) => response.json())
      .then((responseData) => {
         alert(JSON.stringify(responseData))
    })

在使用 npm i querystring --save 后,它能正常工作。


4

只需使用

import  qs from "qs";
 let data = {
        'profileId': this.props.screenProps[0],
        'accountId': this.props.screenProps[1],
        'accessToken': this.props.screenProps[2],
        'itemId': this.itemId
    };
    return axios.post(METHOD_WALL_GET, qs.stringify(data))

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