When I test my Error #404 page, I get the default "Not Found."
require('html');
var WebSocketServer = require('ws').Server
, http = require('http')
, fs = require('fs')
, express = require('express')
, app = express();
app.use(express.static(__dirname + '/public'));
var server = http.createServer(app);
server.listen(42069);
var MainServer = new WebSocketServer({server: server});
// Handle 404
app.use(function(req, res) {
res.status(404).render('/error/404.html',{title: "Error #404"});
});
However, it does work with
app.use(function(req, res) {
res.status(404).render('/error/404.html',{title: "Error #404"});
});
but I don't want to be redirected to my 404 page, I want it to be rendered on any non-existent address.
Any thoughts on how to get this to work?
Thanks!
You could try something like this after your route handling
app.get('/404', function(req, res, next){
// trigger a 404 since no other middleware
// will match /404 after this one, and we're not
// responding here
next();
});
app.use(function(req, res, next){
res.status(404);
// respond with html page
if (req.accepts('html')) {
res.render('404', { url: req.url });
return;
}
// respond with json
if (req.accepts('json')) {
res.send({ error: 'Not found' });
return;
}
// default to plain-text. send()
res.type('txt').send('Not found');
});
Place this after all your middleware
app.use(function(req,res){
res.status(404);
res.render('404page',{
});
});
Related
I am writing custom middleware(s) to send request data(like request path, response code, request timestamp, etc) to a remote server on every request. I'm unable to figure out the ordering of my middlewares.
I have created 2 middlewares,
a) process_request (m1) --> this middleware just adds a timestamp to the request object to register when the request was received.
b) process_response (m2) --> this middleware posts the required data to the remote server
m1
function process_request(req, res, next) {
req.requestTime = Date.now()/1000;
next();
}
m2
function process_response(req, res, next) {
const request_obj = {};
request_obj.request_timestamp = req.requestTime;
request_obj.response_timestamp = Date.now()/1000;
request_obj.path = req.protocol + "://" +
req.get('host') + req.originalUrl;
request_obj.response_code = res.statusCode;
send_perf_request(request_obj); // sends remote https request not shown here
}
I can think of two ordering options in my app.js:
Order 1:
m1
route 1
route 2
...
route n
m2
404 request handler middleware
Order 2:
m1
route 1
route 2
...
route n
404 request handler middleware
m2
404 request handler middleware
app.use((req, res, next) => {
const err = new Error('Not Found');
err.status = 404;
next(err);
});
The problem with order 1 is that I won't be able to catch 404 requests, which I want to.
The problem with order 2 in all in request the responseCode=404 as the 404 request handler middleware is doing that.
I am new to node.js & confused if I am thinking about this the right way.
My app.js
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
const custom_middleware = require("custom_middleware");
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(custom_middleware.process_request); //*calling middleware
app.use('/', indexRouter);
app.use('/users', usersRouter);
//catch 404 and forward to error handler
// app.use(function(req, res, next) {
// console.log("in 404 handler");
// //next(createError(404, "this page doesn't exist;"));
// next();
// });
// error handler
app.use(function(err, req, res, next) {
// this is called only in case of errors
// set locals, only providing error in development
console.log("in error handler");
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
console.log(res.statusCode);
//res.render('error');
next(err);
});
app.use(custom_middleware.process_exception); //*calling middleware
module.exports = app;
Custom middleware file
function process_request(req, res, next) {
// this middleware adds request timestamp
console.log("in process request middleware");
req.requestTime = Date.now()/1000;
res.on("finish", function (){
// this middleware collects performance data
console.log("in process response middleware");
const request_obj = {};
request_obj.request_timestamp = req.requestTime;
request_obj.response_timestamp = Date.now()/1000;
request_obj.ip_address = ip.address();
request_obj.path = req.protocol + "://" +
req.get('host') + req.originalUrl;
request_obj.requester = null;
request_obj.response_code = res.statusCode;
console.log(request_obj.response_code);
send_perf_request(request_obj);
})
next();
}
function process_exception(err, req, res, next) {
// this middleware collects exception data
console.log("in process exception middleware");
const error_obj = {};
error_obj.path = req.protocol + "://" +
req.hostname + req.originalUrl;
error_obj.exception = "error occured";
error_obj.traceback = err.stack;
error_obj.user = null;
error_obj.ip_address = ip.address();
send_exception_request(error_obj);
next();
}
My routes/index.js
var express = require('express');
var router = express.Router();
router.get('/', function(req, res, next) {
throw new Error('this does not exist'); // error manually triggered
res.status(500);
});
module.exports = router;
As mentioned in the comments, abstract the middlewares to avoid defining a set of middlewares on each route.
To replace m1 Create a global middleware, which you define before all your other middlewares which sets res.on("finish", function () {}) event handler to do something when the route is done. I'm pretty sure this is the only event you will get the actual correct res.statusCode if you're doing res.status() anywhere.
Then move your 404 error handler logic into the main error handler, you can then check for status code and all that good stuff before logging and responding. You can also use req.xhr to determine how to respond.
Also, you can use a catch-all to pick up on route 404's: app.get('*', function (req, res, next) { then fire off an error which the error handler handles.
Here is an example putting it all together:
const express = require('express')
const app = express()
// logger middleware
app.use((req, res, next) => {
// set something
req.requestTime = Date.now() / 1000;
// gets fired after all routes have been handled
res.on("finish", function () {
//
req.finishTime = Date.now() / 1000;
// do something with req, res objects
console.log('[in logger] originalUrl:', req.originalUrl)
console.log('[in logger] status code:', res.statusCode)
})
next()
})
// apply routes
// ...
app.get('/', (req, res, next) => {
try {
// example: look for something, which is not found
const mock = false
if (!mock) {
let err = new Error('Mock was not found!')
err.status = 404
throw err
}
res.send('Hello World!')
} catch (err) {
next(err)
}
})
// app.get('/foo', ...
// not found route (catch all)
app.get('*', (req, res, next) => {
let err = new Error('Page not found!')
err.status = 404
next(err)
})
// error handler (will catch everything even uncaught exceptions)
app.use((error, req, res, next) => {
//
res.header('Cache-Control', 'private, no-cache, no-store, must-revalidate')
res.header('Expires', '-1')
res.header('Pragma', 'no-cache')
// set status from error
res.status(error.status || 500)
if (req.xhr) {
// is ajax call, send error as json
res.json({
error: error.name || 'Server error',
status: error.status || 500,
message: error.message || 'Internal server error'
})
} else {
// render a view instead (would need to add a view engine)
res.render('error/' + (error.status || 500), error)
}
})
app.listen(3000, function () {
console.log('App listening on port 3000!')
})
Hy Guys i try to make a simple API but i would like do something thani can't cuz i just learn nodejs not long. I would like than instead the api display
Cannot Get /ROUTE
Display this
{
"code": 404,
"message": "Not found"
}
Att All routes.
My code :
var http = require("http");
var express = require('express');
var app = express();
var mysql = require('mysql');
var bodyParser = require('body-parser');
var connection = mysql.createConnection({
host : 'localhost',
user : 'root',
password : 'root',
database : 'restapi',
port : '8889'
});
connection.connect(function(err) {
if (err) throw err
console.log('Congrats you are connected')
})
app.use( bodyParser.json() ); // to support JSON-encoded bodies
app.use(bodyParser.urlencoded({ // to support URL-encoded bodies
extended: true
}));
var server = app.listen(3000, "127.0.0.1", function () {
var host = server.address().address
var port = server.address().port
console.log("Example app listening at http://%s:%s", host, port)
});
app.all('/*', function(req, res, next) {
// CORS headers
res.header("Access-Control-Allow-Origin", "*"); // restrict it to the required domain
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
// Set custom headers for CORS
res.header('Access-Control-Allow-Headers', 'Content-type,Accept,X-Access-Token,X-Key');
if (req.method == 'OPTIONS') {
res.status(200).end();
} else {
next();
}
});
//GET Domains
app.get('/api/domains.json', function (req, res) {
console.log(req);
connection.query('select * from domain', function (error, results, fields) {
if (error) throw error;
var user = results[0];
if (user == undefined) {
res.status(404);
res.send({
code: 404,
message: "Not found"
});
} else {
res.send({
code: 200,
message: 'success',
datas: results
});
}
});
});
So its just one request for the moment but i will try to add POST, PUT and Delete, juste make a simple CRUD.
I see one thing that can affect expected behaviour. You are declaring a more general route before the most specific one.
This one is so generic, that is going to treat all requests
app.all('/*',
And this one is included in the previous one, so noone request is going to be treated here.
app.get('/api/domains.json',
Try to change the order of both routes, and tell me if it changes the behaviour.
res.header('Access-Control-Allow-Headers', 'Content-type,Accept,X-Access-Token,X-Key');
if (req.method == 'OPTIONS') {
res.status(200).end();
} else {
res.status(404);
res.send({
code: 404,
message: "Not found"
});
}
I change the conditions and it works.
In POST requests through Postman in a NodeJS API I receive empty bodies... (I receive exactly this: {}), however from automated tests it works perfectly. Actually from Postman that happends when I send it as "form" or as "raw" with "text", but if I send it as a "JSON" in raw it simply freezes in "loading..."
When I search I read about adding these 2 lines related to body parse it made it work for the tests but not for Postman:
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());
The entire code is here: https://github.com/nemenosfe/TakeMe-Api
But the 3 key files are the following (simplified):
app.js:
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
const cors = require('cors');
const user = require('./routes/users');
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());
app.use('/users', user);
app.use(cors());
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
app.use(function(err, req, res, next) {
res.status(err.status || 500);
});
}
// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
res.status(err.status || 500);
});
app.listen(8888, function() {
console.log("Node server running on http://localhost:8888");
});
module.exports = app;
routes/users:
"use strict"
const express = require('express'),
router = express.Router(),
/* ... many require and other code probably not relevant for the problem ... */
router
.post('/', function(req, res, next) {
console.log(`conditions: ${(!req.body)} ${(!req.body.uid)} ${(!req.body.provider)} ${JSON.stringify(req.body)}`);
// This console.log appears as follows in the console once I make the request by Postman: false true true {}
// But it receives what it shoulds with automated tests
if (!req.body || !req.body.uid || !req.body.provider) { utilsErrors.handleNoParams(res); }
else {
/* ... There is a lot of code here but it's not relevant for the problem because it doesn't even reaches this point. */
}
})
module.exports = router
tests/users:
"use strict"
let request = require('supertest-as-promised');
const api = require('../app');
/* ... Another require not relevant for the problem ... */
request = request(api);
describe('Users route', function() {
describe.only('POST /users', function() {
it("should create a new user when it doesn't exist", function(done) {
const params = {
'appkey': helperCommon.appkey,
'uid': 1,
'provider': 'providerTest',
'name': 'fakeName'
};
request
.post('/users')
.set('Accept', 'application/json')
.send(params)
.expect(201)
.expect('Content-Type', /application\/json/)
.then((res) => {
expect(res.body).to.have.property('user');
const userResponse = res.body.user;
expect(userResponse).to.have.property('uid', params.uid);
expect(userResponse).to.have.property('provider', params.provider);
expect(userResponse).to.have.property('name', params.name);
/* ... other expectectations that are not important for the problem ... */
done();
}, done)
});
});
Thanks!
Make sure your are sending the POST request In postman as a x-www-form-urlenconded
var express = require('express');
var app = express();
app.get('*', function (req, res) {
var host = req.get('Host');
return res.redirect(['https://', host, req.url].join(''));
});
var server = app.listen(8080, function () {
console.log('starting');
});
I have a simple script that redirects http to https. This is working fine except when there is a malformed url for example: website.com/%c0%ae%c0%ae. It displays something like:
URIError: Failed to decode param '/%c0%ae%c0%ae'
at decodeURIComponent (native)
at decode_param (/...<PROJECT DIRECTORY>.../node_modules/express/lib/router/layer.js:167:12)
at Layer.match (/.../node_modules/express/lib/router/layer.js:143:15)
at matchLayer (/.../node_modules/express/lib/router/index.js:557:18)
at next (/.../node_modules/express/lib/router/index.js:216:15)
at expressInit (/.../node_modules/express/lib/middleware/init.js:33:5)
at Layer.handle [as handle_request] (/.../node_modules/express/lib/router/layer.js:95:5)
at trim_prefix (/.../node_modules/express/lib/router/index.js:312:13)
at /.../node_modules/express/lib/router/index.js:280:7
at Function.process_params (/.../node_modules/express/lib/router/index.js:330:12)
It's not nice when a user can randomly see where my project files are in the server. Any way to handle this error?
Thanks #Oleg for the tip. But somehow your solution wasn't logging error for me. Here's what I have come up with:
var express = require('express');
var app = express();
app.use(function(req, res, next) {
var err = null;
try {
decodeURIComponent(req.path)
}
catch(e) {
err = e;
}
if (err){
console.log(err, req.url);
return res.redirect(['https://', req.get('Host'), '/404'].join(''));
}
next();
});
app.get('*', function (req, res) {
return res.redirect(['https://', req.get('Host'), req.url].join(''));
});
var server = app.listen(8080, function () {
console.log('Starting');
});
Possible workaround:
var express = require('express');
var app = express();
app.get('*', function (req, res) {
// redirect regular paths
var host = req.get('Host');
return res.redirect(['https://', host, req.url].join(''));
});
// your express error handler
app.use(function(err, req, res, next) {
// in case of specific URIError
if (err instanceof URIError) {
err.message = 'Failed to decode param: ' + req.url;
err.status = err.statusCode = 400;
// .. your redirect here if still needed
return res.redirect(['https://', req.get('Host'), req.url].join(''));
} else {
// ..
}
// ..
});
var server = app.listen(8080, function () {
console.log('starting');
});
var express = require('express');
var app = express();
// handles 400 error
app.use((err, req, res, next) => {
if (!err) return next();
return res.status(400).json({
status: 400,
error: 'OOps! Bad request',
});
});
Edited:
The code snippet should be placed as the last route, it checks if there is an error that was skipped by other routes, which obviously there is, and sends a default response. This error happens when you add % as last character of an API endpoint..
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');
});