Is it possible, using Express 4, to send a JSON response to the front-end indicating that there was an error, as well as calling next(err) inside the Express middleware, so that the error can be handled by the server as well? Or are these calls completely mutually exclusive?
My current assumption is that you can do this:
app.get('/', function(req, res, next) {
res.json({ error : true });
});
and you can do this:
app.get('/', function(req, res, next) {
next(new Error('here goes the error message');
});
but that you can't do this
app.get('/', function(req, res, next) {
res.json({ error : true });
next(new Error('here goes the error message');
});
and you can't do this:
app.get('/', function(req, res, next) {
next(new Error('here goes the error message');
res.json({ error : true });
});
They aren't mutually exclusive. For example (instead of middleware I'm using route handlers to demonstrate, but the principle is the same for both):
app.get('/', function(req, res, next) {
res.json({ error : true });
next(new Error('something happened'));
});
app.get('/another', function(req, res, next) {
next(new Error('something happened'));
});
app.use(function(err, req, res, next) {
console.error(err);
if (! res.headersSent) {
res.send(500);
}
});
You could check res.headersSent in the error handler to make sure that a response is sent (if not, the error handler should send one itself).
Related
Can someone explain to me about the different between two ways exception error handling in code Express JS below:
const express = require('express');
const app = express();
app.get('/test', (req, res, next) => {
// the first way:
throw new Error('my error message');
// the second way:
next(new Error('my error message'));
});
app.use((err, req, res, next) => {
res.status(err.status || 500).send(err.message || 'Internal Server Error');
});
app.listen(3000, () => console.log('Welcome to ExpressJS'));
It returns the same result handled by error middleware but what is the difference here?
Nothing, based on the source code.
try {
fn(req, res, next);
} catch (err) {
next(err);
}
I am using express with a pattern like this :
app = express();
router = express.Router();
router.use((req, res, next) => {
console.log("my middleware before");
next();
});
router.get('/foo', (req, res, next) => {
console.log("My route");
res.send("<h1>Hello</h1>")
next();
});
router.use((req, res, next) => {
console.log("my middleware after");
});
app.use("/", router);
app.get("*", (req, res, next) => {
console.log("page not found");
throw new Error("Not Found");
});
app.use((err, req, res, next) => {
console.log("Error occure");
res.send("<h1>Error</h1>");
});
app.listen(3000);
When I request '/foo' I would like to have
> my middleware before
> My route
> my middleware after
<h1>Hello</h1>
And when I request anything else :
> page not found
> Error occure
<h1>Error</h1>
But the page not found route is executed in each case, even if route '/foo' is done.
How can I get it working ?
When I run your code, I do not get the output you show, so something about your real code is apparently different than what you show in your question.
I do get a slightly confusing output and that happens because the browser sends both the /foo request and a /favicon.ico request. When I run it, the /foo request generates the desired output. The /favicon.ico request generates some middleware output and then gets stuck in the router.
If you filter out the /favicon.ico route (so that it doesn't confuse things) by adding this as the first route:
app.get("/favicon.ico", (req, res) => {
res.sendStatus(404);
});
Then, I get exactly this output in the server logs when I request /foo:
my middleware before
My route
my middleware after
Which is exactly what you asked for.
There is, however, a general problem with this:
router.use((req, res, next) => {
console.log("my middleware after");
});
Because it will catch and hang any legit requests that haven't yet had a response sent. You can't really code it that way unless you only don't call next() if a response has already been sent.
As a bit of a hack, you could do this:
router.use((req, res, next) => {
console.log("my middleware after");
// if response hasn't yet been sent, continue routing
if (!res.headersSent) {
next();
}
});
But, there is probably a better way to solve whatever problem you're actually trying to solve. If, in the future, you describe your real problem rather than a problem you have with your solution, then you allow people to offer a wider range of solutions to your real problem including things you haven't even thought of to try. As your question is written right now, we're stuck down the solution path you followed and don't know what the original problem was. That is, by the way, referred to as an XY Problem.
Do this
app = express();
router = express.Router();
router.use((req, res, next) => {
console.log("my middleware before");
next();
});
router.get('/foo', (req, res, next) => {
// use locals to record the fact we have a match
res.locals.hasMatch = true
console.log("My route");
res.send("<h1>Hello</h1>")
next();
});
router.use((req, res, next) => {
console.log("my middleware after");
});
app.use("/", router);
app.get("*", (req, res, next) => {
console.log("page not found");
throw new Error("Not Found");
});
app.use((err, req, res, next) => {
// check locals to see if we have a match
if (!res.locals.hasMatch) {
console.log("Error occure");
res.send("<h1>Error</h1>");
}
});
app.listen(3000);
You can utilize middlewares and even nest them.
You can implement it like this:
Middlewares
const before = (req, res, next) => {
console.log("my middleware before");
next(); // Supply next() so that it will proceed to the next call,
// in our case, since this is supplied inside the router /foo, after this runs, it will proceed to the next middleware
};
const after = (req, res, next) => {
console.log("my middleware after");
};
Route
// Supply "before" middleware on 2nd argument to run it first when this route is called
router.get('/foo', before, (req, res, next) => {
console.log("My route");
res.send("<h1>Hello</h1>");
next(); // Call next() to proceed to the next middleware, or in "after" middleware
}, after); // Supply the "after" middleware
Once ran, it will proceed with this desired result sequence:
> my middleware before
> My route
> my middleware after
Unmatched Routes Handler
Instead of this
app.get("*", (req, res, next) => {
console.log("page not found");
throw new Error("Not Found");
});
You can implement it like this instead, this is after your app.use("/", router); -- This will handle your unmatched routes:
Sources:
https://stackoverflow.com/a/44540743/6891406
https://stackoverflow.com/a/16637812/6891406
app.use((req, res, next) => {
console.log("page not found");
res.json({ error: 'Page not Found' })
});
I am trying to use express-jwt for jwt validation.
It is working but it gives only 401 status and not any custom validation message.
app.use(function (err, req, res, next) {
if (err.name === "UnauthorizedError") {
res.status(402).send("invalid token...");
}
});
app.get("/", jwt({ secret: "MY_SECRETT_KEY" }), (req, res) => {
console.log('eeeeeeeeeeeeeeeeeeeeeee', req.user);
res.json({ message: "success" });
});
It is not sending 402 status, but default 401 only.
Need some help in this.
app.get("/", jwt({ secret: "MY_SECRETT_KEY" }), (req, res) => {
console.log('eeeeeeeeeeeeeeeeeeeeeee', req.user);
res.json({ message: "success" });
});
app.use(function (err, req, res, next) {
if (err.name === "UnauthorizedError") {
res.status(402).send("invalid token...");
}
});
Error handlers should be added at the end.
You can imagine middlewares as steps in a stair and the error handler is the floor. You are the request and you are going down the stairs, when you trip/fail from one of the steps, you fall straight to the floor.
The way I understand it, if I do something like:
app.use('/something', function(req, res, next) {
// some content here
});
This basically means that if there's a request to 'something', then the middleware (my function) is executed before the next function.
So if I have something like this to handle a GET request,
app.get('/something', function(req, res, next) {
console.log('hello');
});
Then 'hello' is going to be printed out after my original function has finished executing.
But how do I make it so that my middleware function is just executed when I ONLY make a GET request and not a POST request?
For a GET only middleware, just do the following
// Get middleware
app.get('/something', function(req, res, next) {
console.log('get hello middleware');
next();
});
// GET request handler
app.get('/something', function(req, res) {
console.log('get hello');
res.end();
});
// POST request handler
app.post('/something', function(req, res) {
console.log('post hello');
res.end();
});
app.post('/something', your_middleware, function(req, res, next) {
console.log('hello');
});
Only during the post request your_middleware will be executed.
your_middleware should be a function as follows:
function(req, res, next){
....
next()
}
you can pipe in as many middlewares you want in this way for a specific route and request type
At the moment I have the following which sits below all my other routes:
app.get('*', function(req, res){
console.log('404ing');
res.render('404');
});
And according to the logs, it is being fired even when the route is being matched above. How can I get it to only fire when nothing is matched?
You just need to put it at the end of all route.
Take a look at the second example of Passing Route Control:
var express = require('express')
, app = express.createServer();
var users = [{ name: 'tj' }];
app.all('/user/:id/:op?', function(req, res, next){
req.user = users[req.params.id];
if (req.user) {
next();
} else {
next(new Error('cannot find user ' + req.params.id));
}
});
app.get('/user/:id', function(req, res){
res.send('viewing ' + req.user.name);
});
app.get('/user/:id/edit', function(req, res){
res.send('editing ' + req.user.name);
});
app.put('/user/:id', function(req, res){
res.send('updating ' + req.user.name);
});
app.get('*', function(req, res){
res.send('what???', 404);
});
app.listen(3000);
Alternatively you can do nothing because all route which does not match will produce a 404. Then you can use this code to display the right template:
app.error(function(err, req, res, next){
if (err instanceof NotFound) {
res.render('404.jade');
} else {
next(err);
}
});
It's documented in Error Handling.
I bet your browser is following up with a request for the favicon. That is why you are seeing the 404 in your logs after the 200 success for the requested page.
Setup a favicon route.
You can this at the end of all routes,
const express = require('express');
const app = express();
const port = 8080;
// All your routes and middleware here.....
app.use((req, res, next) => {
res.status(404).json({
message: 'Ohh you are lost, read the API documentation to find your way back home :)'
})
})
// Init the server here,
app.listen( port, () => {
console.log('Sever is up')
})
Hope it helpful, I used this code in bottom of routes
router.use((req, res, next) => {
next({
status: 404,
message: 'Not Found',
});
});
router.use((err, req, res, next) => {
if (err.status === 404) {
return res.status(400).render('404');
}
if (err.status === 500) {
return res.status(500).render('500');
}
next();
});
You can use this
const express = require('express');
const app=express();
app.set('view engine', 'pug');
app.get('/', (req,res,next)=>{
res.render('home');
});
app.use( (req,res,next)=>{
res.render('404');
})
app.listen(3000);
I wanted a catch all that would render my 404 page only on missing routes and found it here in the error handling docs https://expressjs.com/en/guide/error-handling.html
app.use(function (err, req, res, next) {
console.error(err.stack)
res.status(404).render('404.ejs')
})
This worked for me.
Very simple you can add this middleware.
app.use(function (req, res, next) {
//Capture All 404 errors
res.status(404).render("404.ejs")
})
404 error in a service is typically used to denote that the requested resource is not available. In this article we will see how to handle 404 error in express.
We need to handle the Error and Not-Found collectively as
Write two separate middleware for each,
// Import necessary modules
const express = require('express');
// Create a new Express app
const app = express();
// Define routes and middleware functions
app.get('/', (req, res) => {
res.send('Hello World!');
});
// Catch 404 Not Found errors and forward to error handler
app.use((req, res, next) => {
const error = new Error('Not Found');
error.status = 404;
next(error);
});
// Error handler middleware function
app.use((err, req, res, next) => {
// Set status code and error message based on error object
res.status(err.status || 500);
res.send({
error: {
message: err.message
}
});
});
// Start the server
app.listen(3000, () => {
console.log('Server started on port 3000');
});