How to make request to static resource with parameters? - javascript

I have my node.js restify server, and folder with static resource
const restify = require('restify')
let server = restify.createServer()
server.listen(8080, function () {
console.log('%s listening at %s', server.name, server.url)
});
server.get('/*', restify.plugins.serveStatic({
directory: __dirname + '/static',
default: 'index.html'
}));
i'm trying to understand how to make get request to index.html with parameters like localhost:8080/index.html?token=123
and if token is valid, return index.html to client, else return error

You can chain multiple request handlers and the next() method - first do some parameters' validation and then, as a second handler, use the serveStatic method. Here's an example:
const restify = require('restify')
let server = restify.createServer()
server.listen(8080, function () {
console.log('%s listening at %s', server.name, server.url)
});
server.get('/*', (request, response, next) => {
const token = request.query.token;
if(token !== '123') {
//those two lines below will stop your chain and just return 400 HTTP code with some message in JSON
response.send(400, {message: "Wrong token"});
next(false);
return;
}
next(); //this will jump to the second handler and serve your static file
return;
},
restify.plugins.serveStatic({
directory: __dirname + '/static',
default: 'index.html'
}));

Related

Why does the webpage load forever if there is a 404?

I have an Express.js/Node.js website hosted with Heroku. If a file can't be found, the server is supposed to send a 404 error (like most websites do). But the error handlers in my code don't work properly. When the client requests a file that doesn't exist, the page will load...and load...and load...forever. How do I make the server stop loading when it detects the file can't be found?
This is the first section of my JavaScript:
var express = require('express'); // Express.js
var app = express();
var http = require('http');
var server = http.createServer(app);
var bodyParser = require('body-parser');
var postgres = require('pg'); // Postgres database
app.use(express.static('static', {
extensions: ['html']
}));
app.all('*', function (request, response, next) {
var redirectURL = request.query.redirect;
if (redirectURL != undefined) {
response.redirect(redirectURL);
}
});
app.get('/', function (request, response, next) {
response.redirect('/main/index');
});
And the following is my error handling middleware (it goes right after the previous part). The first one handles 400x error codes and the second one handles 500x error codes.
// Handle 404 error
app.use(function(request, response) {
response.status(400);
response.send("Error 404!");
});
// Handle 500 error
app.use(function(error, request, response, next) {
response.status(500);
response.send("Error 500!");
});
server.listen(process.env.PORT || 8080, function () {
console.log('Listening on port 8080!');
});
Call response.end() in your middleware, like so
// Handle 404 error
app.use(function(request, response) {
response.status(400);
response.send("Error 404!");
response.end();
});
This is how I check for errors in my app, one single middleware
app.use((err, req, res, next) => {
if (err.statusCode === 403) {
res.statusCode = 403;
var out = {
message: 'missing/invalid authorization: ' + err.message,
code: err.code,
statusCode: err.statusCode
};
res.end(JSON.stringify(out));
return;
}
next(err);
});
Check that your middleware actually gets called, console.log should be sufficient
If a middleware does not end the request, it must forward the request to the next middleware, so you need to call next(), the format of a middleware is
app.use((req, res, next) => {
//I'm not doing anything, forward the request
next();
});
app.use((req, res, next) => {
req.send('I ended the request');
//this middleware ended the request
}));
I had almost the same issue (in my case it was the page for HTTP 500 that was loading forever) and the problem was with another middleware that I have added myself and which was before the error handler middlewares and inside which I forgot to call next():
app.use(function(req: Request, res: Response, next: NextFunction) {
closeConnFromRequestStorage()
next(); // make sure to call to call next
});

Calling Express Route internally from inside NodeJS

I have an ExpressJS routing for my API and I want to call it from within NodeJS
var api = require('./routes/api')
app.use('/api', api);
and inside my ./routes/api.js file
var express = require('express');
var router = express.Router();
router.use('/update', require('./update'));
module.exports = router;
so if I want to call /api/update/something/:withParam from my front end its all find, but I need to call this from within another aspect of my NodeJS script without having to redefine the whole function again in 2nd location
I have tried using the HTTP module from inside but I just get a "ECONNREFUSED" error
http.get('/api/update/something/:withParam', function(res) {
console.log("Got response: " + res.statusCode);
res.resume();
}).on('error', function(e) {
console.log("Got error: " + e.message);
});
I understand the idea behind Express is to create routes, but how do I internally call them
The 'usual' or 'correct' way to handle this would be to have the function you want to call broken out by itself, detached from any route definitions. Perhaps in its own module, but not necessarily. Then just call it wherever you need it. Like so:
function updateSomething(thing) {
return myDb.save(thing);
}
// elsewhere:
router.put('/api/update/something/:withParam', function(req, res) {
updateSomething(req.params.withParam)
.then(function() { res.send(200, 'ok'); });
});
// another place:
function someOtherFunction() {
// other code...
updateSomething(...);
// ..
}
This is an easy way to do an internal redirect in Express 4:
The function that magic can do is: app._router.handle()
Testing: We make a request to home "/" and redirect it to otherPath "/other/path"
var app = express()
function otherPath(req, res, next) {
return res.send('ok')
}
function home(req, res, next) {
req.url = '/other/path'
/* Uncomment the next line if you want to change the method */
// req.method = 'POST'
return app._router.handle(req, res, next)
}
app.get('/other/path', otherPath)
app.get('/', home)
I've made a dedicated middleware for this : uest.
Available within req it allows you to req.uest another route (from a given route).
It forwards original cookies to subsequent requests, and keeps req.session in sync across requests, for ex:
app.post('/login', async (req, res, next) => {
const {username, password} = req.body
const {body: session} = await req.uest({
method: 'POST',
url: '/api/sessions',
body: {username, password}
}).catch(next)
console.log(`Welcome back ${session.user.firstname}!`
res.redirect('/profile')
})
It supports Promise, await and error-first callback.
See the README for more details
Separate your app and server files with the app being imported into the server file.
In the place you want to call your app internally, you can import you app as well as 'request' from 'supertest'. Then you can write
request(app).post('/someroute').send({
id: 'ecf8d501-5abe-46a9-984e-e081ac925def',
etc....
});`
This is another way.
const app = require('express')()
const axios = require('axios')
const log = console.log
const PORT = 3000
const URL = 'http://localhost:' + PORT
const apiPath = (path) => URL + path
app.get('/a', (req, res) => {
res.json('yoy')
})
app.get('/b', async (req, res) => {
let a = await axios.get(apiPath('/a'))
res.json(a.data)
})
app.listen(PORT)

Nodejs - Express res.download giving Can't set headers after they are sent exception

I want to make an api that will serve files of any extensions.
Like this: http://localhost/download/[file].[extension]
Here is my code, but it is intermittently giving this message: Can't set headers after they are sent.
var express = require('express');
var app = express();
app.get('/download/:fileName/:extension', function(req, res){
var file = __dirname + '/' + req.params.fileName + '.' + req.params.extension;
res.download(file, function(err){
if (err) {
res.sendStatus(404);
}
res.end();
});
});
var server = app.listen(3000, function () {
var host = server.address().address;
var port = server.address().port;
console.log('app listening at http://%s:%s', host, port);
});
res.download has already sent a response (Not always true in the case of an error though)
You can fix this by doing
res.download(file, function(err){
if(err) {
// Check if headers have been sent
if(res.headersSent) {
// You may want to log something here or do something else
} else {
return res.sendStatus(SOME_ERR); // 404, maybe 500 depending on err
}
}
// Don't need res.end() here since already sent
}
Other changes called out in the comments above:
download uses sendFile, which you don't need res.end() after
download's documentation warns that you need to check res.headersSent when handling errors, as the headers may already be sent, which would mean you can't change the status

can't delete record from mongodb dbase using backbone.js

Following "Developing Backbone js" http://addyosmani.github.io/backbone-fundamentals/#talking-to-the-server (search "parse function")
on click "Delete": the book is not deleted from the server (dbase) even with this "parse" function operating OK... the DELETE http command is given, with the correct ID for the book... but this doesn't delete it from the dbase...
generated URL command looks like this:
DELETE http://localhost:4711/api/books/5417ff846b205d9c05000001
... this is triggering the following function in server.js
app.delete( '/api/books/:id', function( request, response ) {
console.log( 'Deleting book with id: ' + request.params.id );
...
... but the DELETE command never "returns" (in FF Console you just get the spinner, which doesn't go away)...
In your server.js, setup your server as follows:
// Module dependencies.
var application_root = __dirname,
express = require("express"), // Web framework
path = require("path"), // Utilities for dealing with file paths
mongoose = require('mongoose'); // MongoDB integration
//Create server
var app = express.createServer();
// Configure server
app.configure(function () {
app.use(express.bodyParser()); // parses request body and populates req.body
app.use(express.methodOverride()); // checks req.body for HTTP method overrides
app.use(app.router); // perform route lookup based on url and HTTP method
app.use(express.static(path.join(application_root, "public"))); // Where to serve static content
app.use(express.errorHandler({ dumpExceptions:true, showStack:true })); // Show all errors in development
});
//Start server
app.listen(4711, function () {
console.log("Express server listening on port %d in %s mode", app.address().port, app.settings.env);
});
// Connect to database
mongoose.connect('mongodb://localhost/library_database');
//Schemas
var Book = new mongoose.Schema({
title:String,
author:String,
releaseDate: Date
});
//Models
var BookModel = mongoose.model('Book', Book);
Try creating the delete route as follows:
app.delete('/api/books/:id', function(req, res){
console.log('Deleting book with id: ' + req.params.id);
return BookModel.findById(req.params.id, function(err, book){
return book.remove(function(err){
if(!err){
console.log('Book removed');
return res.send('');
} else {
console.log(err);
}
});
});
});
And test it via AJAX:
$.ajax({
url:'/api/books/5417ff846b205d9c05000001',
type: 'DELETE',
success:function(data, textStatus, jqXHR){
console.log("Post resposne:");
console.dir(data);
console.log(textStatus);
console.dir(jqXHR);
}
});

how to get request path with express req object

I'm using express + node.js and I have a req object, the request in the browser is /account but when I log req.path I get '/' --- not '/account'.
//auth required or redirect
app.use('/account', function(req, res, next) {
console.log(req.path);
if ( !req.session.user ) {
res.redirect('/login?ref='+req.path);
} else {
next();
}
});
req.path is / when it should be /account ??
After having a bit of a play myself, you should use:
console.log(req.originalUrl)
Here is an example expanded from the documentation, which nicely wraps all you need to know about accessing the paths/URLs in all cases with express:
app.use('/admin', function (req, res, next) { // GET 'http://www.example.com/admin/new?a=b'
console.dir(req.originalUrl) // '/admin/new?a=b' (WARNING: beware query string)
console.dir(req.baseUrl) // '/admin'
console.dir(req.path) // '/new'
console.dir(req.baseUrl + req.path) // '/admin/new' (full path without query string)
next()
})
Based on: https://expressjs.com/en/api.html#req.originalUrl
Conclusion: As c1moore's answer states, use:
var fullPath = req.baseUrl + req.path;
In some cases you should use:
req.path
This gives you the path, instead of the complete requested URL. For example, if you are only interested in which page the user requested and not all kinds of parameters the url:
/myurl.htm?allkinds&ofparameters=true
req.path will give you:
/myurl.html
UPDATE 8 YEARS LATER:
req.path was already doing exactly same thing that I mentioned here. I don't remember how this answer solved issue and accepted as a correct answer but currently it's not a valid answer. Please ignore this answer. Thanks #mhodges for mentioning this.
Original answer:
If you want to really get only "path" without querystring, you can use url library to parse and get only path part of url.
var url = require('url');
//auth required or redirect
app.use('/account', function(req, res, next) {
var path = url.parse(req.url).pathname;
if ( !req.session.user ) {
res.redirect('/login?ref='+path);
} else {
next();
}
});
This can produce different results when calling directly in base module i.e. main file (e.g. index.js or app.js) vs calling from inside module via app.use() middleware i.e. route file (e.g. routes/users.js).
API call:
http://localhost:8000/api/users/profile/123/summary?view=grid&leng=en
We'll be comparing our outputs against above API call
1) First, we'll see the result from inside module:
We'll be placing our user module inside routes directory, with one route
routes/users.js
const router = (require('express')).Router();
router.get('/profile/:id/:details', (req, res) => {
console.log(req.protocol); // http or https
console.log(req.hostname); // only hostname (abc.com, localhost, etc)
console.log(req.headers.host); // hostname with port number (if any)
console.log(req.header('host')); // <same as above>
console.log(req.route.path); // exact defined route
console.log(req.baseUrl); // base path or group prefix
console.log(req.path); // relative path except path
console.log(req.url); // relative path with query|search params
console.log(req.originalUrl); // baseURL + url
// Full URL
console.log(`${req.protocol}://${req.header('host')}${req.originalUrl}`);
res.sendStatus(200);
});
module.exports = router;
index.js
const app = (require('express'))();
const users = require('./routes/users');
app.use('/api/users', users);
const server = require('http').createServer(app);
server.listen(8000, () => console.log('server listening'));
Output
http ....................................................................................... [protocol]
localhost .............................................................................. [hostname]
localhost:8000 ..................................................................... [headers.host]
localhost:8000 ..................................................................... [header('host')]
/profile/:id/:details ........................................................ [route.path]
/api/users ............................................................................. [baseUrl]
/profile/123/summary .......................................................... [path]
/profile/123/summary?view=grid&leng=en ........................ [url]
/api/users/profile/123/summary?view=grid&leng=en ..... [originalUrl]
Full URL:
http://localhost:8000/api/users/profile/123/summary?view=grid&leng=en
2) Now, directly from main module:
We'll define our route right in the starting file (i.e. app.js or index.js)
index.js
const app = (require('express'))();
app.get('/api/users/profile/:id/:details', (req, res) => {
console.log(req.protocol); // http or https
console.log(req.hostname); // only hostname (abc.com, localhost, etc)
console.log(req.headers.host); // hostname with port number (if any)
console.log(req.header('host')); // <same as above>
console.log(req.route.path); // exact defined route
console.log(req.baseUrl); // base path or group prefix
console.log(req.path); // relative path except path
console.log(req.url); // relative path with query|search params
console.log(req.originalUrl); // baseURL + url
// Full URL
console.log(`${req.protocol}://${req.header('host')}${req.originalUrl}`);
res.sendStatus(200);
});
const server = require('http').createServer(app);
server.listen(8000, () => console.log('server listening'));
Output
http ........................................................................ [protocol]
localhost ............................................................... [hostname]
localhost:8000 ...................................................... [headers.host]
localhost:8000 ...................................................... [header('host')]
/profile/:id/:details ......................................... [route.path]
.............................................................................. [baseUrl]
/profile/123/summary ........................................... [path]
/profile/123/summary?view=grid&leng=en ......... [url]
/profile/123/summary?view=grid&leng=en ......... [originalUrl]
Full URL:
http://localhost:8000/api/users/profile/123/summary?view=grid&leng=en
We can clearly see in above output that the only difference is of baseUrl which is empty string here. So, the originalUrl also changes & looks same as the url
//auth required or redirect
app.use('/account', function(req, res, next) {
console.log(req.path);
if ( !req.session.user ) {
res.redirect('/login?ref='+req.path);
} else {
next();
}
});
req.path is / when it should be /account ??
The reason for this is that Express subtracts the path your handler function is mounted on, which is '/account' in this case.
Why do they do this?
Because it makes it easier to reuse the handler function. You can make a handler function that does different things for req.path === '/' and req.path === '/goodbye' for example:
function sendGreeting(req, res, next) {
res.send(req.path == '/goodbye' ? 'Farewell!' : 'Hello there!')
}
Then you can mount it to multiple endpoints:
app.use('/world', sendGreeting)
app.use('/aliens', sendGreeting)
Giving:
/world ==> Hello there!
/world/goodbye ==> Farewell!
/aliens ==> Hello there!
/aliens/goodbye ==> Farewell!
It should be:
req.url
express 3.1.x
For version 4.x you can now use the req.baseUrl in addition to req.path to get the full path. For example, the OP would now do something like:
//auth required or redirect
app.use('/account', function(req, res, next) {
console.log(req.baseUrl + req.path); // => /account
if(!req.session.user) {
res.redirect('/login?ref=' + encodeURIComponent(req.baseUrl + req.path)); // => /login?ref=%2Faccount
} else {
next();
}
});
req.route.path is working for me
var pool = require('../db');
module.exports.get_plants = function(req, res) {
// to run a query we can acquire a client from the pool,
// run a query on the client, and then return the client to the pool
pool.connect(function(err, client, done) {
if (err) {
return console.error('error fetching client from pool', err);
}
client.query('SELECT * FROM plants', function(err, result) {
//call `done()` to release the client back to the pool
done();
if (err) {
return console.error('error running query', err);
}
console.log('A call to route: %s', req.route.path + '\nRequest type: ' + req.method.toLowerCase());
res.json(result);
});
});
};
after executing I see the following in the console and I get perfect result
in my browser.
Express server listening on port 3000 in development mode
A call to route: /plants
Request type: get
For those getting undefined from req.route.path that is correct.
Inside route handler, there's a route.
Inside middleware handlers, there's no route.
When using a middleware in express, your request object has several properties you can use to get the correct path:
req.baseUrl: /api/account
req.originalUrl: /api/account
req._parsedUrl.path: /account
req._parsedUrl.pathname: /account
req._parsedUrl.href: /account
req._parsedUrl._raw: /account
PLEASE NOTE: This applies to middlewares

Categories