First of all, I have the following route:
route: '/list/:param1'
Im sending the following request to the server:
url: '/list/someParam?param2=foo¶m3=bar
After that, express is giving to me two important objects that i need, the req.url and the req.params.
I want to get an object that merges both url and params. Im using the following code:
module.exports = function (req, res, next) {
var url = require('url');
var queryObject = url.parse(req.url, true).query;
console.log(queryObject);
console.log(req.params);
console.log(req.params.length);
}
So it logs something like:
{ param2: 'foo', param3: 'bar' }
[ param1: 'someParam' ]
0
Now, assuming that I dont know the name of the params, I need to have an object with the information from both req.url and req.params. Here are my questions:
1) How can I do to get something like:
{ param1: 'someParam', param2: 'foo', param3: 'bar' }
2) Why is req.params logging something like an array? And if it is an array, why is it returning me 0 as its length?
Thanks in advance.
Use req.query instead of parsing the URL again.
I'm guessing Express has a custom toString implementation for that particular object.
You can also look up both in order of importance using req.param(name)
Related
I'm trying to add the named routers feature to the express router for NodeJS (It's a practice, i don't want to donwload any other package), i've made it successfully, but just left one thing... the path, i mean, if i've got a route like:
index.js
const express = require('express');
const app = express();
app.use(require('./routes'));
routes/index.js
const express = require('express');
const expressRouter = express.Router();
const router = require("./namedRoutes")(expressRouter); // <- This is my own named router
const ViewsController = require('../app/controllers/ViewsController');
router.get("/", ViewsController.loadHome).setName("home");
router.get("/fromHome", ViewsController.loadHome).setName("fromHome");
module.exports = router;
I can get the route name from my helper (handlebars) using:
{{ route "home" }} or {{ route "fromHome" }}
And it returns me:
localhost:3000/ or localhost:3000/fromHome
But when i try to prepend a path using this syntax on mi index.js
app.use("/other/path", require('./routes'));
My named routes will return me localhost:3000/ or localhost:3000/fromHome again without the path.
Doing test in my route helper i saw that i can get all the routes registered from the request object, this is an example of just one object route registered (These routes objects come inside of an array that is in req.app._router.stack):
Layer {
handle: [Function: router] {
params: {},
_params: [],
caseSensitive: undefined,
mergeParams: undefined,
strict: undefined,
stack: [Array],
_get: [Function],
_post: [Function],
_put: [Function],
_delete: [Function],
get: [Function],
post: [Function],
put: [Function],
delete: [Function],
setName: [Function],
lastRouteAdded: '/fromOthers',
names: [Array]
},
name: 'router',
params: undefined,
path: undefined,
keys: [],
regexp: /^\/other\/path\/?(?=\/|$)/i { fast_star: false, fast_slash: false },
route: undefined
}
As you can see, the path is undefined, but if you see, there's a property called:
regexp: /^\/other\/path\/?(?=\/|$)/i { fast_star: false, fast_slash: false }
This is a regexp that contains the path, so if i could get that regexp i could get the path, but it seems to be that it's no way to get it, if i try to do this (Supossing that the var routeInstance contains this JSON object):
const path = routeInstance.regexp;
console.log(path);
I get this:
/^\/other\/path\/?(?=\/|$)/i { fast_star: false, fast_slash: false }
But now i can't get that regexp, if i do this:
console.log(Object.keys(path));
I get this:
[ 'fast_star', 'fast_slash' ]
As you can see, now there isn't the regexp, and if i do this:
console.log(Object.keys(routeInstance));
(Remember that the var routeInstance contains the Layer object) I get this:
[
'handle', 'name',
'params', 'path',
'keys', 'regexp',
'route'
]
So, how can i get that regexp? Or how can i get that path if there's other way to obtain it?
Note:
I know that app.use("/other/path", require('./routes')); allows you to put an regexp or array instead of a static path string, so my idea is that the route helper returns the most obvious route based on the past regexp.
I appreciate your response!!!
Ok i've solved it, i can get that regexp doing this:
const regexpPath = routerInstance.regexp.toString();
It seems that the JSON Object is discarded and just return the regexp string.
So now i can get the path with this:
// Search all ocurrences of type /some (Returns an array)
const pathOcurrences = regexpPath.match(/\/[A-Za-z0-9]+/igm);
// Remove the last element 'cause it will still bring me the / i of the regexp
pathOcurrences.pop();
// Join the ocurrences
const path = pathOcurrences.join("");
Obviously it will fails for a regexp that is not a path, but that's right, you're trying to get the path from a regexp, you can't so it works ^-^
I'm having an issue with routing to a route with query params I have a function like so
goToLink(link) {
this.router.navigate([`${link.split('?')[0]}`, { queryParams: this.sortParams(link)}]);
}
and this function
sortParams(link) {
let queryParams = url.split('?')[1];
let params = queryParams.split('&');
let pair = null;
let data = {};
params.forEach((d) => {
pair = d.split('=');
data[`${pair[0]}`] = pair[1];
});
return data;
}
okay so basically what Is happening I have a function called goToLink() and that takes in a url and the url that gets passed is a string with query params like so..
https://website.com/page?id=37&username=jimmy
the above is just an example thats not what it actually looks like but its a link string with query parameters now so what happens is I remove the params from the string and store them in a data object in the sortParams() function so when I pass the above string in I get an object that looks like this
{id: 37, username: 'jimmy'}
now thats what i'm passing into the queryParams: section in the router.navigate,
the function should look like this when the object is returned
this.router.navigate([`${link.split('?')[0]}`, { queryParams: {id: 37, username: 'jimmy'}}]);
so i successfully route to the desired route but the query params look like this..
/page;queryParams=%5Bobject%20Object%5D
Am I doing something wrong here??
Any help would be appreciated!
EDIT
If I just change the function to this
this.router.navigate([`${link.split('?')[0]}`, { queryParams: {id: 37, username: 'jimmy'}}]);
I get the same url /page;queryParams=%5Bobject%20Object%5D
Can be of that you had placed the bracket which is supposedly for the 1st param but you had encapsulated it on the whole line of route
Your code:
// This is the end of your route statement: '}}]);' which the close bracket is included
this.router.navigate([`${link.split('?')[0]}`, { queryParams: {id: 37, username: 'jimmy'}}]);
Update route:
place the ] close bracket within the 1st parameter only, try to not place it on the last part of the route statement.
// Update end line: '}});'
this.router.navigate([`${link.split('?')[0]}`], { queryParams: {id: 37, username: 'jimmy'}});
Summary:
this.router.navigate([ url ], { queryParams: { ... } })
If you want to navigate to a URL string including query params, try using router.navigateByUrl.
For example:
this.router.navigateByUrl('/page?id=37&username=jimmy');
Try like this:
this.router.navigate(['my-route', 37], { queryParams: { username: "jimmy"}});
this.router.navigateByUrl('/page?id=37&username=jimmy');
This is better when the params are dynamic.
I have a problem with extra quotes in req.query object. I'm using Angular.JS(1.5.8) with NodeJS(6.2.0).
So what i mean:
On client side I have simple REST api
.factory('Users', function ($resource) {
var Users = $resource("api/users/" + ":_id", { _id: "#_id" }, {update: {method: 'PUT'}, query:{ method: "GET", isArray: false }});
return Users;
})
And use it like this
return Users.query({a: 'some text', b: 10}}).$promise.then(function(results){
return results.users;
});
And all works fine, on server I'm get as results console.log('Query parsing - ', req.query); - Query - { a: 'some text', b: '10' }
But when I'm trying to send nested object:
Users.query({a: 'some text', b: {first: 10, second: 20}})
On server I have results with extra quotes and object not valid: Query - { a: 'some text', b: '{"first":10,"second":20}' }.
As result I cannot use it for mongoose queries. When I waited for {$text:{"$search":"admin"}} I'm recived {$text:'{"$search":"admin"}'}.
Can someone faced this problem before. Thanks for the help
JSON/Object to QueryString and back conversion has many many issues. Nesting, Arrays, "null", boolean values etc. You just encountered one.
Simplest solution is to JSON.stringify() objects as query string value:
url = 'www.example.com' + '/resource' + '?json=' + JSON.stringify(dataObject);
Browsers will automatically URL encode the JSON string. On other clients you may have to do it manually.
You can parse it back on server. For example this expressjs middleware:
app.use(function(req, res, next){
if(req.query.json){
try {
req.query.json = JSON.parse(req.query.json);
next();
}catch(err){
next(err);
}
}
});
So I got two mongoose-models:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var eventSchema = new mongoose.Schema({
name: String,
date: String,
dogs: [{ type: Schema.Types.ObjectId, ref: 'Dog' }]
});
module.exports = mongoose.model('Event', eventSchema);
and
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var dogSchema = new mongoose.Schema({
name: String,
age: String,
gender: String,
});
module.exports = mongoose.model('Dog', dogSchema);
Event contains an array of dogs and im trying to figure out how to add/delete dogs to this array.
On the client I got this method:
$.ajax({
url: "http://localhost:3000/api/events/",
dataType: 'json',
type: 'POST', // Not sure if I should Post or Put...
data: {event_Id : this.props.choosenEvent._id, //Here I got the Id of the Event that i want to update by
dog_Id : this.props.events[dog]._id }, //adding this dog, which Id is here
success: function(data) {
}.bind(this),
});
},
On the server, NodeJs, I got my routes to the API. To me, it makes sense to use an PUT-method and start by getting the right Event with the event_Id passed as a param. Something like:
router.route('/events/:event_id')
.put(function(req, res) {
Event
.findById({ _id: req.param.event_id })
.populate('dogs')
});
But Im stuck at this point. Any help appreciated. Thanks!
Update!
Thank you! Your code helped a lot, you used lodash .remove to delete a dog from the array, is there a similar way to add an item with lodash?
I gave the add method a go like this:
router.route('/events')
.post(function(req, res) {
// Your data is inside req.body
Event
.findById({ _id: req.body.event_Id })
// execute the query
.exec(function(err, eventData) {
// Do some error handing
// Your dogs are inside eventData.dogs
eventData.dogs.push(req.body.dog_Id);
console.log(eventData)
});
// Update your eventDate here
Event.update({_id: req.body.event_id}, eventData)
.exec(function(err, update) {
// Do some error handing
// And send your response
});
});
When I hit the console.log(eventData) I can see that dog_id gets added to the array as it should. However it does not get saved to the db and the error says that eventData is not defined in Event.Update. I suspect this is a Js-scope-issue.
Onte thing that boggles me is this:
Obviously I would like to be able to add and remove dogs from the array and the
route is this: router.route('/events') .
But if both the add-method and the remove-method is on the same route, how can the code know which one I am going for?
There are a few mistakes you are making. First of all, you are making a POST request but your route accepts a PUT request. I have updated your code so it accepts a POST.
When posting objects, your data is inside req.body. req.params is used for url parameters. This is also the case when using a PUT request.
Populating dogs is not really necessary. You are sending your dog_id to your function so you can delete your item from your array which removes your dog from your event. This should do the trick. Please note that this does not remove your dog from your DB but only from your event.
Last but not least. I am using lodash. _.remove is a lodash function. You should definitely check it out, it will help you a lot.
Take a look at my code. It should get you going:
router.route('/events/:event_id')
// Since you are posting, you should use POST from JavaScript instead of PUT
.post(function(req, res) {
// Your data is inside req.body
Event
.findById({ _id: req.body.event_id })
// execute the query
.exec(function(err, eventData) {
// Do some error handing
// Your dogs are inside eventData.dogs
_.remove(eventData.dogs, function(d) {
return d._id === req.body.dog_Id;
});
// Update your eventDate here
Event.update({_id: req.body.event_id}, eventData)
.exec(function(err, update) {
// Do some error handing
// And send your response
});
});
});
UPDATE:
I do not think there is a way to add items to an array with lodash but you can simply use push like you did in your code example. That works just fine.
Your update is not working because your are executing the findById and update at the same time. You will have to find the item first, add the id and THEN update the item :) Move your update function inside the callback of your findById function and that should be fixed. So it looks like this:
router.route('/events')
.post(function(req, res) {
// Your data is inside req.body
Event
.findById({ _id: req.body.event_Id })
// execute the query
.exec(function(err, eventData) {
// Do some error handing
// Your dogs are inside eventData.dogs
eventData.dogs.push(req.body.dog_Id);
console.log(eventData)
// Update your eventDate here
Event.update({_id: req.body.event_id}, eventData)
.exec(function(err, update) {
// Do some error handing
// And send your response
});
});
});
You can add different functions on the same route as long as the method is different from the others. Take a look at REST at this answer. You can have a GET, POST, PUT & DELETE on /events. This is defined by this rule:
router.route('/events').post();
I want to get a value in my url.
My url is like :
host/:value.schema
I want to get value.
Exemple :
host/horse.schema value = horse
I have another route without .schema:
host/:value
Exemple :
host/horse value = horse
How tell Express to make the difference ?
You can try something like this:
app.get('/:value.:schema?', function (req, res) {
console.log(req.params);
});
You'll receive this:
http://localhost:3000/horse { value: 'horse', schema: undefined }
http://localhost:3000/horse.schema { value: 'horse', schema: 'schema' }