I'm nesting information in my forms to match my models, this greatly simplify things on the backend, but I can't find out how to get arrays or objects (or the combination of both) in Sails.js
Assuming I have a form like this
NOTE: Support for "multipart/form-data" is totally required.
<form action="/articles" method="post" enctype="multipart/form-data">
<input type="file" name="status" value="published">
<!-- Entry 0 -->
<input(type="text" name="entries[0][title]" value="Entry 1")
<input(type="text" name="entries[0][content]" value="Entry 1 Content...")
<!-- Entry 1 -->
<input(type="text" name="entries[1][title]" value="Entry 2")
<input(type="text" name="entries[1][content]" value="Entry 2 Content...")
<!-- images -->
<input type="file" name="images[]">
<input type="file" name="images[]">
</form>
Im expecting to get an object like this in the req.params.all() obj
{
status: 'published',
entries: [
{title: 'Entry 1', content: 'Entry 1 Content...'},
{title: 'Entry 2', content: 'Entry 2 Content...'}
]
}
Now when calling req.params.all()
/req.body
what I'm getting instead is:
{
status: 'published',
'entries[0][title]': 'Entry 1'
'entries[0][content]': 'Entry 1 Content...'
'entries[1][title]': 'Entry 2'
'entries[1][content]': 'Entry 2 Content...'
'entries[0][title]': 'Entry 1'
}
Calling req.file('images[]')
gives the correct behavior. I'm checking the ._files
property of what is returned by that fn and shows my 2 images in there.
Using the brackets here seems really weird, but that's what it is.
I guess that related to what I'm getting with req.params.all() I can further parse this, but it'll be hacky and brittle if something changes in the future. In any case this is a common pattern in any web app and supported by many languages and frameworks, so to me is really odd that it seems impossible to get what I need with just plain sails.js functionality, so i'm guessing that I'm not doing something as it should or that I'm missing something. Please point me out in the right direction, and if actually Sails does not support this basic nesting behaviour, then how should I proceed?
Sending raw content via Javascript is not an option here (Unless is impossible otherwise) as suggested in the third answer in this question: Is it possible in Sailsjs to build more complex models Doing it this way, at least for the text based fields I get the correct output, not sure with images, since I've tested via postman with rawdata.
Edit: So far i've tried changing the skipper body parser in config/http.js like this:
bodyParser: {
fn: require('body-parser').urlencoded,
options: {extended:true}
}
But that made my server useless, it did start, but it didn't respond to any request, not sure why (Even using their our example with skipper, that you just have to uncomment, does not work).
Since skipper is based on bodyparser, I modified the skipper module index.js just to test what happens.
var URLEncodedBodyParser = connect.urlencoded({extended:true})
But it didn't work, I get the same result as the begining, even installing body-parser and using it instead of the connect.urlencoded body parser had no effect.
Edit 2: As @robertklep stated using form-data without multipart works, but of course I lose the ability to upload files, which is really important and the reason I put it in the example form.
Edit 3: Just to complement the accepted answer in case someone needs it, this is what I did:
In config/http.js
middleware: {
order: [
// some middleware
'bodyParser',
'qsBodyParser',
// more middleware
],
qsBodyParser: require('../api/middleware/qsBodyParser')
}
And in api/middleware/qsBodyParser
Qs = require('qs');
qsBodyParser = function(req, res, next) {
if req.is('multipart/form-data'){
req.body = Qs.parse(req.body);
}
return next();
};
module.exports = qsBodyParser;
The currect version of skipper
depends on [email protected]
, which depends on [email protected]
, which doesn't handle the way you are sending form arrays/objects.
Your form example comes out like this (using extended : true
):
{
"entries" : [{
"content" : "Entry 1 Content..."
}, {
"content" : "Entry 2 Content..."
}]
}
The latest version ([email protected]
) works as expected, so you have to plug that into skipper
somehow.
EDIT: this comment seems to suggest that using multipart/form-data
will disable array(/object?) parsing altogether.
EDIT #2: you can manually parse req.body
using qs
, which seems to accept an object as argument:
var qs = require('qs');
var obj = qs.parse({
status: 'published',
'entries[0][title]': 'Entry 1',
'entries[0][content]': 'Entry 1 Content...',
'entries[1][title]': 'Entry 2',
'entries[1][content]': 'Entry 2 Content...',
});
// obj is now:
// { status: 'published',
// entries:
// [ { title: 'Entry 1', content: 'Entry 1 Content...' },
// { title: 'Entry 2', content: 'Entry 2 Content...' } ] }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With