We have a form and now we're going to handle it. Remember the form will use the process route? Well, let's create that. Note that this is a POST.
app.js
app.get("/thankyou", (req, res)=> {
res.render("thankyou");
});
app.post("/process", (req, res)=> {
}
app.use((req, res, next)=> {
res.status(404);
res.render("404");
});
res.render("thankyou");
});
app.post("/process", (req, res)=> {
}
app.use((req, res, next)=> {
res.status(404);
res.render("404");
});
But if we want to grab the values of the form, we have to install another module. I use this one, but it might be deprecated at this point, so if you can find something better, go with that.
install --save body-parser
We'll need to include this. We will use the middleware function use() and pass in the module body-parser.
app.js
app.set("view engine", "handlebars");
app.set("port", process.env.PORT || 3000);
app.use(require("body-parser")());
app.get("/", (req, res)=> {
res.render("form");
});
app.set("port", process.env.PORT || 3000);
app.use(require("body-parser")());
app.get("/", (req, res)=> {
res.render("form");
});
Back to the process route. Log the body object from req.
app.js
app.post("/process", (req, res)=> {
console.log(req.body);
});
console.log(req.body);
});
Then redirect to the thankyou route with a Status of 303. We use 303 to prevent the repeated submission of form data if page is refreshed.
app.js
app.post("/process", (req, res)=> {
console.log(req.body);
res.redirect(303, "/thankyou");
});
console.log(req.body);
res.redirect(303, "/thankyou");
});
Now to test this. Fill up the form.
You should see this in the CLI! This will be information you can use.
Also, see what happens when you try to submit the form without filling stuff in. HTML5 does its thing. Let's use this as an excuse not to spend time validating the fields on the server. Just for today.
Implementing anti-CSRF
This is not mandatory to ensure that your form works, but it's pretty damn important, anyway. An anti-CSRF token is a mechanism to ensure that the request comes from a legitimate source. Probably overkill for this form, but let's see how it's done.We'll use the csurf module.
install --save csurf
But an anti-CSRF token also needs cookies and sessions. So we'll need to install these too.
install --save express-session
install --save cookie-parser
install --save cookie-parser
Add in these modules.
app.js
app.use(require("body-parser")());
app.use(require("cookie-parser")());
app.use(require("express-session")());
app.use(require("csurf")());
app.get("/", (req, res)=> {
res.render("form");
});
app.use(require("cookie-parser")());
app.use(require("express-session")());
app.use(require("csurf")());
app.get("/", (req, res)=> {
res.render("form");
});
When you try to run the app again, you'll get this error. That's because the cookie-parser module requires a secret.
Actually, even a string containing only a single space, qualifies as a secret. This will suffice to get things working again. But that's not a good way to do things.
app.js
app.use(require("cookie-parser")(" "));
What we should do, is create a file separate from app.js. Let's call it auth.js. In it, we will declare that this file exports an object. This object has a property, secret. Here, just use any string you like as its value.
auth.js
module.exports = {
secret: "thisisasecret"
}
secret: "thisisasecret"
}
Then import auth.js using the require() function, and set the result to the variable auth.
app.js
var auth = require("./auth.js");
var express = require("express");
var app = express();
var express = require("express");
var app = express();
And here, instead of the string we used earlier, we have the secret property of auth.
app.js
app.use(require("cookie-parser")(auth.secret));
Now things should run! However, when you attempt to submit the form, you should get this error. That's because once you include the module csurf, you're expected to have the anti-CSRF token as part of the form, and we haven't done that yet.
Before we do that, however, let's tidy up one loose end. Remember the last two errors you've seen was ugly black text and white backgrounds? Let's change that. Add another middleware function after the one we used to handle 404s. This one has the err parameter in the callback. So if the URL is valid but an error is thrown, the status is set to 500 and we render the page 500.
app.js
app.use((req, res, next)=> {
res.status(404);
res.render("404");
});
app.use((err, req, res, next)=> {
res.status(500);
res.render("500");
});
app.listen(app.get("port"), ()=> {
});
res.status(404);
res.render("404");
});
app.use((err, req, res, next)=> {
res.status(500);
res.render("500");
});
app.listen(app.get("port"), ()=> {
});
But we also want to pass in information, so let's use the code property of err, and set it as the value of errorMessage which we will pass into the page.
app.js
app.use(function(err, req, res, next) {
res.status(500);
res.render("500", { errorMessage: err.code });
});
res.status(500);
res.render("500", { errorMessage: err.code });
});
In here, you'll see where we've placed errorMessage.
views/500.handlebars
<h1>500</h1>
<p>There was an error.</p>
<p><b>{{ errorMessage }}</b></p>
<p>There was an error.</p>
<p><b>{{ errorMessage }}</b></p>
Now run the code again! There's still an error, but we've beautified it.
OK, let's now create the anti-CSRF token. Add this object in the call to render(), as a second argument. It will have the property _csrf and the value is the value returned by calling the csrfToken() method of the req object.
app.js
app.get("/", (req, res)=> {
res.render("form", { csrf: req.csrfToken() });
});
res.render("form", { csrf: req.csrfToken() });
});
Now in the view form, add a text field for that value. The name of the field is _csrf.
views/form.handlebars
<form action="/process" method="POST">
<input type="text" name="_csrf" value="{{ csrf }}" />
<label for="ddlLayout">
<span class="labelText">LAYOUT</span>
<select id="ddlLayout" name="ddlLayout">
<option value="red">RED</option>
<option value="green">GREEN</option>
<option value="blue">BLUE</option>
</select>
</label>
<input type="text" name="_csrf" value="{{ csrf }}" />
<label for="ddlLayout">
<span class="labelText">LAYOUT</span>
<select id="ddlLayout" name="ddlLayout">
<option value="red">RED</option>
<option value="green">GREEN</option>
<option value="blue">BLUE</option>
</select>
</label>
We should see the value in there!
Change the type attribute to hidden, because while we want the element in the form, we don't want it to be seen.
views/form.handlebars
<input type="hidden" name="_csrf" value="{{ csrf }}" />
Try submitting the form again and this time it should work.
And we're done here. What we want to do next, is use the values submitted via the form, to send a HTML email!
No comments:
Post a Comment