Express.JS url parameters aren't parsed - node.js

I have the following route configured
app.put('/v1/users/:uid', function(req, res){
res.send(req.route);
});
When sending a PUT request to http://localhost:3000/v1/users/blablabla
I get the following output back
{
"path": "/v1/users/:uid",
"method": "put",
"callbacks": [
null
],
"keys": [
{
"name": "uid",
"optional": false
}
],
"regexp": {},
"params": []
}
As you see the params array seems to be empty instead of having the value "blablabla". But the "uid" key appears in keys, which I don't really know what to make of.
Would appreciate any suggestions.

OK, the trick is that Express uses a sparse array to parse the params.
When you pass it to req.send, the array is converted with JSON.stringify. Here's what happens in a JS shell:
> var params = [];
> params['uid'] = 1;
> params;
[ uid: 1 ]
> JSON.stringify(params);
'[]'
What's happening is that adding a non-numeric to an array does not change its length:
> params.length
0
So the new value is ignored by JSON.stringify.

Well this is the weirdest thing I've seen.
When doing a console.log(req.params) or console.log(req.route.params) I get an empty array response ([]).
But when doing a console.log(req.params.uid) I get the value! Thats extremely weird but hey, it works :)
Cheers.

Related

How to remove last element in set in dynamo?

I have created a set using dynamoDB document client . I am able to remove items in this set however when i remove to the last element in the set nothing returns until i make a new post. Then all the other data is displayed.
const params = {
TableName: 'beta-user-' + process.env.NODE_ENV,
Key: {
username: request.username
},
UpdateExpression: "DELETE #features :feature",
ExpressionAttributeNames: { "#features" : "features" },
ExpressionAttributeValues: { ":feature": dynamodb.createSet([request.feature]) },
ReturnValues: "NONE"
};
and im calling it like
const dynamoPromise = dynamodb.update(params).promise();
return await dynamoPromise.then(result => { // stuff })
The UpdateExpression i do not think is wrong
https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html#Expressions.UpdateExpressions.DELETE
I belive the problem is with ExpressionAttributeValues if i remove
dynamodb.createSet I get many validation errors.
When i make a get request to app i get
{
"message": [
{
"username": "x",
"feature": [
"blah",
"test"
]
},
{
"username": "z",
"feature": [
"blah"
]
},
}
I make a delete request and remove the feature test from username x. This works and returns the same response minus the test feature. I make another delete request to remove blah. Blah is removed however when I make a get request I recieve:
{
"message": {}
}
The other data is returned when i make a new post to that specific user.
EDIT:
I think the issue might be due to dynamo not liking an empty set
The issue was with my return statement in my get request. I assumed that once features were deleted the record would be deleted. I was trying to return features on an object that had no features, therefore, it was erroring out and not returning anything.

Node dbus-native

The documentation for node module dbus-native is weak, and the none of the many examples apply to my use case, which seems like it ought to be simple.
My problem is trying to make a simple method call into the "connman" network manager. I can make a request (invoking GetProperties method) just fine, and get a complex structure back and generally figure out how to get data out of it. But when I try to send something back with SetProperty, I just can't figure out how to get my data into the form it wants.
There's not really any documentation about how to translate to and from DBus's type system and node's. I gathered from source that variant types are arrays with signature and value, e.g., [ 's', 'string ], but no matter how I try to massage the data I'm trying to send to SetProperty, I get an "Invalid struct data" error.
The SetProperty method I'm trying to call has an argument signature "sv"--name, value. The particular property I'm trying to set has a value with is itself (as far as I can determine--there's precious little documentation on that side as well) an "a{sv}". that is,property is a hash of property names and values. My initial attempt as calling this function was:
var sysbus = require('dbus-native').systemBus();
sysbus.invoke({
path: '/net/connman/service/ethernet_1cba8cfa0e57_cable',
destination: 'net.connman',
'interface': 'net.connman.Service',
member: 'SetPropertry',
signature: 'sv',
body: [
'IPv4.Configuration', [
'a{sv}',
{ 'Method': [ 's', 'dhcp' ] }
]
],
type: dbus.messageType.methodCall
}, function (err, res) {
// etc...
});
This gives me the "Invalid struct data" error. I've tried man other ways to wrap the data in arrays, hashes, etc., and just can't seem to find the right answer. I'm trying to emulate this working Python code:
import dbus
bus = dbus.SystemBus()
service = dbus.Interface(bus.get_object("net.connman",
"/net/connman/service/ethernet_1cba8cfa0e57_cable",
"net.connman.Service");
conf = { "Method": make_string_variant("dhcp") }
service.SetProperty("IPv4.Configuration", conf);
Any ideas?
I recently started using dbus-native for connman and also had problems with all the nested arrays. I'll be improving the marshaling/unmarshaling in the fork of the project at https://github.com/Digisoft-tv/node-dbus - you might want to have a look. Hopefully my changes will be accepted up-stream.
Anyway, to get it working now, the format of the parameters you need to pass in is as below:
mgr.CreateSession([ [ 'key', [ 's', 'value' ] ], [ 'key2', [ 'i', 2 ] ] ], '/object/path', function(error, response) {
if (error) {
return console.error('SetProperty error:', error);
}
console.info('SetProperty response', response);
});
The outermost array is the "object" (ARRAY of dict entries).
Each nested array holds a key-value pair (DICT ENTRY). key at index 0 and value at index 1.
The values is a variant, which is always encoded as an array, with "signature" at index 0 and the actual value at index 1.
Hope it helps.
The correct way to encode 'a{sv}' is [ ['string', ['signature', 'value']], ... ]
Arrays ( a ) are encoded as normal JS arrays as well as structs / hashes ( () / {} ). I'll probably add shortcut to allow JS object to be used in place of a{sv} but at the moment input has to be quite verbose.
var sysbus = require('dbus-native').systemBus();
sysbus.invoke({
path: '/net/connman/service/ethernet_1cba8cfa0e57_cable',
destination: 'net.connman',
'interface': 'net.connman.Service',
member: 'SetPropertry',
signature: 'sv',
body: [
'IPv4.Configuration', [
'a{sv}',
[ // a
[ // {
'Method', [ 's', 'dhcp' ] // sv
]
]
]
],
type: dbus.messageType.methodCall
}, function (err, res) {
// etc...
});

How do I sort 'auto-magically' using Mongoose?

Say I have a URL like this:
http://dev.myserver.com/systems?system_type.category=Penetration
which hits the following controller:
exports.findAll = function(req, res) {
System.find(req.query, function(err, systems) {
res.send(systems);
});
};
and then returns the set below 'auto-magically' using Node, Express, MongoDB and Mongoose:
[{
"_id": "529e5f29c128685d860b3bad",
"system_number": "123",
"target_country": "USA",
"system_type": {
"category": "Penetration",
"subcategory": ["Floor", "Wall"]
}
},{
"_id": "999e5f29c128685d860b3bad",
"system_number": "456",
"target_country": "Canada",
"system_type": {
"category": "Penetration",
"subcategory": ["Floor", "Wall"]
}
}]
Now, if I want the results sorted by 'target_country', what is the 'best practice' for 'auto-magically' doing that?
Are there certain parameters/syntax that Mongoose/Express are expecting to do it for me? Or, is this a case where I have to specifically code for it? (That would kill the 'auto-magical' functionality already there.)
Thanks!
UPDATE: Here is what worked for me.
exports.findAll = function(req, res) {
// Sort Ascending:
http://dev.dom.com/systems?system_type.category=Penetration&sort=system_number
// Sort Descending:
http://dev.dom.com/systems?system_type.category=Penetration&sort=-system_number
// Default sort ascending on system_number:
http://dev.dom.com/systems?system_type.category=Penetration
var sort_param = req.query.sort ? req.query.sort : 'system_number';
System.find().sort(sort_param).find(function(err, menus) {
res.send(menus);
});
};
I guess where I went wrong, was to think I should find with filters and then sort, instead of find all, sort and then find again with filters. Still getting my head wrapped around the whole 'callback philosophy' I guess.
You need to define separate URL parameters for the query and sort components of your System query. As in:
System.find(req.query.query).sort(req.query.sort).exec(function(err, systems) {
res.send(systems);
});
Then you'd use request URL parameters that look like:
?sort=target_country
&query[system_type.category]=Penetration
&query[system_type.subcategory]=Floor
Docs on sort here.

How to create a comma seperated values by reading JSON using node js

I have a JSON as follows:
{
"workloadId": "68cf9344-5a3c-4e4a-927c-c1c9b6e48ccc",
"elements": [
{
"name": "element1",
"uri": "vm/hpcloud/nova/large"
},
{
"name": "element2",
"uri": "vm/hpcloud/nova/small"
}
],
"workloadStatus": "none"
}
I need to get the comma seperated string as follows :
element1,element2
when i tried as given below , i got empty string:
app.post('/pricingdetails', function(req, res) {
var workload = req.body;
var arr = new Array();
for(var index in workload.elements)
{
arr[index] = workload.elements[index].uri;
}
console.log(arr.join(","));
}
Elements is an array. Never use for/in for arrays. Use a standard for loop instead:
for(var i = 0; i < workload.elements.length; ++i) {
arr.push(workload.elements[i].uri);
}
console.log(arr.join(','));
Node will let you use the forEach method instead of a for loop or the for/in (the latter of which has the potential to cause problems).
Avoiding new Array() is another Crockford-ism, but I also just find the [] notation more concise and readable.
var workload = req.body;
var arr = [];
workload.elements.forEach(function(el) {
arr.push(el.uri);
});
console.log(arr.join(','));
These sort of nitpicks aside, like dc5 I tried just defining a variable with your JSON and your code did what I would expect (returning the array elements joined by a comma). What are you using to call that route, and how are you passing it the indicated JSON?
EDIT: using
curl -v -H "Content-type: application/json" -X POST -d '{"workloadId": "68cf9344-5a3c-4e4a-927c-c1c9b6e48ccc", "elements": [{"name": "element1", "uri": "vm/hpcloud/nova/large"}, {"name": "element2", "uri": "vm/hpcloud/nova/small"} ], "workloadStatus": "none"}' http://localhost:3000/pricingdetails
fails for me if bodyParser() isn't used. With app.use(express.bodyParser()); in my code, it works as expected.

Node.js - Send and receive Array as GET/POST using querystring

I've got the following code, but it doesn't seem to work:
var post_req = {
array: [
[ {
param1: 'something',
param2: 123
} ],
[ ],
[ ],
[ {
param2: 'something',
param4: 1234,
param1: 'hello'
} ]
]
};
var data_send = querystring.stringify(post_req);
var request = client.request('POST', '/', headers);
request.end(data_send);
and
if( req.method == 'POST' ) {
req.addListener('data', function(chunk)
{
POST = querystring.parse(chunk);
console.log(POST);
}
}
I end up with 5 sub-arrays, corresponding to the 5 parameters in the objects but with extra '][' characters in their names:
{ array:
[ { '][param1': 'something' }
, { '][param2': '123' }
, { '][param2': 'something' }
, { '][param4': '1234' }
, { '][param1': 'hello' }
]
}
There is a new node package that fixes this: "npm install qs".
https://github.com/ljharb/qs
"query string parser for node supporting nesting, as it was removed from 0.3.x, so this library provides the previous and commonly desired behaviour (and twice as fast)"
If anyone can tell me why it was removed from 0.3.x, I will give you an upvote for your comment. (I want my confidence in Node.js restored.)
To confirm my comment above, node's querystring.stringify function won't handle nested arrays (at the time of writing).
You can see the source of stringify at https://github.com/ry/node/blob/master/lib/querystring.js
Note that it handles one level of arrays but it doesn't recurse. When it finds an array it uses stringifyPrimitive to encode the array's values. You can see that stringifyPrimitive doesn't handle arrays, only number, boolean and string.
As I suggested in my comment, given that you're using a POST request a better idea would be to use JSON encoding for your complex data structure.
Or use https://github.com/visionmedia/node-querystring as suggested by #FriendlyDev
Don't use querystring.parse for recreating a JSON object sent as string. Use instead JSON.parse. And use JSON.stringify instead of querystring.stringify
querystring is useful when you send parameter encoded in the url or when you post a form. But there is no point in using it if you send just one JSON object with all your data.
In your scenario I would dismiss the querystring library and use JSON library, which is already included. It would be cleaner and faster.
http://www.json.org/js.html

Resources