Node Express custom 404 page - node.js

I have a pretty standard Node Express app with a global 404 handler like.
app.get('*', function(req, res){
res.status(404);
res.render('404page');
});
Now in some routes I have code like
app.get('/store/:product', function (req, res) {
if (productNotFound) return res.sendStatus(404);
});
What I would like is the res.sendStatus(404) to redirect to the error page WITHOUT having to change it to res.sendStatus(404).render('404page').
Is that possible?

Just override .sendStatus of res object.
Register this middleware right after creating the express app:
app.use((req, res, next) => {
const sendStatus = res.sendStatus.bind(res);
res.sendStatus = (status) => {
if (status === 404) {
return res.status(404).render('404page');
}
return sendStatus(status);
}
next()
});

Related

NodeJS Express Routing - Serving Both Frontend and Backend on same NodeJS App

I'm currently trying to wrap my head around the best way to achieve running my frontend and backend on the same NodeJS app.
I currently have two Express routers, one for my frontend and another for the backend.
Below is an extract of my UI/frontend route. On visiting / I want to redirect to /dashboard, I have a middleware that checks if you have accessed dashboard but not authed then you are re-directed to the /login route.
const routes = (app) => {
const ui = Router();
ui.use(function (req, res, next) {
console.log(req.originalUrl)
if (req.originalUrl === '/login' || req.originalUrl === '/register') {
console.log('public route hit')
return next();
}
if (req.isAuthenticated()) {
console.log('user is authed')
return next();
}
console.log('re-directing to login')
return res.redirect('/login');
});
ui.get('/', (req, res) => {
res.redirect('/dashboard'); //If root is navigated to go to dashboard
});
ui.get('/dashboard', async (req, res) => {
res.render('static/dashboard.html', {
locals: {
...globalLocals,
pageName: 'Dashboard'
},
partials: {
...globalPartials
}
});
});
app.use('/', ui);
};
An extract of my API routing is:
const routes = (app) => {
const api = Router();
api.get('/logout', async (req, res, next) => {
await req.logout();
return handleResponse(res, 200, 'success');
});
app.use('/api/v1/', api);
};
The main issue I am having is that by having my frontend router path using / it then runs my authentication middleware on any API calls to /api/v1/***, which results in any login attempts on /api/v1/login result in a re-direction to /login.
How do I get around this issue? Should I have my frontend/UI routing on it's own path, such as /app/ giving me /app/dashboard?

How do I automatically return a 404 when a GET path doesn't exist?

I am using NodeJS, Express and Handlebars (template engine) to build a web application. Currently I'm trying to automatically redirect users whenever they enter an URL that does not exist (or whenever they might not have access to it).
The following returns the index page:
router.get('/', (req, res) => {
res.render('index/index');
});
But how do I make something like this:
router.get('/:ThisCouldBeAnything', (req, res) => {
res.render('errors/404');
});
The following example is from Github:
Say that I enter this URL:
https://github.com/thispagedoesnotexist
It automatically returns a 404. How do I implement this in my application?
Thanks in advance.
Use a middleware just after all route handlers to catch non existing routes:
app.get('/some/route', function (req, res) {
...
});
app.post('/some/other/route', function (req, res) {
...
});
...
// middleware to catch non-existing routes
app.use( function(req, res, next) {
// you can do what ever you want here
// for example rendering a page with '404 Not Found'
res.status(404)
res.render('error', { error: 'Not Found'});
});
After all your other routes you can add:
app.get('*', (req, res) => {
res.render('errors/404');
});
Alternately, you can use a middleware function after all your other middleware and routes.
app.use((req, res) => {
res.render('errors/404');
});
So you might end up with something that looks like:
//body-parser, cookie-parser, and other middleware etc up here
//routes
app.get('/route1', (req, res) => {
res.render('route1');
});
app.get('/route2', (req, res) => {
res.render('route2');
});
//404 handling as absolute last thing
//You can use middleware
app.use((req, res) => {
res.render('errors/404');
});
//Or a catch-all route
app.get('*', (req, res) => {
res.render('errors/404');
});
I see that you have express tagged. All you have to do is include a default handler that includes
res.status(404).render('404template')
For example
app.get('*', (req, res,next) => {
res.status(404).render('error.ejs')
});

how to use errorhandler middleware in express 4.x

I tried to use middleware errorhandler, but doesn't work, even i set process.env.NODE_ENV ='development'
below is the server code:
var express = require('express');
var app = express();
var errorhandler = require('errorhandler');
var notifier = require('node-notifier');
process.env.NODE_ENV = 'development'; //just purposely do this, see if it can work
if (process.env.NODE_ENV == 'developmet') {
app.use(errorhandler({ log: errorNotification }));
}
function errorNotification(err, str, req) {
var title = 'Error in' + req.method + '' + req.url;
notifier.notify({
title: title,
message: str
});
}
app.get('/', function (req, res, next) {
nonexist(); //the error is still captured Native node.js not errorhandler
res.send('this is home page!');
next();
});
app.listen(1338);
no matter what kind of options i tried in errorhandler, it still doesn't work.
can anyone help me to check any setting is wrong?
Error handling is declared after all your other routes. Express moves through routes from top to bottom or left to right (if you imagine all your code on one line).
You can capitalize on this by putting a splat route after all your other routes, and it will be activated if no other exact route matches. This is how you can do an error 404 page.
It's why you build your routes like this (which will prepare you for React Router 'switch component', if you move into React coding):
GET /test/:slug
GET /test
GET /
Here is an example of a splat route and following that, your error handler middleware:
// Try switching the order of these first two
app.get('/', async (req, res, next) => {
return res.status(200).send('test')
})
app.get('*', async (req, res, next) => {
return res.status(404).send('error 404') // res.render('error/404')
})
// ERRORS
app.use(async (err, req, res, next) => {
// if next() is called with a parameter, which can be anything,
// this middleware will fire
res.status(500).send('error 500') // res.render('error/500')
throw err
})
// Try replacing your default route with this now
app.get('/', async (req, res, next) => {
return next('Extreme detonations')
})
You do not need the async functions as I have shown here, but I do it as a matter of convention so I can always slap await in there. I use explicit returns to prevent any issues with sending headers after they are already sent, and because async functions return promises, so explicit return will resolve them.
All my routes generally look like this:
app.get('/admin', async (req, res, next) => {
try {
if (!req.user) throw 'garbageUser'
const poop = await something()
return res.render('template', {
data: obj,
bonusData
})
} catch (e) {
if (e === 'garbageUser') {
log.add(`illegal: ${req.originalUrl} from ${sniffer.getClientIp(req)}`)
return res.render('403')
}
return next(e)
}
})
This should hopefully be informative for you and offer some code to forensically analyze. The Express error handler middleware takes a 4th parameter in the first position called err which contains the value passed into next().
Check out the Express documentation again after studying this, and it will make much more sense :)
To answer your question:
var express = require('express');
var app = express();
// You can add these back now that you understand
// var errorhandler = require('errorhandler');
// var notifier = require('node-notifier');
function handleErrors(error) {
console.log('I'm telling your mom about this: ' + error);
}
app.get('/', function(req, res, next) {
return next('REALLY BAD');
return res.send('this is home page!');
});
// Remember, this must be after all your other routes
app.use(function(err, req, res, next) {
console.log('Problem occurred, we could put logic here...');
console.log('Error was: ' + err);
if (err === 'REALLY BAD') {
handleErrors(err);
}
next();
});
app.listen(1338);
Try commenting this out now return next('REALLY BAD'); and run it again. You should see "this is home page!".
When you call next() with no parameter in it, Express treats it as no error. If you pass any value in, like next(err) or next('Chicken tastes good'), you will see err defined with that value in your error handling middleware.

How do I check Content-Type using ExpressJS?

I have a pretty basic RESTful API so far, and my Express app is configured like so:
app.configure(function () {
app.use(express.static(__dirname + '/public'));
app.use(express.logger('dev'));
app.use(express.bodyParser());
});
app.post('/api/vehicles', vehicles.addVehicle);
How/where can I add middleware that stops a request from reaching my app.post and app.get if the content type is not application/json?
The middleware should only stop a request with improper content-type to a url that begins with /api/.
If you're using Express 4.0 or higher, you can call request.is() on requests from your handlers to filter request content type. For example:
app.use('/api/', (req, res, next) => {
if (!req.is('application/json')) {
// Send error here
res.send(400);
} else {
// Do logic here
}
});
This mounts the middleware at /api/ (as a prefix) and checks the content type:
app.use('/api/', function(req, res, next) {
var contype = req.headers['content-type'];
if (!contype || contype.indexOf('application/json') !== 0)
return res.send(400);
next();
});
Get content type from the request by using this.
req.get('Content-Type')
Example :
app.post("/api/test", (req, res) => {
console.log("Request type :", req.get('Content-Type'));
//your code
})
As an alternative, you can use the express-ensure-ctype middleware:
const express = require('express');
const ensureCtype = require('express-ensure-ctype');
const ensureJson = ensureCtype('json');
const app = express();
app.post('/', ensureJson, function (req, res) {
res.json(req.body);
});
app.listen(3000);
Adding this express middleware will validate all PATCH, POST and PUT and ensure that they contain the application/json header:
app.use((req, res, next) => {
if (['PATCH', 'POST', 'PUT'].includes(req.method) && !req.is('application/json')) {
res.send(400);
} else {
next();
}
});
Limiting it to certain methods will prevent errors with other methods such as GET
For input validation a good module is express-validator. It provides the middlewares needed to do any kind of check. In your case something like:
const { check, validationResult } = require('express-validator')
app.use('/api/', [
check('content-type').equals('application/json')
], function(req, res, next) {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() });
}
next();
});

How can I get Express.js to 404 only on missing routes?

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');
});

Resources