i have question about routing in sails.js.
So, i'm following a tutorial about making a login page. it consists of
AuthController.js
module.exports = {
login: function(req , res){
res.view('login'); //view login page
},
authenticate: function(req, res) {
//some auth function
}
};
login.ejs
<div id="login">
<form align="center" action="/login" method="post">
<ul>
<li>
<input type="text" name="username" placeholder="Username"></li>
<li>
<input type="password" name="password" placeholder="Password" ></li>
<li>
<input type="submit" value="Log In"></li>
</ul>
</form>
</div>
and finally this is what makes me confused in routes.js. why this works?
'get /login': {
controller: 'AuthController',
action: 'login'
},
'post /login' : {
controller: 'AuthController',
action: 'authenticate'
},
but this doesn't (i removed the get)?
'/login': {
controller: 'AuthController',
action: 'login'
},
'post /login' : {
controller: 'AuthController',
action: 'authenticate'
},
when i'm using the later route it seems that authentication action is never called when i enter username password, and it's just redirecting me to login page again (it's calling login action instead).
From the sails documentation:
If no verb is specified, the target will be applied to any request that matches
the path, regardless of the HTTP method used (GET, POST, PUT etc.).
URLs are matched against addresses in the list from the top down.
Also the order works from top to bottom. So when you try to POST in /login, it again goes to /login rather than POST /login.
Hope this helps.
As others have said, it's because the routes are compared in order, triggering whichever matches first.
Interestingly that means that if you swap the order, it works as you described:
'post /login' : {
controller: 'AuthController',
action: 'authenticate'
},
'/login': {
controller: 'AuthController',
action: 'login'
},
In sails Js, route.js consists of an address (on the left, e.g. 'get /login') and a target (on the right, e.g. 'AuthController.login'). The address is a URL path and (optionally) a specific HTTP method. When Sails receives an incoming request, it checks the address of all custom routes for matches. If a matching route is found, the request is then passed to its target.
Now, when you remove the get option, & lift your app, & navigate to /login, first the login page is rendered but when the form is posted, it's unable to differentiate b/w the requests as you have omitted get request, so it again calls /login & never reach on the post route.
Reference :http://sailsjs.org/documentation/concepts/routes
Related
Ok, so first off, this is my first post. I've searched high and low for a solution, but have found none. I have posted this first on Udemy, for the course I've taken, but no one has answered, so I'm reposting it here.
I have been trying very hard to figure out why the new-password page will not display correctly for me. The reset link works fine, and I can even reset the password on my new password page when I am sent there from the email link.
However, no matter what I do, I can't get it to display any styling. It only gives me basic html. The logic works fine, it's just the page that doesn't display correctly.
I know it isn't a path issue to the css folder either. If I simply render as another basic page without any token logic, such as replacing my index page with the new-password page, then it displays normally. I just don't know what I'm missing, or if there was some updates that I need to take into consideration.
I'm hoping someone sees this and can help me out. It's the only thing that doesn't work right, and it's very frustrating.
Just to be a little more clear, if I do something like below, and just replace or create a route, the page shows up correctly. It's the token logic I believe that is breaking the rendering, I just don't know how, since I don't get any errors.
Please let me know what code you may need to see, as I'm not sure what sections would be helpful, there are a lot of moving parts here. I will be happy to post whatever is needed.
exports.getNewPassword = (req, res, next) => {
res.render("auth/new-password", {
path: "/new-password",
pageTitle: "Update Password",
});
};
With the logic built-in and following the email reset link, the below will not render any styling, only the html.
exports.getNewPassword = (req, res) => {
const token = req.params.token;
User.findOne({
resetToken: token,
resetTokenExpiration: { $gt: Date.now() },
})
.then((user) => {
if (!user) {
req.flash(
"error",
"That reset password link has already been used."
);
return res.redirect("/");
}
let message = req.flash("error");
message.length > 0 ? (message = message[0]) : (message = null);
res.render("auth/new-password", {
path: "/new-password",
pageTitle: "New Password",
errorMessage: message,
userId: user._id.toString(),
passwordToken: token,
});
})
.catch((err) => console.log(err));
};
I am using ejs for templating as well. As I said above, if I remove all token logic and just render the page as a normal view, it works fine.
<main>
<% if (errorMessage) { %>
<div class="user-message user-message--error"><%= errorMessage %></div>
<% } %>
<form class="login-form" action="/new-password" method="POST">
<div class="form-control">
<label for="password">Password</label>
<input type="password" name="password" id="password">
</div>
<input type="hidden" name="userId" value="<%= userId %>">
<input type="hidden" name="passwordToken" value="<%= passwordToken %>">
<input type="hidden" name="_csrf" value="<%= csrfToken %>">
<button class="btn" type="submit">Update Password</button>
</form>
</main>
Well, in case anyone stumbles across this, the answer was pretty simple, though I'm not sure why in this one instance is was a problem. However, the solution was to add a forward slash in front of my path to the css location for the update password page.
Again, not sure why it needed it, seeing as all my other css and view pages were in the same folder structures and worked fine, but it apparently solved the issue. SMDH.
<link rel="stylesheet" href="/css/login.css" />
PUGJS script
form(id="form1" action="/delete" method="POST")
input(type="submit",name=+item['id'] value="delete")
My ExpressJS code
router.post('/delete', function(req, res, next) {
var id = req.params("i");
console.log("i am 0")
MongoClient.connect(url, function(err, db) {
console.log("i am 1")
db.collection('books', function(err, book) {
db.collection.deleteOne( {_id: new mongodb.ObjectID(id)} );
console.log("i am 2")
if (err) {
throw err;
} else {
db.close();
res.redirect('/');
}
});
});
});
Trying to perform a delete request but it does not even print ("i am 0")
can not determine what's broken with the code
NPM response POST /delete 404 7.247 ms - 1202
When you see a 404 the root cause is definitely how you set up the routing. This code would be successfully called if it is in app.js/server.js (or whatever your root express file is), but a 404 means that you have placed it in a secondary file and are using another path in there somewhere.
With that said, you also have an issue with how your route is defined if you want to read a route parameter (which is what the first line of your route handler tries to do).
router.post('/delete/:i', function...
The form itself isn't passing any id though through name (or id). You could either pass the id through the url called:
action= "/delete?id=" + id
...and read this in your route handler using req.query.id or you could insert a hidden input in the form and read it using req.body.id.
Also, the form name attribute has been deprecated and should be replaced with id.
Then, it's important to note that pug is highly dependent on indentation. Your code as pasted will generate an empty form and a separate input field.
This:
form(id="form1" action="/delete" method="POST")
input(type="submit",name=+item['id'] value="delete")
Generates this HTML:
<form id="form1" action="/delete" method="POST"></form>
<input type="submit" name="itemId" value="delete">
If you change the pug template to this (note the two additional spaces on the input line):
form(id="form1" action="/delete" method="POST")
input(type="submit",name=+item['id'] value="delete")
You will get this, which should work as expected:
<form id="form1" action="/delete" method="POST">
<input type="submit" name="itemId" value="delete">
</form>
Then, there's the problem in your delete function where you're missing a callback.
db.collection.deleteOne( {_id: new mongodb.ObjectID(id)} );
You need to either add a promise or callback here otherwise your code will move straight to close the connection.
I have an express server and a login page developed in HTML(with jquery). after login button is hit, jQuery fires a HTTP get request to express server and after user gets verified, user should be redirected to landing page with some data like name, gender, age etc(that is fetched from mongoDB on server itself). When I do res.sendFile or res.redirect, The parameters (name, age, gender) could not be sent on the view which is required there in response.
Jquery:
$("#submit").click(function() {
user = $("#email").val();
pass = $("#password").val();
$.post("https://localhost:443/login", {
user: user,
password: pass
}, function(response) {
if (response) {
alert("login success" + response.userName);
}
});
});
HTML:
<form id="grad">
<h1 style="margin-top: -10px; text-shadow: 1px 1px whitesmoke;">Login</h1>
<h3 style="padding-bottom: 30px;font-size: 22px ">Please enter email ID and password.</h3>
<div class="group">
<input type="email" id="email" name="email"><span class="highlight"></span><span class="bar"></span>
<label>Email ID</label>
</div>
<div class="group">
<input type="password" id="password" name="password"><span class="highlight"></span><span class="bar"></span>
<label>Password</label>
</div>
<div class="group">
<input type="submit" id="submit" class="button buttonRed" value="Login" onclick="validateUser()" />
<input type="reset" class="button buttonRed" value="Reset" />
</div>
</form>
Express:
app.post('/login', function (req, res) {
// some logic to validate user and fetch details, which will be used on view.
res.sendFile('landing**strong text**page.html', { root: "public" } );
})
The post route should save the user in session before returning the response to jQuery code. Essentially the response from post route should be only success or response.
There should also be a get route to redirect the user to home page (or the page where u want the user after login).
This get route will first check if there is a user in session then redirect accordingly.
Use express-session to save the session in app.
Simple solution for starter. This is NOT a practical solution in production site, just for starter who wants to learn the basic.
Jquery
$("#submit").click(function() {
user = $("#email").val();
pass = $("#password").val();
$.post("https://localhost:443/login", {
user: user,
password: pass
}, function(response) {
$('body').append(response); //append html to body returned from /login
});
});
Express Server:
app.post('/login', function (req, res) {
// some logic to validate user and fetch details, which will be used on view.
var userInfo = validateAndFetchDetailFunc();
res.send('Username: ' + userInfo.name + '<br>Gender: ' + userInfo.gender);
})
The real solution should be using session to management the login session for user.
User login with username/password
Create a session at the server side, send back a session key or access token to the client.
When the client request user information, use the session key or access token to retrieve the user info
Render the html page with the user info from the saved session
For express server, you can start to learn session with express-session.
I don't know if this is possible or not. All the research I've done has shown that it is possible with a form and text input. But anyways, Using NodeJs & Express I want to be able to click a button on my webpage, and once it's clicked, it sends a post request to my Node.JS server.
Simpler way of saying it:
When button is clicked, send info to the server.
Goal I'm trying to achieve:
When button is clicked, it sends some sort of ID/code/anything to turn on a service from my database. (I have yet to learn how db's work so I am just trying to focus on front end.)
Code I have so far:
app.post("/send", function(req, res){
var newID = req.body.ID;
res.redirect("/action")
});
<form action="/send" method="POST">
<input type="button" name="newID" placeholder="Button">
<button>send</button>
</form>
You do not need to use jQuery or AJAX.
Simply add an input of type submit inside the form tag so that the POST request defined by your form tag is submitted.
Your newID input should be of type text, this allows entering a value in the input field.
The newID value can be retrieved server side with req.body.newID (be sure to use the body-parser middleware).
<form action="/send" method="POST">
<input type="text" name="newID" placeholder="Enter your ID"/>
<input type="submit" value="Click here to submit the form"/>
</form>
For this purposes you should use $.ajax,
example:
$('button').on('click', function() {
$.ajax({
type: 'POST',
url: '/send',
data: { ID: 'someid' },
success: function(resultData) {
alert(resultData);
}
});
});
I finished this walkthrough for creating a very basic Reddit clone using the MEAN stack. The app included a few different views, such as a view for all posts, a single post, the login form, and the register form, and all of these views were included in a single file: views/index.ejs.
Is putting all the views together like this common practice, or was it merely for brevity in the tutorial? I was hoping to be able to separate at least the login and register forms from the rest of the views in index.ejs for the sake of organization, but placing them in a login.ejs file in views causes a 404.
Login portion of views/index.ejs
<script type="text/ng-template" id="/login.html">
<div class="page-header">
<h1>Flapper News</h1>
</div>
<div ng-show="error" class="alert alert-danger row">
<span>{{ error.message }}</span>
</div>
<form ng-submit="logIn()"style="margin-top:30px;">
<h3>Log In</h3>
<div class="form-group">
<input type="text" class="form-control" placeholder="Username" ng-model="user.username"></input>
</div>
<div class="form-group">
<input type="password" class="form-control" placeholder="Password" ng-model="user.password"></input>
</div>
<button type="submit" class="btn btn-primary">Log In</button>
</form>
</script>
Login portion of routes/index.js
router.post('/login', function(req, res, next){
if(!req.body.username || !req.body.password){
return res.status(400).json({message: 'Please fill out all fields'});
}
passport.authenticate('local', function(err, user, info){
if(err){ return next(err); }
if(user){
return res.json({token: user.generateJWT()});
} else {
return res.status(401).json(info);
}
})(req, res, next);
});
Login portion of controller
.state('login', {
url: '/login',
templateUrl: '/login.html',
controller: 'AuthCtrl',
onEnter: ['$state', 'auth', function($state, auth){
if(auth.isLoggedIn()){
$state.go('home');
}
}]
})
I don't understand how the views fit together in this app. What is telling the app to find the login template in index.ejs, and how can I redirect the app to look in a different file?
The way they did this is a little strange, but it was most likely for the sake of brevity.
The reason why it's 404'ing is because of how the routes are set up. There's a single route to serve index.ejs, and the rest of the routing is handled client-side through Angular. In fact, the only reason they used ejs is because they wanted to send it using Express' res.render() method most likely. (Although, since it's just HTML from what I saw, instead of actually using any EJS, they could likely just as easily used Express' res.sendFile() method, or prior to 4.8.0, res.send() in conjunction with Node's builtin fs.readFile to send the plain HTML file.
If you wanted to split out the views you'd have to set up server-side routes, but I guess they were dead set on a single-page app. More commonly, views that are rendered on the server-side are split out into individual files, with a main "layout", in which other views are included into.