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 bination 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 mon 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 plex 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 unment, 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 plement 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;
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 bination 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 mon 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 plex 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 unment, 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 plement 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;
Share
Improve this question
edited Jul 14, 2017 at 14:40
Amiga500
6,14111 gold badges71 silver badges119 bronze badges
asked Aug 13, 2015 at 3:38
Lu RomanLu Roman
2,2603 gold badges26 silver badges41 bronze badges
2
-
1
body-parser
doesn't handlemultipart/form-data
, which would explain why using it doesn't seem to change anything. – robertklep Commented Aug 13, 2015 at 7:59 - I was thinking about that last night, and checked today without the multipart, and indeed it parses correctly, but i need it to work with multipart/form-data – Lu Roman Commented Aug 13, 2015 at 15:42
1 Answer
Reset to default 7The 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 es 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 ment 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...' } ] }