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.
Related
In the express app below:
Executing a POST for /push with invalid json data will cause an HTTP 400 error to be sent back to the client
Executing a POST with valid json data, but for an unknown route will cause an HTTP 404 error to be sent back to the client
Executing a POST with invalid json data for an unknown route, will cause an HTTP 400 error to be sent back to the client
What I'd like to achieve, is that in the third scenario, also the 404 would be sent. In other words, in case the route is unknown, I want that to be catched by app.post('*',(req, res, next)), which will throw the 404, and not by the generic middleware error handler, which throws a 400 json SyntaxError
Any way to achieve this?
const express = require('express');
const { handleError, ErrorHandler } = require('./helpers/error');
const app = express();
app.use(express.json());
app.use(express.urlencoded({extended: true}));
app.get('/', function(req, res){
res.send('Hello World');
});
app.post('/', function(req, res){
console.log(req.body.data);
res.sendStatus(200);
});
app.post('/push', function(req, res){
console.log('push API');
res.sendStatus(200);
});
app.post('*',(req, res, next) => {
console.log('Unknown route');
throw new ErrorHandler(404, 'API endpoint does not exist.');
});
app.use((err, req, res, next) => {
console.log('Middleware error handling');
handleError(err, res);
});
app.listen(3000, function(){
console.log('Server started on port 3000')
});
error.js content:
class ErrorHandler extends Error {
constructor(statusCode, message) {
super();
this.statusCode = statusCode;
this.message = message;
}
}
const handleError = (err, res) => {
const { statusCode, message } = err;
res.status(statusCode).json({
status: "error",
statusCode,
message
});
};
module.exports = {
ErrorHandler,
handleError
}
By sending a request to a non-existing route, none of your handlers are matched. Thus, your error handling middleware
app.use((err, req, res, next) => {
console.log('Middleware error handling');
handleError(err, res);
});
is executed directly. What you can do is to try to detect a Syntax/parsing error in your handleError function. Something like:
const handleError = (err, res) => {
let {statusCode, message} = err;
if (err.status === 400 && err.message.includes("Unexpected token")) {
statusCode = 404;
}
res.status(statusCode).json({
status: "error",
statusCode,
message
});
};
EDIT:
The issue can be fixed by only using the json-parser middleware for the routes you want to parse json and removing it as a general middleware for all requests:
...
//app.use(express.json());
app.use(express.urlencoded({extended: true}));
app.post('/', express.json(), function (req, res) {
console.log(req.body.data);
res.sendStatus(200);
});
app.post('/push', express.json(), function (req, res) {
console.log('push API');
res.sendStatus(200);
});
I am using express and I want to have my user profile URLs like this: example.com/:username
However, I still need other URLs such as example.com/login and example.com/view/:id
If I order the router like this, it treats "login" as a username when a request is sent to example.com/login:
router.get('/:username', function (req, res, next) {
res.render('profile', {data: req.params.username});
})
router.get('/login', function (req, res, next) {
res.render('login', {data: null});
})
router.get('/view/:id', function (req, res, next) {
res.render('view', {data: req.params.id});
})
If I put the /:username router at the end, everything works correctly. However, if someone went to example.com/view (without an id), I need it to send an error that the view controller didn't receive an id. Instead, it sees it as a username again and instead sends an error that the username doesn't exist.
What is the cleanest way to solve this? Do I just have to add a router for all base url paths? (Something like this):
router.get('/login', function (req, res, next) {
res.render('login', {data: null});
})
router.get('/view/:id', function (req, res, next) {
res.render('view', {data: req.params.id});
})
router.get('/view', function (req, res, next) {
res.render('viewError', {data: null});
})
router.get('/:username', function (req, res, next) {
res.render('profile', {data: req.params.username});
})
I am not entirely sure if this is the right way to do it, but then again this sounds like something I might encounter and I would like it to be solved by this method until I find a better solution.
The below solution uses a single route for the path format /:value, be it login, view or any username, hence you could put in a simple if-else-if or switch to give control to respective controllers or just simply render a view from it. This way the order in which it has to be handled doesn't matter at all.
router.get("/:username", function(req, res, next) {
if (req.params.username === "login") {
res.render("login", { data: null });
} else if (req.params.username === "view") {
res.render("viewError", { data: null });
} else {
res.render("profile", { data: req.params.username });
}
});
router.get("/view/:id", function(req, res, next) {
res.render("view", { data: req.params.id });
});
I'm building/learning a web-app with React and Express. All of the routes and redirects work but URL won't change and my props won't pass until i manually go to the URL.
For example;
After a successful login (local passport with MongoDB), it renders main page but it's empty since i don't get any data (user id or email etc..) but if enter URL manually or press home button on nav-bar, it works or if i logout it logouts but URL stays at /logout instead of /login. Example code below:
server.js
...
server.use((req, res, next) => {
res.locals.success_msg = req.flash("success_msg");
res.locals.error_msg = req.flash("error_msg");
res.locals.error = req.flash("error");
res.locals.messages = req.flash();
res.locals.user = req.user;
next();
});
server.get("/index", ensureAuthenticated, (req, res) => {
const msg = {name: req.user.name, email: req.user.email};
return app.render(req, res, "/index", msg);
});
server.post("/login", (req, res, next) => {
passport.authenticate("local", function(err, user, info) {
if (err) {
return next(err);
} else if (!user) {
req.flash("error_msg", info.message);
return app.render(req, res, "/login", req.flash());
} else {
req.logIn(user, function(err) {
if (err) {
return next(err);
}
req.user = user.name;
return app.render(req, res, "/index", user.name);
});
}
})(req, res, next);
});
server.get("/logout", (req, res) => {
req.logOut();
req.flash("success_msg", "done!");
return app.render(req, res, "/login", req.flash());
});
server.get("*", ensureAuthenticated, (req, res) => {
return handle(req, res);
});
I think that what you meant by return app.render(req, res, "/index", user.name); on your login method, is actually a redirect.
What render does is take the file and the data you give it and then send it back to the browser as a response.
However, what you're trying to do is have the user go to a different URL if the login process is successful, that can be accomplished by doing the following:
res.redirect('/index')
This will make the server go to your index route, which in turn executes all the code required for your user data to be loaded!
You can learn more about redirect and render by looking at the express docs.
I would like to know how to run a function when passport authenticating fails or succeed, for example
.post('/login', (req, res, next) => {
passport.authenticate('local', {
successRedirect: '/',
failureRedirect: '/users/login', //I would like to run a function here instead of redirecting
failureFlash: false
})(req, res, next);
})
you need to pass a function instead of the config object.
But make sure that you handle the request properly.
ie:
const login = function (req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err || !user) {
console.log(err);
return res.status(401).json({
message: 'Something is not right',
user: user
});
}
req.login(user, {session: false}, function(err) {
if (err) {
res.send(err);
}
return res.json({user});
});
})(req, res);
};
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).