ExpressJS sendFile() cannot send URL GET parameters - node.js

I'm new to Node.js and Express.js and its routing. It's all set up correctly and it all works except for the following code.
I tried the following code:
app.get("/game/*", function(req, res) {
res.sendFile(__dirname + "/public/game.html?gameId=" + /\/([^\/]+$)/.exec(req.url)[1]);
});
The goal was to send all requests with /game/{gameId} (where gameId is some number) to /public/game.html?gameId={gameId}.
It correctly gets the requests with /game/, gets the gameId parameter from the URL, and attempts to sendFile() it. However, the sendFile() does not work, saying:
web.1 | Error: ENOENT, stat '/opt/lampp/htdocs/papei/public/game/32'
I've searched this error, and I guess it has to do with a file not being found. The problem is, /public/game.html exists. If I remove the ?gameId... part in the sendFile(), then it works. But I guess the sendFile() is looking for an exact URL, and is not finding it.
Is there any way to send URL GET parameters using ExpressJS?

I think the problem is that sendFile tries to find an exact match (which your query parameters break) as you thought.
You could use express-static to serve the html page, and then redirect to it as needed like so:
app.get("/game/:gameid", function(req, res) {
// Not ideal, as it uses two requests
res.redirect('/game.html?gameId=' + req.params.gameid)
});
Or you could put the html inside a template and render it for the response e.g:
app.get("/game/:gameid", function(req, res) {
// Render the 'game' template and pass in the gameid to the template
res.render('game', {gameid: req.params.gameid})
});
Either way, you don't need to use a catch all route and regex to get query parameters, see req.params or req.query in the express documentation.
Hope this helps.

Related

How to ignore specific files to be loaded when I use route parameters in Express

When I make a GET request with route parameters in express with mongoose like the following code, I sometimes see that the browser tries to load some unexpected files such as favicon.ico, robots.txt, humans.txt, sitemap.xml, ads.txt, etc., and 404 error shows up in the browser console.
app.get("/:userId", ...);
By refering to this Q&A, I figured out that if I don't use the route parameters right after the root route like the following code, it doesn't happen.
app.get("/user/:userId", ...);
In the same Q&A, however, there seem to be another way that uses req.url to ignore those unexpected files to be loaded, but it isn't explained in detail.
How do you do that?
All that's meant in that other answer is that you could examine req.url in your route handler and make sure it is not a known special name. In this specific case, it's probably simpler to use req.params.userId instead of req.url, but you could also use req.url in the same way.
const specials = new Set(["favicon.ico", "robots.txt", "humans.txt", "sitemap.xml", "ads.txt"]);
app.get("/:userId", (res, res, next) => {
// if it's a special URL, then skip it here
if (specials.has(req.params.userId)) {
next();
return;
}
// process your route here
});
Personally, I wouldn't recommend this solution because it presupposes a perfect knowledge of all possible special filenames. I don't use a top level wildcards ever because they ruin the ability to use your server for anything else.

Cannot execute any statement in express route except last

I have set up an express route, which is expected to serve an HTML file, which is this:
app.use('/',express.static("../first-app/dist"));
var filepath=path.join(__dirname,"..","first-app","dist","index.html");
app.get('/', (req, res) => {
console.log("hello");
console.log("hello");
res.sendFile(filepath);
});
When '/' route is hit, the file is served correctly but none of the statement above res.sendFile is being executed. This is little bit strange because the statements are expected to execute in sequence. No matter what I write above the last line, nothing will execute.
Please help me to find the problem in this.
Thanks in advance.
If the response/file is getting sent, if these are all your routes and if you don't see the output from console.log("hello");, then the only possible answer is that this route:
app.use('/',express.static("../first-app/dist"));
is what is sending the file. From your code, it looks like this will be the case because index.html is inside the ../first-app/dist and if express.static() gets a request for /, then it looks for index.html in the target directory. If it finds it, then it sends it and routing does not continue any further.
So, you have a couple possible solutions.
Move index.html somewhere else so express.static() doesn't find it and then change where you build filepath from to the new location of index.html.
Add an option to express.static("../first-app/dist", {index: false}) to tell it not to look forindex.htmlif the path is/`. You can see that option documented here.
Move the app.get('/', ...) route before the express.static() route so it gets the request first.

Function isn't being called in Node Js using REST API

I am writing a code in Node JS, which uses mustache for templating html and REST API as backend.
Here is my code that doesn't work.
function setupRoutes(app) {
const base = app.locals.base;
app.get(`${base}/search.html`,doSearchContent(app));
app.get(`${base}/:name`,doGetContent(app));
}
function doSearchContent(app) {
return async function(req, res) {
console.log("here");
}; };
When I run my program and go to base/search.html. It never calls the doSearchContent method.
Any idea why and how I can fix this?
EDIT: The doGetContent works as expected. It's when I run the search.html it doesn't
The express paths should be started with a leading slash. Please change your routes addition to something like this:
...
app.get(`/${base}/search.html`,doSearchContent(app));
app.get(`/${base}/:name`,doGetContent(app));
...
Express matches the path of http request against the 'path' provided for all routes to decide which routes must be called. Since the http paths always start with a slash, your routes also must specify those to match.
These lines
app.get(`${base}/search.html`,doSearchContent(app));
app.get(`${base}/:name`,doGetContent(app));
are not working as you expect. In Express routes we don't invoke functions directly. Instead, we either pass a name of a callback fucntion to invoke, that receives req and res params, or an anonymous callback. In your case it could be something like this:
app.get(`${base}/search.html`,(req, res) => {
console.log("It's alive!");
doSearchContent(app);
});
app.get(`${base}/:name`, (req, res) => {
doGetContent(app)
});
The express paths should be started with a leading slash.
This is not true
Have you added search.html file or used a template to build the HTML?
Make sure the template is being called and not the html file.
Other than that your code looks fine and it should work

Express GET route will not work with parameters

I am new to Express and Mongoose. I am currently working on my first project, that is not a tutorial, and I has run into a problem.
I have multiple routes, they are defined in the index.js like this:
app.use('/api/client',require('./routes/client'));
app.use('/api/host',require('./routes/host'));
In the routes, there are multiple verbs that work, like PUT and POST.
Here is the problematic route (I am trying to do more that what is presented here, but what is presented here, does not work as well):
router.get('/ama/:id', function (req, res, next) {
Ama.findById(req.params.id).then(function(Ama){
res.send(Ama);
});
});
This should work, right? It should return the document in the database, with that id. And I have checked if the document excists, probably around a 100 times.
Now, if I simplify the route greatly, removing the id, and make a simple response, the route works:
router.get('/ama', function (req, res, next) {
res.send({type:"GET"});
});
It's so wierd, that as soon as i add the parameter, i get a:
<pre>Cannot GET /api/host/ama</pre>
in Postman.
Any ideas? Mongod is running, my other routes are working.
It looks like you're trying to retrieve this URL:
/api/host/ama?id=SOMEID
However, you have a route declared for URL's that look like this:
/api/host/ama/SOMEID
In other words, the id is part of the path of the URL, and not passed as a query string parameter (that's what /:id means: it's a placeholder for a part of the URL that the route should match).
So either change the request-URL by adding the id to the path (/api/host/ama/58e395a8c6aaca2560089c‌​e7), or rewrite your route handler to something like this:
router.get('/ama', function (req, res, next) {
Ama.findById(req.query.id).then(function(Ama){
res.send(Ama);
});
});
However, I would advise using the former (making the id part of the URL).
There are two problems here:
router.get('/ama/:id', function (req, res, next) {
Ama.findById(req.params.id).then(function(Ama){
res.send(Ama);
})
res.send(req.params.id)*/
});
First of all, res.send(req.params.id) will run before the res.send(Ama); and it will probably send the entire response. You are missing the .exec() method call (see the answer by Cédric De Dycker for details). The res.send() can only be reliably used once. Maybe you want res.write() instead if you want to wrte multiple things. Also you don't handle the promise rejection so you don't handle database errors. To know why you should always handle the promise rejections see this answer: Should I refrain from handling Promise rejection asynchronously?
The parameter should work fine but it seems you missed to add .exec to your query
Ama.findById(req.params.id).exec().then(function(Ama){
res.send(Ama);
})

Express Framework app.post and app.get

I am fairly new to the express framework. I couldn't find the documentation for application.post() method in the express API reference. Can someone provide a few examples of all the possible parameters I can put in the function? I've read a couple sites with the following example, what does the first parameter mean?
I know the second parameter is the callback function, but what exactly do we put in the first parameter?
app.post('/', function(req, res){
Also, let's say we want the users to post(send data to our server) ID numbers with a certain format([{id:134123, url:www.qwer.com},{id:131211,url:www.asdf.com}]). We then want to extract the ID's and retrieves the data with those ID's from somewhere in our server. How would we write the app.post method that allows us to manipulate the input of an array of objects, so that we only use those object's ID(key) to retrieve the necessary info regardless of other keys in the objects. Given the description of the task, do we have to use app.get() method? If so, how would we write the app.get() function?
Thanks a lot for your inputs.
1. app.get('/', function(req, res){
This is telling express to listen for requests to / and run the function when it sees one.
The first argument is a pattern to match. Sometimes a literal URL fragment like '/' or '/privacy', you can also do substitutions as shown below. You can also match regexes if necessary as described here.
All the internal parts of Express follow the function(req, res, next) pattern. An incoming request starts at the top of the middleware chain (e.g. bodyParser) and gets passed along until something sends a response, or express gets to the end of the chain and 404's.
You usually put your app.router at the bottom of the chain. Once Express gets there it starts matching the request against all the app.get('path'..., app.post('path'... etc, in the order which they were set up.
Variable substitution:
// this would match:
// /questions/18087696/express-framework-app-post-and-app-get
app.get('/questions/:id/:slug', function(req, res, next){
db.fetch(req.params.id, function(err, question){
console.log('Fetched question: '+req.params.slug');
res.locals.question = question;
res.render('question-view');
});
});
next():
If you defined your handling functions as function(req, res, next){} you can call next() to yield, passing the request back into the middleware chain. You might do this for e.g. a catchall route:
app.all('*', function(req, res, next){
if(req.secure !== true) {
res.redirect('https://'+req.host+req.originalUrl);
} else {
next();
};
});
Again, order matters, you'll have to put this above the other routing functions if you want it to run before those.
I haven't POSTed json before but #PeterLyon's solution looks fine to me for that.
TJ annoyingly documents this as app.VERB(path, [callback...], callback in the express docs, so search the express docs for that. I'm not going to copy/paste them here. It's his unfriendly way of saying that app.get, app.post, app.put, etc all have the same function signature, and there are one of these methods for each supported method from HTTP.
To get your posted JSON data, use the bodyParser middleware:
app.post('/yourPath', express.bodyParser(), function (req, res) {
//req.body is your array of objects now:
// [{id:134123, url:'www.qwer.com'},{id:131211,url:'www.asdf.com'}]
});

Resources