344

I have some parameters that I want to POST form-encoded to my server:

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

I'm sending my request (currently without parameters) like this

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
  }); 

How can I include the form-encoded parameters in the request?

Paolo
  • 20,112
  • 21
  • 72
  • 113
texas697
  • 5,609
  • 16
  • 65
  • 131

17 Answers17

476

You have to put together the x-www-form-urlencoded payload yourself, like this:

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

Note that if you were using fetch in a (sufficiently modern) browser, instead of React Native, you could instead create a URLSearchParams object and use that as the body, since the Fetch Standard states that if the body is a URLSearchParams object then it should be serialised as application/x-www-form-urlencoded. However, you can't do this in React Native because React Native does not implement URLSearchParams.

Mark Amery
  • 143,130
  • 81
  • 406
  • 459
Narongdej Sarnsuwan
  • 5,095
  • 1
  • 12
  • 10
  • 84
    The ES6 way : `const formBody = Object.keys(details).map(key => encodeURIComponent(key) + '=' + encodeURIComponent(details[key])).join('&');` – Eric Burel Feb 03 '17 at 09:57
  • 1
    This polyfill for URLSearchParams https://github.com/WebReflection/url-search-params may work for React Native or older browsers. – bucabay Feb 13 '18 at 07:11
  • 19
    Another similar way: `const formBody = Object.entries(details).map(([key, value]) => encodeURIComponent(key) + '=' + encodeURIComponent(value)).join('&')` – Flynn Hou Aug 26 '18 at 04:03
  • 2
    It converts the json array parameter to string – atulkhatri Oct 02 '18 at 14:02
  • I have tried all of the suggested methods. No matter what I do, fetch injects unwanted quotes around the body, directly into the string - opening and closing quotes. This causes the params to be parsed, e.g. like this: '"mykey': 'myvalue"'. Which makes calling APIs impossible, as these of course just result in 400 errors (server recognizes mykey, not "mykey). Anybody else have this issue? Baffling. – Dave Munger Dec 15 '20 at 16:46
433

Even simpler:

fetch('https://example.com/login', {
    method: 'POST',
    headers:{
      'Content-Type': 'application/x-www-form-urlencoded'
    },    
    body: new URLSearchParams({
        'userName': 'test@gmail.com',
        'password': 'Password!',
        'grant_type': 'password'
    })
});

Docs: https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch

alex_1948511
  • 6,073
  • 2
  • 20
  • 21
80

Use URLSearchParams

https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams

var data = new URLSearchParams();
data.append('userName', 'test@gmail.com');
data.append('password', 'Password');
data.append('grant_type', 'password');
Nicu Criste
  • 3,072
  • 1
  • 19
  • 9
  • 10
    It is part of React Native now. Be sure to call `toString()` on the data before passing it request `body`. – phatmann Oct 08 '18 at 20:58
  • 1
    Even after RN said that they implemented `URLSearchParams`, I'm still having issues. I don't think it's implemented in accordance with the spec and it isn't just a drop in solution. Please consider reading [URLSearchParams 'Error: not implemented'](https://github.com/facebook/react-native/issues/23922) if you try to drop in `URLSearchParams` and still have issues. – zero298 Sep 18 '19 at 18:20
30

Just did this and UrlSearchParams did the trick Here is my code if it helps someone

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;
};
P-A
  • 1,161
  • 12
  • 16
23

No need to use jQuery, querystring or manually assemble the payload. URLSearchParams is a way to go and here is one of the most concise answers with the full request example:

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

The same technique using 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
}

Yes, you can use Axios or any other HTTP client library instead of native fetch.

Neurotransmitter
  • 6,289
  • 2
  • 51
  • 38
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
})

it is so helpful for me and works without any error

refrence : https://gist.github.com/milon87/f391e54e64e32e1626235d4dc4d16dc8

mahsa k
  • 555
  • 6
  • 9
12

You can use UrlSearchParams and then do a toString() like so:

Here is a simple way of doing it:

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:
})
John Franke
  • 1,444
  • 19
  • 23
10

You can use FormData and URLSearchParams to post as application/x-www-form-urlencoded with the example below:

If you have a form:

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

You can add use the JS below to submit the form.

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);
    }
});
ikhvjs
  • 5,316
  • 2
  • 13
  • 36
  • 3
    FYI you don't need to set the `content-type` header. See [this answer](https://stackoverflow.com/a/68643919/283366) – Phil May 18 '22 at 02:12
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))
    })

After using npm i querystring --save it's work fine.

Akshita Agarwal
  • 235
  • 3
  • 4
4

Just Use

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))
Mojtaba Shayegh
  • 371
  • 3
  • 8
4

If you are using JQuery, this works too..

fetch(url, {
      method: 'POST', 
      body: $.param(data),
      headers:{
        'Content-Type': 'application/x-www-form-urlencoded'
      }
})
wishy
  • 1,748
  • 2
  • 12
  • 14
  • With ```$.param({value: function(){return 'values'; }})``` it is possible to pass functions as parameters. – Ever CR Jul 13 '22 at 18:50
3

According to the spec, using encodeURIComponent won't give you a conforming query string. It states:

  1. Control names and values are escaped. Space characters are replaced by +, and then reserved characters are escaped as described in [RFC1738], section 2.2: Non-alphanumeric characters are replaced by %HH, a percent sign and two hexadecimal digits representing the ASCII code of the character. Line breaks are represented as "CR LF" pairs (i.e., %0D%0A).
  2. The control names/values are listed in the order they appear in the document. The name is separated from the value by = and name/value pairs are separated from each other by &.

The problem is, encodeURIComponent encodes spaces to be %20, not +.

The form-body should be coded using a variation of the encodeURIComponent methods shown in the other answers.

const formUrlEncode = str => {
  return str.replace(/[^\d\w]/g, char => {
    return char === " " 
      ? "+" 
      : encodeURIComponent(char);
  })
}

const data = {foo: "bar߃©˙∑  baz", boom: "pow"};

const dataPairs = Object.keys(data).map( key => {
  const val = data[key];
  return (formUrlEncode(key) + "=" + formUrlEncode(val));
}).join("&");

// dataPairs is "foo=bar%C3%9F%C6%92%C2%A9%CB%99%E2%88%91++baz&boom=pow"
papiro
  • 2,158
  • 1
  • 20
  • 29
3

Just set the body as the following

var reqBody = "username="+username+"&password="+password+"&grant_type=password";

then

fetch('url', {
      method: 'POST',
      headers: {
          //'Authorization': 'Bearer token',
          'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
      },
      body: reqBody
  }).then((response) => response.json())
      .then((responseData) => {
          console.log(JSON.stringify(responseData));
      }).catch(err=>{console.log(err)})
2

You can use react-native-easy-app that is easier to send http request and formulate interception request.

import { XHttp } from 'react-native-easy-app';

* Synchronous request
const params = {name:'rufeng',age:20}
const response = await XHttp().url(url).param(params).formEncoded().execute('GET');
const {success, json, message, status} = response;


* Asynchronous requests
XHttp().url(url).param(params).formEncoded().get((success, json, message, status)=>{
    if (success){
       this.setState({content: JSON.stringify(json)});
    } else {
       showToast(msg);
    }
});
rufeng
  • 121
  • 4
1

In the original example you have a transformRequest function which converts an object to Form Encoded data.

In the revised example you have replaced that with JSON.stringify which converts an object to JSON.

In both cases you have 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' so you are claiming to be sending Form Encoded data in both cases.

Use your Form Encoding function instead of JSON.stringify.


Re update:

In your first fetch example, you set the body to be the JSON value.

Now you have created a Form Encoded version, but instead of setting the body to be that value, you have created a new object and set the Form Encoded data as a property of that object.

Don't create that extra object. Just assign your value to body.

Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335
  • 1
    Hi @Quentin. I've just radically slimmed down the question in order to try and make it into a more useful reference for future readers; in doing so, I've entirely invalidated your answer which refers to the details and bugs of the asker's original code. I suppose you have the right to revert my edit if you want to - in theory, we're not meant to make answer-invalidating edits, which is what I've done - but if you'd be willing to, I think it'd be better to just delete this answer instead; IMO the question is much nicer without the Angular code or previous failed attempt. – Mark Amery Dec 25 '17 at 13:46
1

wrapped fetch in a simple function

async function post_www_url_encdoded(url, data) {
    const body = new URLSearchParams();
    for (let key in data) {
        body.append(key, data[key]);
    }
    return await fetch(url, { method: "POST", body });
}

const response = await post_www_url_encdoded("https://example.com/login", {
    "name":"ali",
    "password": "1234"});
if (response.ok){ console.log("posted!"); }

Ali80
  • 6,333
  • 2
  • 43
  • 33
-69

For uploading Form-Encoded POST requests, I recommend using the FormData object.

Example code:

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

var formData = new FormData();

for (var k in params) {
    formData.append(k, params[k]);
}

var request = {
    method: 'POST',
    headers: headers,
    body: formData
};

fetch(url, request);
David Kay
  • 1,006
  • 10
  • 16
  • 90
    This is not application/x-www-form-urlencoded, but multipart/form-data – Haha TTpro May 31 '16 at 02:55
  • I concur, this request won't have "application/x-www-form-urlencoded" as Content-Type but "multipart/form-data". – b4stien Jul 01 '16 at 12:21
  • What different would it make when it comes to the server actually finding the credentials sent? Is the OAuth end point designed to accept one content type and reject others? – Lzh Nov 28 '16 at 10:02
  • 2
    @Mzn - For example if you're using a service like Google's [Closure Compiler API](https://developers.google.com/closure/compiler/docs/api-tutorial1), the server will only accept `application/x-www-form-urlencoded`, not `multipart/form-data`. – Sphinxxx Mar 16 '17 at 15:33
  • 2
    You'll have to do extra processing on the server, when submitting FormData objects. Basically process a regular form as if it were a file upload. What is the advantage of FormData objects for regular forms? – Kalnode Feb 01 '18 at 00:29
  • FormData should be used for uploading files mostly. Not for basic form requests. – Puerto Jun 13 '18 at 13:26