I am making a wikipedia clone for a project. My initial edit Route looks like this:
router.get('/edit/:id', function(req, res){
var id = req.params.id;
console.log(id);
models.Page.findById(id, function(err, doc){
console.log(doc);
res.render('edit', {page: doc});
});
});
All I am doing is creating an edit page view for a Page with matching id of the id params.
This works until...
I had to add this new route:
router.get('/:url_name/:id', function(req,res){
var id = req.params.id;
models.Page.findById(id, function(err, doc){
res.render('show_page', {page: doc});
});
});
Now when I have this route active, my edit/:id page route doesn't collect the id parameter(req.params.id).
I am lost on why this isn't working and where I should start trying to debug because I am not getting any errors, it will still take me to my edit/:id page, but when I console.log(id) I do not receive a value, or even and undefined, nothing. Furthermore, the {page:doc} I am passing to my edit view is not being found.
If anyone can provide insight or a place to start looking to debug, I'd appreciate it. Just a reminder, the edit/:id route works as it should (req.params.id grabs the id) when I don't have the :url_name/:id route active.
Current Routes -
I added var wiki_routes = require('./routes/wiki'); in my app.js and in that route I have:
// **** URL ROUTES ****
router.get('/', function(req, res) {
models.Page.find(function(err, docs) {
res.render('index', { docs: docs });
});
});
router.get('/:url_name', function(req, res){
var url_name = req.params.url_name;
var isUpdated = req.query.updated;
var updated = (isUpdated === 'true')?true:false;
models.Page.find({url_name: url_name}, function(err, page){
if(page.length > 1){
console.log(page);
res.render('disambiguation', {pages: page, updated: updated });
} else {
console.log(page);
res.render('show_page', {page: page[0], updated: updated});
}
});
});
router.get('/:url_name/:id', function(req,res){
var id = req.params.id;
models.Page.findById(id, function(err, doc){
res.render('show_page', {page: doc});
});
});
// **** EDIT ROUTES ****
router.get('/edit/:id', function(req, res){
var id = req.params.id;
console.log(id);
models.Page.findById(id, function(err, doc){
console.log(doc);
res.render('edit', {page: doc});
});
});
router.post('/edit_submit/:id', function(req, res){
var id = req.params.id;
var new_title = req.body.title;
var new_body = req.body.body;
console.log(req.body);
models.Page.findByIdAndUpdate(id, {title: new_title, body: new_body }, function(err, docs){
// redirects to the wiki page
res.redirect('/wiki/'+ docs.url_name +'?updated=true');
});
});
// **** DELETE ROUTE ****
router.get('/delete/:id', function(req, res){
var id = req.params.id;
models.Page.findByIdAndRemove(id, function(err, data){
res.redirect('/?deleted=true');
});
});
Routes are set up as they occur in the code, as node will give presedence to whichever route it encounters first, so order matters a lot when you're setting up your routes.
An URL could potentially match several routes, especially when using variables that catch a large number of different URL's, or static routes etc.
Say in your case you have an URL that looks like
http://example.com/edit/1234
That URL would most certainly be caught by this route
router.get('/:url_name/:id' ....
as it matches the http://example.com/something/something layout, and it would also be caught by the following route
router.get('/edit/:id', ....
as it matches the http://example.com/edit/something layout.
What route actually cathes the URL depends on the order they where encountered when set up, whichever route that was declared first will catch the URL.
Just shifting the order of the routes will in most cases solve issues like this
// if the URL matches, this will execute first
router.get('/edit/:id', function(req, res){
// do stuff
});
// You'll only get here if the URL doesn't match the above route
router.get('/:url_name/:id', function(req, res){
// do stuff
});
There is a workaround if you simply can't swap the routes around, using the next() callback, like this
router.get('/:url_name/:id', function(req, res, next){
if ( req.params.url_name == 'edit' ) {
next(); // this sends the request back to look for routes below this one
}else{
// do stuff
}
});
router.get('/edit/:id', function(req, res){
// now we'll get here when the "url_name" is "edit" ...
});
Related
i am getting started with node.js and now I got stuck.
var mysql = require('mysql');
var dbconfig = require('../config/database');
var connection = mysql.createConnection(dbconfig.connection);
connection.query('USE ' + dbconfig.database);
app.get('/createarticle', isLoggedIn, function(req, res){
res.render('createarticle.ejs', {
user:req.user
});
});
app.post('/createarticle' , (req, res ) => {
let sql = 'INSERT INTO news SET ?'
let post = {
// author: req.user.username,
content : req.body.content,
title: req.body.title
}
connection.query(sql, post, (err, res) => {
if(err) throw err;
console.log('success');
console.log(res);
});
});
If I use req.user.username I get this error message Cannot read property 'username' of undefined.
I also tried user.username and req.user.
In my main JS I have this function that shoud always give the user if logged in.
app.get('*', function(req, res, next) {
res.locals.user = req.user || null;
next();
});
In addition to this I want to include two redirects but I don't know where to put it.
successRedirect: '/',
failureRedirect: '/createarticle',
I appreciate every answer and pacience with me. :)
I can't comment so I have to post an answer:
What does your request object look like:
app.get('/createarticle', isLoggedIn, function(req, res){
console.log('request',req)
res.render('createarticle.ejs', {
user:req.user
});
});
If you're not populating the request object in your 'GET' from your front end,
you won't have the user you are asking for.
You may want to do some handling in the front to make sure that you only send populated request objects.
I have two separate app.get routes that render "/" and contain mongoose find{} methods, which relates to two mongodb collections. I then have corresponding post routes that redirect/post to "/" with the data I entered on the forms.
Depending on the form I use (devportals/new or writingportals/new) to submit my data, I would like it to use the correct app.get to find the data and then populate my index.ejs file to display the new entry.
When I submit my post, it always defaults to the devportal.find{} GET route at the top even though I submitted my update to mongodb through the writingportals form.
app.get("/", function (req, res){
devportal.find({}, function(err, newDevPortal){
if(err){
console.log(err);
}else{
res.render("index",{newDevPortal: newDevPortal});
}
});});
app.get("/", function (req, res){
writingportal.find({}, function(err, newWritingPortal){
if(err){
console.log(err);
}else{
res.render("index",{newWritingPortal: newWritingPortal});
}
});
});
app.get("/portals/writingportals/new", function(req, res){
res.render("portals/writingportals/new");
});
app.get("/portals/devportals/new", function(req, res){
res.render("portals/devportals/new");
});
app.post("/", function(req, res){
var wpTitle = req.body.writingtitle;
var wpUrl = req.body.writingurl;
var wpImageUrl = req.body.writingimageurl;
writingportal.create({title: wpTitle, url: wpUrl, imageurl: wpImageUrl}, function(err, newWritingPortal){
if(err){
console.log(err);
}else{
res.redirect("/");
}
});
});
app.post("/", function(req, res){
var devTitle = req.body.writingtitle;
var devUrl = req.body.writingurl;
var devImageUrl = req.body.writingimageurl;
devportal.create({title: devTitle, url: devUrl, imageurl: devImageUrl}, function(err, newDevPortal){
if(err){
console.log(err);
}else{
res.redirect("/");
}
});
});
I have two separate app.get routes that render "/"
Your endpoints do not render "/", they get triggered based on the request uri, which in your case is "/".
Next, you cannot have two endpoints with the same route and same type of request, this is true with any web server. So in your case, you have two endpoints defined for GET requests to '/'. There is no way for your code to understand the difference between those two routes, so it will always hit the first one it finds.
Finally I'll add that for your solution, you most definitely will want unique endpoints because you are taking a template with a data model and sending back markup.... basically an endpoint for each page.
I will say that I'm not a huge fan of that approach. I think it makes life easier using a framework like react, then have every request to '/' return index.html with a reference to your react code... instead of the whole SSR deal.
However, for a lot of things, its simple to just use query parameters in the request. Here is an example where one endpoint is defined that will return all data from any collection through query parameters:
app.get('/api/collection', function(req, res) {
var collection = req.query.collection;
var _collection = db.collection(collection);
_collection.find({}).toArray(function(err, docs) {
if (err)
return res.status(500).send(err);
res.send(docs);
})
})
And then the request url would be GET -> /api/collection?collection=NAME_OF_YOUR_COLLECTION
When attempting to use req.params.id inside my delete (or update for that matter) route I am getting the above message. This has stumpted me for some time and I'm sure I'm making a mistake somewhere with my routes / objects.
Changing the app from res.render("/campgrounds/ + req.params.id); to -
res.render("/campgrounds"); solves the issue but doesn't reload the same page like i'm look to have it do. I can't wrap my head around why the app is returning undefined when accessing the campground route from req.params.id.
var express= require("express");
var router = express.Router();
var Comment = require("../models/comment");
var Campground = require("../models/campgrounds");
// COMMENTS EDIT ROUTE
router.get("/campgrounds/:id/comments/:comment_id/edit", function(req, res){
Comment.findById(req.params.comment_id, function(err, foundComment){
if(err){
console.log(err)
} else {
res.render("comments/edit", {campground_id: req.params.id, comment: foundComment})
}
})
})
// comment update
//campgrounds/:id/comments/:comment_id
router.put("/:comment_id", function(req, res){
Comment.findByIdAndUpdate(req.params.comment_id, req.body.comment, function(err, updatedComment){
if(err){
console.log(err)
} else {
// KNOWN BUG - /campgrounds/ + req.params.id will return cast to boject failed for value undefined at path _id. having the app redirect to all campgrounds page as a work around
res.redirect("/campgrounds");
}
})
})
// DELETE ROUTER
router.delete("/:comment_id", function(req, res){
Comment.findByIdAndRemove(req.params.comment_id, function(err){
if(err){
res.redirect("back");
} else {
res.redirect("/campgrounds/" + req.params.id);
}
})
})
function isLoggedIn(req, res, next){
if(req.isAuthenticated()){
return next();
} else {
res.redirect("/login");
}
}
module.exports = router;
I've ran into the same issue, and it was due to route order. I moved the show route below the index route cuz "it flows better", but that broke the code, and confused it since I think route order matters. make sure your route make sense from the applications point of view if you reordered them
Make sure you have input field name "id" in comment form (or "id" in ajax request).
router.put("/:comment_id", function(req, res){
const id = req.params.id;
console.log(id);
Comment.findByIdAndUpdate(req.params.comment_id, req.body.comment, function(err, updatedComment){
if(err){
console.log(err)
} else {
res.redirect("/campgrounds/" + id);
}
})
})
I think your problem is you are not sending the comment_id from html to controller try printing the req.params.comnent_id
then try this
var express= require("express");
var router = express.Router();
var Comment = require("../models/comment");
var Campground = require("../models/campgrounds");
// COMMENTS EDIT ROUTE
router.get("/campgrounds/:id/comments/:comment_id/edit", function(req, res){
console.log("params.comment_id",params.comment_id);
if(req.params.comment_id){
Comment.findById(req.params.comment_id, function(err, foundComment){
if(err){
console.log(err)
} else {
res.render("comments/edit", {campground_id: req.params.id, comment: foundComment})
}
}else {
res.render("comments/edit", {campground_id: req.params.id, comment: foundComment})
}
})
})
Ok guys, I recently encountered the same problem. I tried finding solutions all over the internet but failed to get any useful answers.
Then I tried looking hard into the "CastError" and found that the ID I am getting from 'req.params.id' had an extra white space in front of it.
For example: instead of '5bed4f6276c4920db404eb25', I got ' 5bed4f6276c4920db404eb25' as the ID. I don't know (yet) why I get the id with that extra white space but I figured that white space must be the issue.
So I stripped the ID for white space with javascript replace function as follows:
var curid = req.params.id;
curid = curid.replace(/\s/g,'');
and it worked!
So instead of
Campground.findByIdAndUpdate(req.params.id, req.body.campground, function(err, updatedCamp){..}
now use:
Campground.findByIdAndUpdate(curid, req.body.campground, function(err, updatedCamp){..}
So,you have to replace all
req.params.id
in your code block with
curid
And you are good to go!
Here is the whole code block for your reference:
router.put("/:id", function(req, res){
var curid = req.params.id;
curid = curid.replace(/\s/g,'');
Campground.findByIdAndUpdate(curid, req.body.campground, function(err, updatedCamp){
if(err){
console.log(err);
res.redirect("/campgrounds");
} else{
//redirect somewhere (show page)
res.redirect("/campgrounds/" + curid);
}
});
The best solution to this problem is reformat the _id by cleaning the blank space it added (in my case coming from a form in my template "x.ejs")
const to_remove = req.body.checkbox;//id from template is wrong
const listname = req.body.check_list_name;
let **to_rem_cured** = to_remove.replace(/\s/g,'');
List.findOneAndUpdate({name:listname},{'$pull':{list:{_id: **to_rem_cured** }}},function(err,doc){
if(!err){
console.log(doc);
res.redirect("/"+listname);
}
else{
console.log(err);
}
});
I want to redirect incoming request to different endpoints based on a query parameter in the request.
For example,
http://localhost:9000/?category=xyz¶m1=...¶m2=...
category value can be one of [abc, ijk, pqr, xyz].
Based on category value, request should get redirected to one of "/abc", "/ijk", "/pqr", "/xyz" preserving remaining parameters.
So, the above request should become /xyz/?param1=...¶m2=...
I am new to Nodejs/express, I thought it is better to create different routes based on category value to modularize the code.
What is the ideal approach to handle such cases?
Looks like the old "front page controller" pattern:
app.get('/', routeByParameter)
function routeByParameter(req, res){
var category = req.params.category
if(category === 'abc'){
return abcRoute(req, res)
}
else if(category === 'xyz'){
return xyzRoute(req, res)
}
}
function abcRoute(req, res){
req.send('abc')
}
OR to use the "front page terminology"
app.get('/', dispatcher)
function dispatcher(req, res){
var category = req.params.category
if(category === 'abc'){
return viewAbc(req, res)
}
else if(category === 'xyz'){
return viewXyz(req, res)
}
}
function viewAbc(req, res){
req.send('abc')
}
Alternatively, you could get in and modify or create a custom route dispatcher in the middleware...
Create one route for each of these values [abc, ijk, pqr, xyz]. For handling different query parameters (?category=xyz) create a middleware that transform the request url:
app.use(function(req, res, next) {
// adding category in the url path will redirect to the
// correct route after calling next function
req.url = "/" + req.query.category;
next();
});
Assuming it's a GET request the categories routes would be:
app.get('/abc', function(req, res) {
//do stuff for 'abc' category
res.send('abc');
});
app.get('/ijk', function(req, res) {
//do stuff for 'ijk' category
res.send('ijk');
});
app.get('/pqr', function(req, res) {
//do stuff for 'pqr' category
res.send('pqr');
});
app.get('/xyz', function(req, res) {
//do stuff for 'xyz' category
res.send('xyz');
});
Note that middleware will edit url for all requests, filter by the request url adding the path app.use("/path", function(req,res,next)... or add some logic inside it.
Each piece of this code works individually but when put together, the Article section (third set) will not load. I am fairly new to coding so I was hoping for someone to point me in the right direction. I assume this is because the first route is executed and it never hits the second route...but I don't know how to fix it.
// Adds "www." to all URLs
app.all('*', function(req, res, next) {
if (req.headers.host.match(/^www/) == null) {
res.redirect('http://www.' + req.headers.host + req.url);
} else {
next();
}
});
// Serves the .html file but displays without .html in the URL
app.get('*', function(req,res){
var pages = ['/now', '/home', '/itinerary','/exploreourroots','/contact','/credits'];
if (pages.indexOf(req.url.toLowerCase()) !== -1) {
res.sendFile(__dirname + '/public' + req.url + '.html');
};
});
// Loads Articles from the database on the home page
app.get('/home', function(req, res) {
var query = 'SELECT a.title,a.author,a.cover_photo,a.html_loc,a.date_published,c.name country_name FROM articles a INNER JOIN countries c ON c.id=a.country_id ORDER BY a.date_published desc;'
connection.query(query, function(err, rows, fields) {
var list = [];
for (var i = 0;i < rows.length; i++) {
list.push({html_loc: rows[i].html_loc,country_name: rows[i].country_name,cover_photo: rows[i].cover_photo,title: rows[i].title,author: toAuthorPhoto(rows[i].author),date_published: toArticleDate(rows[i].date_published)});
}
res.contentType('application/json');
res.send(list);
});
});
Thanks for the help!
I'm not sure what intentions you have.
What if user type in browser www.mydomain.com/home. You want to return static html file (home.html), which is done by second router. Or you want to serve then article from db (third route)?
If you want to serve article from db when url is /home, then replace order of second and third routes:
app.get('/home', function(req, res) { ... });
app.get('*', function(req,res){ ... });
if you want to serve static html when your route is /home and also you want to have possibility to serve articles, then as before replace orders and additionally change /home router to /article. Then you will serve articles if url is /article :
app.get('/article', function(req, res) { ... });
app.get('*', function(req,res){ ... });