Using express framework I did a middleware that handles a URI with a date as parameter.
app.get("/:date",function(req,res){
var result;
var myDate=req.params.date
if(moment(myDate).isValid()){
//some code here
}
else {
//some code here
}
}
and the http is for example:
https://theweb.com/December%2015,%2020
The middleware works well. But if the http finish in % as in
https://theweb.com/December%2015,%
it gives a error:
URIError: Failed to decode param '/December%2015,%'
and what I would like is to convert that error in the display of a message as Bad Request.
You can register an error handler like this:
app.use(function(err, req, res, next) {
console.log('hit error handler');
res.sendStatus(err.status || 500);
});
Note that the function must have 4 arguments, even if you don't use all of them. This function can then respond in whatever way you think is appropriate, in my example I've just sent back the status code. Express sets err.status to 400 for a URIError.
See https://expressjs.com/en/guide/error-handling.html
I had already use this middleware, but no result. It still gives same error when the URI is: https://myweb.com/December%2015,%
URIError: Failed to decode param '/December%2015,%'
Might be is the position of the code
var express = require('express');
var moment=require("moment")
var app = express();
app.use(express.static('public'));
app.use(function(err, req, res, next) {
console.log('hit error handler');
res.sendStatus(err.status || 500);
});
app.get("/", function (request, response,next) {
response.sendFile(__dirname + '/views/index.html');
});
app.get("/:date",function(req,res){
var result;
var myDate=req.params.date //From here comes the error
if(moment.unix(myDate).isValid()){
// some code
result={/*some code*/}
} else if(moment(myDate).isValid()){
//some code
result={/*some code*/}
else {
result={/*some code*/}
}
res.json(result)
})
Related
I am trying to use cors package in node and express environment to verify if requesting domain can access my resources. That part was not a problem following official documentation. The problem however is with response. If domain is not allowed to access - cors is sending error with stack trace (paths to files). Can this be avoided and just respond with status 401, something like res.status(401).end() ? I tried that but it gives me error because headers were already sent.
const cors = require("cors");
const corsOptions = async (req, callback) => {
let domain = getDomain(req);
if (domain === false) {
return callback(new Error("Not allowed by CORS"));
}
const isWhitelisted = await client.get(domain).catch(err => { console.log(err); });
if (isWhitelisted !== undefined) {
callback(null, true);
} else {
callback(new Error("Not allowed by CORS"));
}
};
app.use(cors(corsOptions));
So i was hoping to replace callback(new Error("Not allowed by CORS")); this part with just sending status 401 and ending stream so no errors with stack are printed in the client.
It can't be done, according to the source code what you are sending in callback as err is passed in the next function like
if(err) next(err);
But there is one easy workaround to what you want.
The cors() is returning a middleware(a function with three params req, res and next). So you can wrap the cors in your middleware and return whatever you want. If all the things are perfect you can use the cors.
const app = require('express')();
const cors = require("cors");
const corsMid = (req, res, next)=>{
const trueVal = true;
if(trueVal){
res.status(400).send("Error")
}else{
return cors()(req,res,next);
}
}
app.use(corsMid);
app.get('/', (req, res)=>{
res.json('Hello World');
})
app.listen(8080, ()=>{
console.log('started')
})
I found a workaround is you can construct the Error on a variable first, then remove the stack property from the error variable:
let err = new Error('Not allowed by CORS')
err.stack = ""
callback(err)
Hope this help!
I have setup a basic Node.js server using express (WebStorm default), and have attempted to make it upon request (from a pebble watch) to run a python script, and send the returned json in the form:
{"willCollide": 1, "time": 6000, "strength": "NA"}
back to the watch. I have just started looking into JavaScript so have very little experience, and would expect I'm doing most of this incorrectly.
Currently I experience an "Error: can't set headers after they are sent" and was wondering what is the correct method to send a json to a user upon a request?
I am also wondering whether this is indeed the best method to go about sending the data from the python script to a Pebble watch.
Below is the code in the JavaScript file being called on the request:
var express = require('express');
var router = express.Router();
var PythonShell = require('python-shell');
var options = {
mode: 'json'
};
var rain_data;
function run_py_script(data){
var pyshell = new PythonShell('dummy.py', options);
var ret_val;
/* Dummy data doesnt matter atm */
pyshell.send("dummy data"); // change to data
pyshell.on('message', function(message){
console.log(message);
ret_val = message;
console.log(message["willCollide"]); // debug check
});
pyshell.end(function(err){
if (err) {
console.log('error received from python script');
}
console.log('finished script');
});
return ret_val;
}
/* GET rain_track data. */
router.get('/', function(req, res, next) {
rain_data = run_py_script(null);
res.write(rain_data);
res.end();
});
module.exports = router;
Seems you're having trouble with asynchronous execution.
Your function run_py_script(data) does not return the final value until the end event is fired. Then, you'll be able to return the response back to the user.
Here you have two possible solutions:
Callbacks
Promises
I'm going to make an approach using a callback
First, run_py_script will have 2 arguments, data and a function to be called with the response, let's call it cb. cb will be called eventually with the final data.
function run_py_script(data, cb) {
// I'm going to summarize this code
var ret_val;
pyshell.on('message', function(message){
ret_val = message;
});
pyshell.end(function(err){
return err ? cb(null) : cb(ret_val);
});
// note there is no return statement
}
Now, let's create your controller:
router.get('/', function(req, res, next) {
run_py_script(null, function(rain_data) {
res.json(rain_data); // same as write().end() but more elegant
});
});
Final bonus: The node convention for cb is to be a 2 arguments function; the first argument uses to be an error which will be null is everything is ok, and the second argument is the data itself which will be null if error.
With this in mind the final code would be (summarizing)
function run_py_script(data, cb) {
// ...
pyshell.end(function(err){
return err ? cb(err, null) : cb(null, ret_val);
});
}
run_py_script(null, function(err, rain_data){
if (err){ return res.json(null); }
return res.json(data);
});
var database = require('database');
var express = require('express');
var app = express();
var cors = require('cors');
app.use(cors());
var bodyParser = require('body-parser');
var urlencodedParser = bodyParser.urlencoded({
extended: false
});
app.post('/dosomething', urlencodedParser, function(req, res) {
if (!req.body.a) {
res.status(500).send(JSON.stringify({
error: 'a not defined'
}));
return;
}
firstAsyncFunction(req.body.a, function(err, result) {
if (err) {
res.status(500).send('firstAsyncFunction was NOT a success!');
} else {
if (result.b) {
secondAsyncFunction(result.b, function(err, data) {
if (err) {
res.status(500).send('secondAsyncFunction was NOT a success!');
return;
}
res.send('EVERYTHING WAS A SUCCESS! ' + data);
});
}
else {
res.status(500).send('result.b is not defined');
}
}
});
});
function firstAsyncFunction(param, callback) {
//Some network call:
// Return either return (callback(null,'success')); or return (callback('error'));
var query = database.createQuery(someOptionsHere);
database.runDatabaseQuery(query, function(err, entities, info) {
if (err) {
return (callback('error'));
}
return (callback(null, 'success'));
});
};
function secondAsyncFunction(param, callback) {
//Some network call:
// Return either return (callback(null,'success')); or return (callback('error'));
var query = database.createQuery(someOptionsHere);
database.runDatabaseQuery(query, function(err, entities, info) {
if (err) {
return (callback('error'));
}
return (callback(null, 'success'));
});
};
var server = app.listen(process.env.PORT || 3000, function() {
var host = server.address().address;
var port = server.address().port;
console.log('App listening at http://%s:%s', host, port);
});
module.exports = app;
I have here a basic express http server. This server has one route, dosomething, which makes two network calls and tells the user if they were a success or not.
This is my entire webserver (this is a bare bones server of my actual server for example purposes). I am now concerned with this server crashing. Reading the docs for express I see there is a default error handler which will catch errors and prevent the server from crashing (http://expressjs.com/en/guide/error-handling.html). I have added the code:
function defaultErrorHandler(err, req, res, next) {
if (res.headersSent) {
return next(err);
}
res.status(500);
res.render('error', { error: err });
}
app.use(defaultErrorHandler);
This still crashes my server though. For example. I had a problem with my database returning an improper JSON response and inside of my firstAsyncFunction (not shown in the code) I tried to parse the JSON and it caused an error telling me it was improper JSON and the server crashed and was unable to take requests anymore until I restarted it. I would like to avoid this and have the default error handler send out a generic response back to the user when this occurs. I thought if I specified the defaultErrorHandler and put it inside of app.use that it would capture and handle all errors, but this does not seem to be the case? Inside of my async function for example you can see I am looking if an error was returned and if it was I send an error back to the user, but what if some other error occurs, how can I get express to capture and handle this error for me?
The defaultErrorHandler cannot handle exceptions that are thrown inside asynchronous tasks, such as callbacks.
If you define a route like:
app.get('/a', function(req, res) {
throw new Error('Test');
});
An error will be thrown, and in this case defaultErrorHandler will successfully catch it.
If the same exception occurs in an async manner, like so:
app.get('/a', function(req, res) {
setTimeout(function () {
throw new Error('Test');
}, 1000);
});
The server will crush, because the callback is actually in another context, and exceptions thrown by it will now be caught by the original catcher. This is a very difficult issue to deal with when it comes to callback.
There is more than one solution though. A possible solution will be to wrap every function that is prone to throw error with a try catch statement. This is a bit excessive though.
For example:
app.get('/a', function(req, res) {
setTimeout(function () {
try {
var x = JSON.parse('{');
}
catch (err) {
res.send(err.message);
}
}, 1000);
});
A nicer solution:
A nicer solution, would be to use promises instead, if it's possible, then for example you can declare a single errorHandler function like so:
function errorHandler(error, res) {
res.send(error.message);
}
Then, let's say you have to following function with fetches stuff from the database (I used setTimeout to simulate async behavior):
function getStuffFromDb() {
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve("{");
}, 100);
});
}
Notice that this function returns an invalid JSON string. Your route will look something like:
app.get('/a', function(req, res) {
getStuffFromDb()
.then(handleStuffFromDb)
.catch(function (error) { errorHandler(error, res) });
});
function handleStuffFromDb(str) {
return JSON.parse(str);
}
This is a very simplified example, but you can add a lot more functionality to it, and (at least theoretically) have a single catch statement which will prevent your server from crushing.
I am currently working on a project to develop an API manager to control an existing API.
It contains a list of "before" and "after" middlewares, which are used to do things like security checking and logging. And a "service" middleware to do http request to the existing API. But the problem is that I want to make the order the middleware being executed to be dynamic, meaning that I could load some configuration file to change the order the middleaware get executed every time the request comes in.
here is my previous code:
'use strict';
// Loading the express library
var express = require('express');
var app = express();
var service = require('./routes/index');
// Testing configurable middleware
var confirguration = {
before1: {
priority: 100,
enable: true
},
before2: {
priority: 80,
enable: true
},
service: {
priority: 50,
enable: true
},
after1: {
priority: 30,
enable: true
},
after2: {
priority: 10,
enable: true
}
}
var before1 = require('./example_middleware/before1');
var before2 = require('./example_middleware/before2');
var after1 = require('./example_middleware/after1');
var after2 = require('./example_middleware/after2');
// Fake request to simulate the /service
var fakeRequest = require('./example_middleware/fake_request');
// Function to sort the order of the middleware to be executed
var sortConfig = function(confirguration){
var sortable = [];
for (var middleware in confirguration)
// To make middlewares configurable
if (confirguration[middleware]['enable'] == true){
sortable.push([middleware, confirguration[middleware]['priority']]);
}
sortable.sort(function(a, b) {return b[1] - a[1]});
return sortable;
}
// var sortedConfig = [];
var middlewareSet = new Array();
app.use('/test', function(request, response, next){
var middleware;
var sortedConfig = sortConfig(confirguration);
for (var i in sortedConfig){
switch(sortedConfig[i][0]){
case 'before1':
middleware = before1;
break;
case 'before2':
middleware = before2;
break;
case 'service':
middleware = fakeRequest;
break;
case 'after1':
middleware = after1;
break;
case 'after2':
middleware = after2;
break;
}
// console.log(sortedConfig[i][0]);
// Execute the middleware in expected order
middlewareSet.push(middleware);
}
// request.sortedConfig = sortedConfig;
console.log(middlewareSet);
console.log('middleware list sorted');
next();
});
app.use('/test', middlewareSet);
But I keep getting the same error message coming from the app.use() at the last line:
app.use() requires middleware functions
It works if I use:
app.use('/test', [before1, before2, fakeRequest, after1, after2]);
But it's not dynamic though, what did I misunderstand? There must be a way to do this in express.js.
Thanks in advance.
EDIT:
I modified my code according to Ryan's answer, here is the code:
var async = require('async');
app.use('/test', configurableMiddleWare);
function configurableMiddleWare(req, res, next) {
var operations = [];
var middleware;
var sortedConfig = sortConfig(confirguration);
// push each middleware you want to run
sortedConfig.forEach(function(fn) {
switch(fn[0]){
case 'before1':
middleware = before1;
break;
case 'before2':
middleware = before2;
break;
case 'service':
middleware = fakeRequest;
break;
case 'after1':
middleware = after1;
break;
case 'after2':
middleware = after2;
break;
}
operations.push(middleware); // could use fn.bind(null, req, res) to pass in vars
});
console.log('middleware list sorted');
// now actually invoke the middleware in series
async.series(operations, function(err) {
if(err) {
// one of the functions passed back an error so handle it here
return next(err);
}
// no errors so pass control back to express
next();
});
}
Just to make sure I haven't made any mistakes in my test middleware, here is an example of one of them:
'use strict';
var express = require('express');
var router = express.Router();
router.route('/')
.all(function(request, response, next){
console.log('This is middleware BEFORE1');
next();
});
module.exports = router;
Now, when I run my application, I got the following error from npm:
TypeError: Cannot call method 'indexOf' of undefined
at Function.proto.handle (/Users/jialunliu/Documents/SOA_project/FAT-LADY/node_modules/express/lib/router/index.js:130:28)
at router (/Users/jialunliu/Documents/SOA_project/FAT-LADY/node_modules/express/lib/router/index.js:35:12)
at /Users/jialunliu/Documents/SOA_project/FAT-LADY/node_modules/async/lib/async.js:610:21
at /Users/jialunliu/Documents/SOA_project/FAT-LADY/node_modules/async/lib/async.js:249:17
at iterate (/Users/jialunliu/Documents/SOA_project/FAT-LADY/node_modules/async/lib/async.js:149:13)
at async.eachSeries (/Users/jialunliu/Documents/SOA_project/FAT-LADY/node_modules/async/lib/async.js:165:9)
at _asyncMap (/Users/jialunliu/Documents/SOA_project/FAT-LADY/node_modules/async/lib/async.js:248:13)
at Object.mapSeries (/Users/jialunliu/Documents/SOA_project/FAT-LADY/node_modules/async/lib/async.js:231:23)
at Object.async.series (/Users/jialunliu/Documents/SOA_project/FAT-LADY/node_modules/async/lib/async.js:608:19)
at configurableMiddleWare (/Users/jialunliu/Documents/SOA_project/FAT-LADY/app.js:135:11)
Which is coming from the line
async.series(operations, function(err){})
I am keep getting this kind of error message, saying the function could not read from this array of functions "operations"....
I think you are on the right track, you will just need to tweak a few things. I would register one top level function with app.use() and then do all of your dynamic stuff within that function. Updating my answer to a working example. Be sure to install async first npm install --save async
// define all middleware functions
var middleware = {
mw1: function(req, res, next) {
console.log('mw 1');
next();
},
mw2: function(req, res, next) {
console.log('mw 2');
next();
},
mw3: function(req, res, next) {
console.log('mw 3');
next();
},
mw4: function(req, res, next) {
console.log('mw 4');
next();
}
};
// register our "top level function"
app.use(configurableMiddleware);
var requestCount = 1; // this is just for the working example
function configurableMiddleware(req, res, next) {
var isEvenRequest = requestCount++ % 2 === 0; // simple logic to alternate which "configurable" middleware to use
var operations; // in the real world you could build this array dynamically, for now we just hardcode two scenarios as an example
// Each request to http://localhost:3000 will alternate which middleware is used, so you will see a different log each time
if(isEvenRequest) {
console.log('Even request should log mw2 and mw4');
// .bind(null, req, res) makes sure that the middleware gets the request and response objects when they are invoked,
// as of this point they still haven't been invoked...
operations = [middleware.mw2.bind(null, req, res), middleware.mw4.bind(null, req, res)];
}
else {
console.log('Odd request should log mw1 and mw3');
operations = [middleware.mw1.bind(null, req, res), middleware.mw3.bind(null, req, res)];
}
// invoke each middleware in series - you could also do async.parallel if the order of middleware doesn't matter
// using the async module: https://github.com/caolan/async
async.series(operations, function(err) {
if(err) {
console.log('There was a problem running the middleware!');
return next(err);
}
// all middleware has been run
next();
});
}
For more info on .bind() see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
connect-sequence: a dedicated node module for that specific purpose:
You can just use the module connect-sequence which is designed for that purpose:
npm install --save connect-sequence
see:
the npmjs page: https://www.npmjs.com/package/connect-sequence
or the github project: https://github.com/sirap-group/connect-sequence
and then, here an example of usage:
/**
* Product API
* #module
*/
var ConnectSequence = require('connect-sequence')
var productsController = require('./products.controller')
module.exports = productRouter
function productRouter (app) {
app.route('/api/products/:productId')
.get(function (req, res, next) {
// Create a ConnectSequence instance and setup it with the current `req`,
// `res` objects and the `next` callback
var seq = new ConnectSequence(req, res, next)
// build the desired middlewares sequence thanks to:
// - ConnectSequence#append(mid0, ..., mid1),
// - ConnectSequence#appendList([mid0, ..., mid1])
// - and ConnectSequence#appendIf(condition, mid)
if (req.query.filter) {
seq.append(productsController.filter)
}
if (req.query.format) {
seq.append(
productsController.validateFormat,
productsController.beforeFormat,
productsController.format,
productsController.afterFormat
)
}
// append the productsController.prepareResponse middleware to the sequence
// only if the condition `req.query.format && req.formatedProduct` is true
// at the moment where the middleware would be called.
// So the condition is tested after the previous middleware is called and thus
// if the previous modifies the `req` object, we can test it.
seq.appendIf(isProductFormatted, productsController.prepareResponse)
seq.append(productsController.sendResponse)
// run the sequence
seq.run()
})
app.param('productId', function (req, res, next, id) {
// ... yield the product by ID and bind it to the req object
})
function isProductFormatted (req) {
return Boolean(req.formatedProduct)
}
}
This is open source, PR are welcome!
If you like and use connect-sequence, but if you find bug or need some new features, feel free to post issues or submit pull requests!
Based on the idea behind #Ryan's code I came up with this function. It executes a list of middleware in order binding the variables as needed, allowing everything to be executed by just executeMiddlewareList([middleware1, middleware2...], req, res, next);. For each middlewarereq, res is passed and the callback from async.eachSeries. This means when next() is called inside the middleware, then next one will be handled from the list. If middleware throws an error with next(err), execution will stop and you can manually handle this.
function executeMiddlewareList (middlewareList, req, res, next) {
async.eachSeries(middlewareList, function(middleware,callback) {
middleware.bind(null,req,res,callback)()
}, function(err) {
if (err) return res.status(500).json({error: err});
next();
})
}
function testMid (number) {
return function (req, res, next) {
log.debug('req.test from', req.test, " to ", number);
req.test=number;
next();
}
}
router.get('/test', function(req, res, next) {
m.executeMiddlewareList([test(1), test(2)], req, res, next);
//Output: req.test from undefined to 1
// req.test from 1 to 2
}, function (req,res) {
//Do stuff after the executeMiddlewareList, req.test = 2
})
Finally, I find the answer according the Ryan's, the code would look like this:
function configurableMiddleWare(req, res, next) {
var operations = [];
var middleware;
var sortedConfig = sortConfig(confirguration);
// push each middleware you want to run
sortedConfig.forEach(function(fn) {
switch(fn[0]){
case 'before1':
middleware = before1;
break;
case 'before2':
middleware = before2;
break;
case 'service':
middleware = fakeRequest;
break;
case 'after1':
middleware = after1;
break;
case 'after2':
middleware = after2;
break;
}
console.log(fn[0]);
console.log(middleware);
operations.push(middleware.bind(null, req, res)); // could use fn.bind(null, req, res) to pass in vars
});
console.log('middleware list sorted');
// now actually invoke the middleware in series
async.series(operations, function(err) {
if(err) {
// one of the functions passed back an error so handle it here
return next(err);
}
console.log('middleware get executed');
// no errors so pass control back to express
next();
});
}
app.use('/test', configurableMiddleWare);
The key step is indeed the operations.push(middleware.bind(null, req, res));
Which to be honest, I don't understand what does it mean exactly. I know this is passing "req" and "res" variable into the middleware, but I don't get what the point of the "null" in the front. Much appreciated if someone could help me clarify this.
I am a newbie in Node.js (and Express) and I am trying to make sense of this. Say I have a website with 3 pages (can be GET or POST): /, /page1, /page2. What should I do so that every page is handled by a separate JS file?
app.all('/', function(request, response)
{
// Get home.js to handle this request and response
});
app.all('/page1', function(request, response)
{
// Get page1.js to handle this request and response
});
app.all('/page2', function(request, response)
{
// Get page2.js to handle this request and response
});
Better yet, is there a way to define a wildcard so there is not so much repetition? Something like this:
app.all('*', function(request, response)
{
// Get *.js to handle this request and response. * is whatever the URI string is
});
The trick here is that app is local to the file that creates it. So you have to get that object to the scope of the other files.
Each other file should export a funciton that you can pass your app instance to so it can register new routes. An approach like this should work.
// home.js
exports.register = function(app) {
app.all('/', function(request, response) { ... });
};
// page1.js
exports.register = function(app) {
app.all('/page1', function(request, response) { ... });
};
// page2.js
exports.register = function(app) {
app.all('/page2', function(request, response) { ... });
};
//server.js - setup the app
app = express.createServer();
require('./home').register(app);
require('./page1').register(app);
require('./page2').register(app);
And for the second part of your question, you want to share some setup methods?
app.all('*', function(req, res, next) {
res.header 'x-snazzy-header', 'Im so snazzy'
next()
});
app.all('/page/:id', function(req, res) {
res.send('content for page #'+ req.params('id'));
});
First, you can use * or named params like /users/:id, to match a range of routes. And if you want to do some common setup, you can actually execute 2 routes. The route handler takes an optional third argument next. When invoked, it will try to find the next route to match. So you can setup things like common headers for a bunch of routes with it.
Continuing my discussion with #Alex. Here's how I did it. Any gotcha?
// app.js
var EXPRESS = require('express');
var URL = require('url');
var PATH = require('path');
var app = EXPRESS.createServer();
app.all(/^\/([a-zA-Z0-9-]+)$/, function(request, response, next)
{
var page = request.params[0];
if (PATH.existsSync(__dirname + '/' + page + '.js'))
{
require('./' + page).handleRequest(request, response, next);
}
else
{
next();
}
});
app.all('*', function(request, response)
{
response.send('Catch all');
});
// --- truncated for brievity
// page1.js
exports.handleRequest = function(request, response, next)
{
response.send('Howdy!');
};