3

I need to programmatically submit multiple values to a POST (in this example, US states), using node.js and Request.

For example, the HTML form might be be

<select name="stateprov[]" id="stateprov" multiple="multiple" >

followed by 50 options..., one per state

And the submitted form data would look like

stateprov%5B%5D=CA&stateprov%5B%5D=WI

How can I do this using Request? Given that I have an array of states, ['CA','WI'}, I've tried

form['stateprov[]'] = states  
   fails 
it generates stateprov%5B%5D[0]=WI&stateprov%5B%5D[1]=CA as the output

form['stateprov[]'] = states.join(',') doesn't work either

BTW, Node people, I'm really trying to like the project, there's a lot of cool things, but your documentation is less than great.

Followup: I think the problem might be that Request (https://npmjs.org/package/request) uses qs (https://npmjs.org/package/qs) to encode the form data, and it adds the extraneous [0] and [1]. Node's built in queryString (http://nodejs.org/api/querystring.html#querystring_querystring_stringify_obj_sep_eq) does the encoding that I want.

Followup #2: Chatted with Mikeal Rogers who does a great job supporting Request, and he basically said that I can't do it this way in Request. Since I'm not exploiting many of the cool features of Request I'll look at the more basic http.

1
  • Are you doing this with HTTP only or using a module like express? Commented Feb 3, 2014 at 20:48

4 Answers 4

17

Stumbled on this issue and never got it to work using restler. I did find that it works using the npm module 'request' Just do

import request from 'request';
let data = { subject: 'a message', recipients:['[email protected]', '[email protected]'] }
// define your data above. I was having issues with the recipients needing to repeat
let options = {
  form: data, qsStringifyOptions: {arrayFormat: 'repeat'}
}
request.post(url, options, function(err, res, body){
   //callback. note request sends 3 params to callback 
})

I wrapped this inside of the Q library to make promises. Worked out well. A bit of a pain because I needed to switch libraries, but hopefully this helps soemone who stumbles on this later.

Sign up to request clarification or add additional context in comments.

5 Comments

You are a true life savior, qsStringifyOptions: {arrayFormat: 'repeat'} option had not been documented anywhere and it just works like a charm!
In case anyone arrives here using request-promise qsStringifyOptions: {arrayFormat: 'repeat'} needs to be passed at the same level of qs as stated in the request official documentation github.com/request/request#requestoptions-callback
More than a year later, and this solution still applies well... it worked like a champ for an issue I had been fighting. Already using Request, but was not aware of the full option variants available with qsStringifyOptions. Thank you.
Good to know the hidden gem, qsStringifyOptions: {arrayFormat: "brackets"}. Useful when shipping arrays in form data.
This saved my artillery tests! I needed to call an API passing mutiple values for the same param, adding the qsStringifyOptions in the beforeRequest function did the trick.
1

Sorry to answer my own question, but in case others run into this issue...

Mikeal Rogers is of course right. Request uses the npm package qs (https://npmjs.org/package/qs) as his query string parser, and, for better or worse, when it "stringifies" an array it adds '[n]'.

function stringifyArray(arr, prefix) {
  var ret = [];
  if (!prefix) throw new TypeError('stringify expects an object');
  for (var i = 0; i < arr.length; i++) {
    ret.push(stringify(arr[i], prefix + '[' + i + ']'));  <<< see here
  }
  return ret.join('&');
}

So a form with multiple values would look like:

foo[0]=value0&foo[1]=value1

Maybe this is what you want, but it's not what I want, and this seems to mismatch normal HTML form behavior. My HTML experience is limited, so this may be wrong:-)

It turns out that node's built in querystring.stringify does what I want, outputting

foo=value0&foo=value1

The quick hack is to change one line in Request.form() (roughly line 974)

this.body = *querystring*.stringify(form).toString('utf8')

However, anytime you do an update, you'll have to remember to do this again. Not robust. The "proper" way is to subclass. It took me a while to find one little gotcha - you cannot require('request'), because that brings in index.js, which exports the lowercase factory request() method. The "real" uppercase with a new constructor is in request.js. So you must be specific: require('request/request.js')

Here's the code: (also at https://gist.github.com/MorganConrad/8827916)

var Request = require('request/request.js');  // IMPORTANT - specify request.js, don't get index.js!!!
var querystring = require('querystring');

MyRequest.prototype = Object.create(Request.prototype);
MyRequest.prototype.constructor = MyRequest;

function MyRequest(options, callbackfn) {
  "use strict";
  if (callbackfn)
    options.callback = callbackfn;
  options.method = options.method || 'POST';
  Request.prototype.constructor.call(this, options);
}

MyRequest.prototype.form = function (form) {
  "use strict";
  if (form) {
    this.setHeader('content-type', 'application/x-www-form-urlencoded; charset=utf-8');
    this.body = querystring.stringify(form).toString('utf8');
    return this;
  }

  else
    return Request.prototype.form.apply(this, arguments);
};


module.exports = MyRequest;

3 Comments

In case anyone stumbles upon this, as I have, it looks like qs now has an indices: false option which disables this behaviour. Not sure if that's useful or not...
In fact, combining qsStringifyOptions in request and arrayFormat: repeat it should now be possible to do this without any modification of request.
useQuerystring: true, this is all you need now.
0

you can simply add a hidden input for each piece of data

<input type="hidden" name="anyData" value="data" ></input>

don't forget the input closing tag.

Comments

-1
var querystring = require('querystring');
var form = {
//form parameters
}
request.post(http + querystring.stringify(form), callback);

Declare the form object outside of request. Then, call querystring.stringify(form) which returns the desired string, and just append that string to the url. Coding the url this manual way avoids child classes and the update problem.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.