How do I handle a route that has a parameter, and if the client fails to include it, not let the server error out.
router.get('/:id', function(req, res, next) {
let profileID = req.params.id
if (profileID) {
res.send('ID provided');
} else {
res.send('No id provided');
}
});
If the ID parameter is provided I want to use it somewhere. But if it is missing, I want there to be a default behaviour to fall back to (ie. Route to another page, some message, etc)
The expected behavior is:
http://example.com/ routes to a landing page
But if a trailing ID is provided
http://example.com/abc123 routes to a specific profile
You can specify router.get('/:id?') and then id will be marked as an optional param.
CodeSandbox example
Express Server Example
// the question mark marks the parameter as optional
app.get("/:id?", (req, res) => {
// will create an id entry even if none is provided
console.log(req.params);
// if there is an id do something else
if (req.params.id) return res.send(req.params.id);
// if there is no id default behaviour for '/' route
res.sendFile(__dirname + "/view/hello.html");
});
Related
I have an express route that I want to find a uid value and for some reason it isn't working I was wondering what I was doing wrong. I keep getting an error whenever I got to localhost:8080/user?uid=20 and I want to retrieve 20 as the param uid but keep getting an error.
My path
/user?uid=:uid\d+
The code itself
router.get('/user\?uid=:uid\d+', (req,res) => {
res.send('This works');
});
Having a regex patter for catching query string is not a good practice. Reduce your route to /user, then you can have access to your query strings through req, just like this:
for localhost:3000/user?uuid=20
router.get('/user', (req,res) => {
console.log(req.query); // { uuid: 20 }
console.log(req.query.uuid); // 20
res.send('This works');
});
You can have more information here: https://expressjs.com/es/api.html#req.query
I am trying to send a variable to the page after I have redirected to another page. I have very basic knowledge in Node.js and can't seem to figure out a way to do that. Here's my code.
app.get('/search' , function (req, res) {
var postcode = req.query.search;
var ward = my_search.getWardNum(postcode,(ward) => {
res.redirect('/:ward'+ ward);
});
});
app.get('/ws/:postcode' , function (req, res) {
var postcode = req.params.postcode.replace("+","");
console.log(pc);
my_search.postcodeLookUp(pc,(ward) => {
var area = my_search.lookupWard(ward);
res.send(area);
});
});
So in the first app.get(), I get the postcode and redirect to another page.
However, I still need that postcode with second app.get().
I understand that nature of node.js is async. Is there a way to do what I want to do?
When you "redirect", you're not just changing the URL the user sees, you're sending a HTTP response with a status code indicating that the client should try to access another page instead.
You can't send data after you redirect because the full response has already been sent! You should consider why you're redirecting and if it is really necessary. You could also redirect to a route containing the URL parameter that you want to be present:
res.redirect('/ward/' + ward + '/' + postcode);
...
app.get('/ward/:wardID/:postcode', (req, res, next) => {
// route code here...
});
Note that you'll probably want a prettier format than that, but that is one way to accomplish this.
When this code hits the redirect line it throws the 'Can't set headers after they are sent error' and doesn't redirect. I'm guilty of not fully understanding headers and how express works with them. This link about this error is confusing me a bit, probably because I don't have a basic enough understanding of what's going on. Also, I know this is a bit of a naive approach to authenticating, but I'm just trying to get basic things to work.
app.post('/api/login', function(req, res) {
if (req.body.password === auth.password) {
auth.date = new Date()
res.redirect('/admin')
} else {
console.log("wrong pw")
}
})
UPDATE : thank you to #Brendan Ashworth I missed an obvious else, which I've added now and no longer get the error.
However this line doesn't change the contents of my page
res.sendfile('./public/admin/views/tunes.html')
It worked before I wrapped it with the auth check
var auth = require('../config/auth')
module.exports = function(app) {
/*
* CONTENT API
*/
//...
/*
* Admin Routes
*/
app.get('/admin/login', function(req, res) {
res.sendfile('./public/admin/views/login.html')
})
app.post('/api/login', function(req, res) {
if (req.body.password === auth.password) {
auth.date = new Date()
res.redirect('/admin')
} else {
res.json({message: 'Wrong password!'})
}
})
app.get('/admin', function(req, res) {
if (auth.date) {
res.sendfile('./public/admin/views/tunes.html')
console.log("test") //
} else { //added else
res.redirect('/admin/login')
}
})
app.get('/admin/:url', function(req, res) {
if (auth.date) {
res.sendfile('./public/admin/views/' + req.params.url + '.html')
} else { //added else
res.redirect('/admin/login')
}
})
// frontend routes
// route to handle all angular requests
app.get('*', function(req, res) {
res.sendfile('./public/views/index.html')
})
}
FINAL UPDATE!! The last thing I needed was to handle the redirect client side after sending the file. Simple authentication works perfectly now!
$http.post('/api/login', $scope.auth).success(function() {
window.location.href = '/admin'
})
An explanation of the error Can't set headers after they are sent error:
All HTTP responses follow this basic structure:
.. Response Line ..
.. Headers ..
.. Body ..
If you want to redirect a user, first the Response Line will be sent with a redirect code (lets say 300), then the Headers will be sent with a Location: xxx header.
Then, we can finally send a body (not in the case of a redirect, but in general). However - in the case with your code - you are sending a Body response then trying to redirect the user. Since the headers (and response line) have both already been sent (because you sent the body), it can't send more headers after the body.
An example of this in your code would be:
app.get('/admin', function(req, res) {
if (auth.date) {
res.sendfile('./public/admin/views/tunes.html')
}
res.redirect('/admin/login')
})
If I'm assuming right, you actually want to return after the res.sendfile() call. If auth.date is truthy, then you'll be sending a file (i.e. body response) and then giving a redirect code - that doesn't work.
after redirect just call res.stop();
Suppose I have a urls like
www.foo.com/abc
www.foo.com/huyr
... etc
where 'abc' and 'huyr' in this example are IDs, and i'd fetch info from the database where the key is that id.
But suppose I also want static links (that'll serve static pages) to follow the same url format:
www.foo.com/about
www.foo.com/contact
What would be the best way to do this?
Right now I have something like this in mind
exports.view = function(req, res) {
var id = req.params.id
switch (id) {
case 'about':
// load 'about' controller
break
case 'contact':
// load 'contact' controller
break
default:
// proceed with this function
}
}
Is this the best approach? is there a more efficient/organized way to do this?
Best way, in my opinion, would be to serve those pages, static or not, like you would with any other page. Respond to the GET request with the right page.
// will respond to http://www.site.com/about
app.get('/about', function( req, res, next ) {
res.render('about.ejs' // etc
});
// will respond to http://www.site.com/contact
app.get('/contact', function (req, res, next) {
res.render('contact.ejs' // etc
});
// will respond to any first subdir (iirc) http://www.site.com/(?)
app.get('/:id', function ( req, res, next ) {
var id = req.param['id'];
// db query, etc
});
You static pages of course can be in a route to make it cleaner. Only thing you need to make sure of is you respond to those static routes before the id route, or else you will be responding to the about/contact url with the route that you want responding to the ids. Order matters here.
The following code is the user-facing part of a new node app we are building:
var loadInvoice = function(req, res, next) {
Invoice.findById(req.params.invoiceId, function (err, invoice) {
if (err) {
res.send(404, 'Page not found');
} else {
req.invoice = invoice;
next();
}
});
};
app.namespace('/invoices/:invoiceId', loadInvoice, function () {
app.get('', function(req, res){
var templateVals = {
//some template data
};
res.render('paymentselection', templateVals);
});
app.post('', function(req, res){
var data = {
// some data for the apiCall
};
someAPI.someRequest(data, function(err, data) {
console.log(res.status());
res.redirect(data.url);
});
});
});
The first method returns a confirmation page where the user presses a button to post to the same url, which triggers a redirect to an external website.
This all works exactly once. Every second request will crash the app with the message Cant set headers after they are sent. After carefull inspection of the code I could find no reason for this to happen so I added the console.log line which indeed confirms the location header has been set. But it is set to the value i got from someAPI on the previous request not the current one.
This makes absolutely no sense to me. I do not store this value anywhere nor do I do caching or persistence of this data in any way.
Does anybody know what could be causing this?
I use express, express-namespace, mogoose and swig
I found out the problem was being caused bij the 'Restler' libaray used within 'someAPI'. I have no idea how this is possible but swapping it out with something else fixed the problem.