I'm working on a small app running on the MEAN stack, and have hit an annoying snag: My backend app (Node with Express) is running at http://localhost:3000 and working just fine, but my frontend client app (Javascript with AngularJS) is running at http://localhost:8000, which means requests sent from Angular are received and responded to, but are rejected once they arrive because they're interpreted as coming from a different origin.
I was able to fix this with relatively little drama, by making my 'show me all the stuff' method look something like this:
exports.index = function(req, res) {
Region.find({}, function(err, docs) {
if(!err) {
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:8000');
res.json(200, { regions: docs });
} else {
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:8000');
res.json(500, { message: err });
}
});
}
The res.setHeader('Access-Control-Allow-Origin', 'http://localhost:8000'); line is the one that I added to tell the browser it was fine to accept the response and stop bothering me about it; the problem now is that I have to add this stupid line to every single response that's sent from anywhere, and I'm convinced I must be missing some way to just change the default headers to include the Access-Control-Allow-Origin entry forever.
In a perfect world, I'd be able to flip this on and off based on what environment the code was being executed in, but I'd totally settle for a code block in app.js that I could at least remove one time instead of trying to track down 75 instances of res.setHeader. I figure there must be a way to change the .json method hiding behind res at its base, but the docs don't offer any insight into how I might do that, not to mention whether it's a terrible idea. Any thoughts?
edit
I thought (as was suggested) that configuring application-level middleware was the key. Here's the code that I added to my app.js file:
// allow CORS:
app.use(function (req, res, next) {
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:8000');
next();
});
This, however, yielded the same error ("No 'Access-Control-Allow-Origin' header is present on the requested resource.") as before.
Try this one as a middleware:
app.use(function(req, res, next) {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST');
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type, Authorization');
next();
});
I just ran into the same issue and tried the same snippet above. It did the trick.
// allow CORS:
app.use(function (req, res, next) {
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:8000')
next()
})
IMPORTANT: I had to place it above all other app.use(xyz) entries, just like #rev_bird mentioned he did with the CORS module. Try it.
you can make a common middleware using .use() or can use npm packages like express-interceptor also to intercept the response
Related
Scenario is:
I want to load a webpage (it loads with sendFile) then I want to create a setTimeout timer and redirect the user to another webpage with sendFile also, but I'm getting the headers error.
I've tried a CORS approach like said in other answers (including all headers before each request with app.use)
I've tried to res.end() but it won't just load the first website.
App use
app.use((req, res, next) => {
res.append('Access-Control-Allow-Origin','*');
res.append('Access-Control-Allow-Methods','GET,POST,PUT');
res.append('Access-Control-Allow-Headers','Content-Type');
next();
})
Redirect of first webpage show.
res.redirect(`/pantallas/reloj?tiempo=${Response.rows[0].Tiempo_refresco}&next=${Response.rows[1].Ruta_pantalla}`)
res.end()
Route to show first webpage and then redirecting to second one.
// Being tiempo = 5 and next = '/pantallas/barras'
app.get('/pantallas/reloj', (req, res) => {
res.sendFile(path.join(__dirname + '/pantallas/reloj.html'));
if(req.query.tiempo)
{
setTimeout(() => {
res.sendFile(path.join(__dirname + req.query.next));
}, req.query.tiempo * 1000);
}
})
At this point it loads the first website then after the timer is done it just throws the headers error, any help with it?
This is happening because you are sending the response twice. Hence, the error:
Can't set headers after they are sent.
You cannot return a response twice from the same route, you are sending a file first via the response and then once again inside the set timeout. This is not permitted.
Why is this happening?
The res object in Express is a subclass of Node.js's
http.ServerResponse (read the http.js source). You are allowed to call
res.setHeader(name, value) as often as you want until you call
res.writeHead(statusCode). After writeHead, the headers are baked in
and you can only call res.write(data), and finally res.end(data).
The error "Error: Can't set headers after they are sent." means that you're already in the Body or Finished state, but some function tried to set a header or statusCode. When you see this error, try to look for anything that tries to send a header after some of the body has already been written. For example, look for callbacks that are accidentally called twice, or any error that happens after the body is sent.
EDIT:
You can try something along these lines:
app.use(function(req, res, next) {
if (req.query.tiempo) {
setTimeout(() => {
res.sendFile(path.join(__dirname + req.query.next));
}, req.query.tiempo * 1000);
}
next();
});
app.get('/pantallas/reloj', function(req, res, next) {
res.sendFile(path.join(__dirname + '/pantallas/reloj.html'));
next();
});
More here: Express sendfile and redirect url
I defined controller which has only one function and this function is set as a callback to `GET /user/ request:
class UserController {
async getAllUserData(req, res) { /* get data, return res */ }
async changepassword(req, res) { /* change password, return res */ }
}
const ctrl = new UserController();
api.get('/user', middlewareA, ctrl.getAllUserData);
api.post('/changepassword', ctrl.changepassword);
export default api;
This works fine, middleware is applied only to GET /user route. However, I wanted to specify middleware to all functions defined in single controller file and apply this middleware on the level of my index.js file, like this:
/* initialization... */
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Token');
res.header('Access-Control-Allow-Methods', 'POST, GET, PUT, DELETE, OPTIONS');
next();
});
app.use(middleware, UserController);
app.listen(3000, () => {
console.log('Example app listening on port 3000!');
});
However, in the second snippet, as I put middleware before UserController, this middleware is applied to all methods - especially to OPTIONS request which is sent by browser - even thought UserController has only 2 methods which have GET and POST methods.
Why the middleware in the second snippet is applied to all methods? Do I have to put it separately to every function in every controller?
In express, everything that you chain with app.use() is called in order. This is what will happen in your second snippet:
Request starts
Runs header middleware
Runs your 'middleware' (the one included before UserController)
Runs UserController
Runs routes middleware
Return response
The main thing to note is that middleware and route middleware are the same thing and they will happen in whichever order you are app.use()'ing them.
You can either assign the middleware like you did in your first snippet, or check the URL in the middleware. Another option would be to group your URL's.
app.use('/example', middleware);
app.use('/example', UserController);
// ........
api.get('/example/user', middlewareA, ctrl.getAllUserData);
api.post('/example/changepassword', ctrl.changepassword);
There are plenty of examples in the express documentation.
I already use the middleware facility in Express.js by means of app.use() to hook on all requests and log a message, but found the fact that res.statusCode is nearly always 200, which I checked is not correct. I presume this is the default value and since it is not yet replaced with my own statusCode with res.send(204) well, is always 200.
I know I can log the message after every res.send(x) in every request, but as you might imagine, that is cumbersome.
So, the question is:
Where/How can I hook my middleware function with the conditions that is only in one place and res.statusCode is the one that the client really sees?
Relevant code:
// Log API requests.
app.use(function (req, res, next) {
logger.info('API request.', {
module: 'core'
data : {
req: {
method: req.method,
url : req.url,
ip : req.ip
},
res: {
status_code: res.statusCode // Always 200 and not the really one.
}
}
});
next();
});
app.use(app.router);
If I move app.use(app.router) the middleware don't get executed.
res inherits from http.ServerResponse in the standard library. This means that it is an EventEmitter with a finish event. So in your middleware, register for the finish event and do the logging when that fires:
app.use(function (req, res, next) {
res.on('finish', function() {
logger.info('API request.', {
module: 'core'
data : {
req: {
method: req.method,
url : req.url,
ip : req.ip
},
res: {
status_code: res.statusCode
}
}
});
});
next();
});
You could, of course, register with res.once, but the finish event is guaranteed to fire only once, so that shouldn't be necessary unless there's a bug in the standard library.
This won't necessarily occur "after res.send but before the response leaves the server" as you request - the docs on the finish event only claim that
[T]his event is emitted when the last segment of the response headers and body have been handed off to the operating system for transmission over the network.
So, some of the response may have already left the server. However, I believe it is the closest you'll get without monkey-patching express, which I would advise against.
There are many ways to mock requests using things like supertest and nock but what I'm looking to do is to be able to create fake request objects, as if the request was going to a different URL, that can be passed on to other processing functions.
app.get("/render", function(req, res) {
// how do I do this?
var fake = createFakeRequest("/bar/?baz=qux", req);
// I want doStuff to believe the request came to /bar/?baz=qux
doStuff(fake, function(err, result) {
res.send(result);
});
});
I'm aware I could modify all of he variables such as req.path, req.query, req.url but I'm worried I may miss something, and it seems like someone out there must have already solved this problem.
The reason I need this behavior is that for any given URL in my CMS, there can be multiple drafts. Each draft will have different content elements, but those individual content elements may have logic based on the URL or query parameters. So even though the request came in as /render/?draft=1&url=/foo/bar/, I want the content element processors to believe the request came in to /foo/bar/ and be oblivious to the version system which actually handled the initial HTTP request.
Not sure to understand but seems like url rewriting, so using a middleware could work
function urlRewrite(req, res, next){
req.url ="...";
next();
}
and so
app.use(urlRewrite);
Be sure to use it at the right place (depending on your server goal)
Cause we maybe need params before the rewrite... and if rewrite, you may need it after...
EDIT
In my framework:
server.before(extractPath);
server.before(urlParams);
server.before(pathParams);
server.get("/foo", route.foo);
So I could write
server.before(extractPath);
=> server.before( function(req, res, next){
urlRewrite(req, res, function(){
extractPath(req, res, next);
}
});
server.before(urlParams);
server.before(pathParams);
server.get("/foo", route.foo);
If urlRewrite depends on urlParams, I could write
server.before(extractPath);
server.before(urlParams);
=> server.before( function(req, res, next){
urlRewrite(req, res, function(){
extractPath(req, res, function(){
urlParams(req, res, next);
});
});
});
server.before(pathParams);
server.get("/foo", route.foo);
As I said, it depends on your framework
Passport.js offers great authentication for node.js and Express including a middleware solution:
ensureAuthenticated = function(req, res, next) {
if (req.isAuthenticated()) {
return next();
}
return res.redirect("/login");
};
How can I use this middleware in the express-resource module? Unfortunately,
app.resource('users', ensureAuthenticated, require('./resources/users'));
doesn't work.
I know this is a little too late, and the original post was answered, however, I was looking for the same answer and found a solution I thought others might want to know.
Just make sure ensureAuthenticated is called from passport.
app.resource('users', passport.ensureAuthenticated, require('./resources/users'));
It is found here: https://gist.github.com/1941301
Workaround. Ensure authentication on all requests and ignore requests going to /auth and /auth/callback.
app.all('*', function(req, res, next) {
if (/^\/auth/g.test(req.url)) {
return next();
} else if (req.isAuthenticated()) {
return next();
} else {
return next(new Error(401));
}
});
You will need to do some magic in order to have each resource use the authentication middleware. The following gist explains how to accomplish this by using a menu structure.
https://gist.github.com/3610934
Essentially, you need the following logic:
app.all('/' + item.name + '*', ensureAuthenticated, function (req, res, next) {
next();
});
You could then in your menu structure specify what resources are protected, and if they require any kind of specific permissions, even down to the HTTP method level.
Hopefully this helps someone!
I'm not sure if express-resource has the option of inserting middleware into specific resources, but you could always insert checking if you are in that resource inside the middleware.
ensureAuthenticated = function(req, res, next) {
if (!/^users/.test(req.url) || req.isAuthenticated()) {
return next();
}
return res.redirect("/login");
};
This works:
app.get('/', ensureAuthenticated, admin.index);
As long as your strategy is setup correctly. I'm using the local strategy. Check out the guide:
http://passportjs.org/guide/username-password.html
I was looking up this topic as well, and found https://npmjs.org/package/express-resource-middleware. Haven't tested it, though.