diff --git a/README.md b/README.md index 16a0673..5cbba89 100644 --- a/README.md +++ b/README.md @@ -23,10 +23,83 @@ request('http://www.google.com', function (error, response, body) { ## Table of contents +- [Forms](#forms) - [Custom HTTP Headers](#custom-http-headers) - [Unix Domain Sockets](#unix-domain-sockets) - [**All Available Options**](#requestoptions-callback) +## Forms + +`urequest` supports `application/x-www-form-urlencoded` and `multipart/form-data` form uploads. + + +#### application/x-www-form-urlencoded (URL-Encoded Forms) + +URL-encoded forms are simple. + +```js +request.post('http://service.com/upload', {form:{key:'value'}}) +// or +request.post({url:'http://service.com/upload', form: {key:'value'}}, function(err,httpResponse,body){ /* ... */ }) +``` + + + +#### multipart/form-data (Multipart Form Uploads) + +For `multipart/form-data` we use the [form-data](https://github.com/form-data/form-data) library by [@felixge](https://github.com/felixge). For the most cases, you can pass your upload form data via the `formData` option. + + +```js +var formData = { + // Pass a simple key-value pair + my_field: 'my_value', + // Pass data via Buffers + my_buffer: Buffer.from([1, 2, 3]), + // Pass data via Streams + my_file: fs.createReadStream(__dirname + '/unicycle.jpg'), + // Pass multiple values /w an Array + attachments: [ + fs.createReadStream(__dirname + '/attachment1.jpg'), + fs.createReadStream(__dirname + '/attachment2.jpg') + ], + // Pass optional meta-data with an 'options' object with style: {value: DATA, options: OPTIONS} + // Use case: for some types of streams, you'll need to provide "file"-related information manually. + // See the `form-data` README for more information about options: https://github.com/form-data/form-data + custom_file: { + value: fs.createReadStream('/dev/urandom'), + options: { + filename: 'topsecret.jpg', + contentType: 'image/jpeg' + } + } +}; +request.post({url:'http://service.com/upload', formData: formData}, function optionalCallback(err, httpResponse, body) { + if (err) { + return console.error('upload failed:', err); + } + console.log('Upload successful! Server responded with:', body); +}); +``` + + +See the [form-data README](https://github.com/form-data/form-data) for more information & examples. + ## Custom HTTP Headers HTTP Headers, such as `User-Agent`, can be set in the `options` object. diff --git a/examples/form-data.js b/examples/form-data.js index 8edfe7f..59d2f1d 100644 --- a/examples/form-data.js +++ b/examples/form-data.js @@ -24,7 +24,6 @@ request( return; } console.log('statusCode:', response.statusCode); // The final statusCode - console.log('Final href:', response.request.uri.href); // The final URI console.log('Body Length:', body.length); // body length } ); diff --git a/examples/www-form.js b/examples/www-form.js new file mode 100644 index 0000000..9b40991 --- /dev/null +++ b/examples/www-form.js @@ -0,0 +1,23 @@ +'use strict'; + +//var request = require('@coolaj86/urequest'); +// To check and make sure the outputs are the same +//var request = require('request'); +var request = require('../'); + +// will redirect to https://www.github.com and then https://github.com +//request('http://www.github.com', function (error, response, body) { +request( + { url: 'http://postb.in/2meyt50C' + , headers: { 'X-Foo': 'Bar' } + , form: { foo: 'bar', baz: 'qux' } + } +, function (error, response, body) { + if (error) { + console.log('error:', error); // Print the error if one occurred + return; + } + console.log('statusCode:', response.statusCode); // The final statusCode + console.log('Body Length:', body.length); // body length + } +); diff --git a/index.js b/index.js index 96f6cf0..78287c3 100644 --- a/index.js +++ b/index.js @@ -163,6 +163,15 @@ function setDefaults(defs) { } } else if (opts.json && true !== opts.json) { _body = JSON.stringify(opts.json); + } else if (opts.form) { + _body = Object.keys(opts.form).filter(function (key) { + if ('undefined' !== typeof opts.form[key]) { + return true; + } + }).map(function (key) { + return encodeURIComponent(key) + '=' + encodeURIComponent(String(opts.form[key])); + }).join('&'); + opts.headers['Content-Type'] = 'application/x-www-form-urlencoded'; } if ('string' === typeof _body) { _body = Buffer.from(_body); @@ -175,8 +184,8 @@ function setDefaults(defs) { finalOpts.headers = opts.headers; if (_body) { // Most APIs expect (or require) Content-Length except in the case of multipart uploads - // chunked is generally only well-supported downstream - //finalOpts.headers['Content-Length'] = _body.byteLength || _body.length; + // Transfer-Encoding: Chunked (the default) is generally only well-supported downstream + finalOpts.headers['Content-Length'] = _body.byteLength || _body.length; } if (opts.formData) { try { @@ -192,7 +201,17 @@ function setDefaults(defs) { try { form = new MyFormData(); Object.keys(opts.formData).forEach(function (key) { - form.append(key, opts.formData[key]); + function add(key, data, opts) { + if (data.value) { opts = data.options; data = data.value; } + form.append(key, data, opts); + } + if (Array.isArray(opts.formData[key])) { + opts.formData[key].forEach(function (data) { + add(key, data); + }); + } else { + add(key, opts.formData[key]); + } }); } catch(e) { cb(e); @@ -307,7 +326,11 @@ function setDefaults(defs) { } } - reqOpts.method = (reqOpts.method || 'GET').toUpperCase(); + if (opts.body || opts.json || opts.form || opts.formData) { + reqOpts.method = (reqOpts.method || 'POST').toUpperCase(); + } else { + reqOpts.method = (reqOpts.method || 'GET').toUpperCase(); + } reqOpts.headers = reqOpts.headers || {}; // crazy case for easier testing @@ -360,6 +383,7 @@ module.exports._keys = Object.keys(_defaults).concat([ 'encoding' , 'body' , 'json' +, 'form' , 'formData' , 'FormData' ]);