HW3 due Tomorrow by 11 pm PST
I'm changing my office hours from Wednesday to Tuesday morning: 9:50-10:50am
Either on your phone or computer, go to PollEv.com/robcse
Localhost? Files? Servers?
Remember our analogy of the restaurant:
Analogous to our client/server:
What if we're making food for ourselves/friends/family at home?
Where's that line now?
I'm giving the order, taking the order, prepp'ing it, serving it, everything.
This is just like what we're looking at with Node on our localhost.
So, last week we talked about why. Let's look at what and how.
Here, we have our own computer, and our browser
Couple of different URLs we could put in: file://... or http{s}://...
Underneath the browser, we have more layers (remember lecture 1? Everything's in layers)
Disk, network, and more.
file:// is the protocol that tells the browser to check the Disk
http{s}:// is the same, but asks the browser to go to the network
In this case, we do a couple other things: lookup the hostname, create an http request, wait for the network, etc.
But what if the hostname we're looking up is something like localhost:8000?
But we also have other things running on the computer:
All of these have their own little boxes (sometimes containers, but It's Complicated™)
Remember, we're here to talk about our own servers. When we start our own server, we do:
node app.js or nodemon app.jsNode is Yet Another Box
Two main steps when we start our node server:
node app.jsapp.listen(PORT);What happens if we don't do app.listen?
So when the browser does that lookup to the network, we come back across that layer and check for things listening on that port
And this is effectively how the server out on the internet works too
Just by default, those servers -- that node box on their computer -- is listening on port 80.
(Since this is the default, we don't have to specify it.)
Just like for our client-side JS, we can set breakpoints in our Node.js apps via Chrome's DevTools or in VS Code
Follow this guide
app.post instead of app.getmulter module to handle FormData POST requestsreq.body.paramName instead of req.params/req.query/addItem POST endpointNote: You can find example documentation for a POST endpoint here
<form id="item-form">
<p>
<label for="category">Category: </label>
<input type="text" name="category" required />
</p>
<p>
<label for="item-name">Item Name: </label>
<input id="item-name" name="name" type="text" required />
</p>
<p>
<label for="images">Item Image:</label>
<select id="images" name="image">
<option value="">-- choose an image --</option>
</select>
</p>
<p><label for="description">Description: </label></p>
<textarea id="description" name="description" minlength=10 rows=5 cols=40></textarea>
<button id="add-item">Add Item!</button>
</form>
HTML
function addItem() {
let params = new FormData(id("item-form"));
fetch("/addItem", { method : "POST", body : params })
.then(checkStatus)
.then(resp => resp.text())
.then(displaySuccess)
.catch(handleError);
}
JS
1. To add a POST endpoint, use app.post instead of app.get
app.post("/addItem", (req, res) => {
...
});
JS
2. Use the multer module to access POST parameters sent through a FormData request
3. Use the multer().none() (required to specify no file-uploading) and 2 other middleware functions built-in to Express for other POST request types
const multer = require("multer");
// for parsing application/x-www-form-urlencoded
app.use(express.urlencoded({ extended: true })) // built-in middleware
// for parsing application/json
app.use(express.json()); // built-in middleware
// for parsing multipart/form-data (required with FormData)
app.use(multer().none()); // multer middleware
app.post("/addItem", (req, res) => {
...
});
JS
Remember to run npm install multer in any project that uses it.
With GET endpoints, we've used req.params and req.query to get endpoint parameters passed
in the request URL.
But remember that POST requests send parameters in the Request body, not in the URL.
app.post("/addItem", (req, res) => {
let name = req.params.name; // this doesn't work!
let name = req.query.name; // this also doesn't work!
...
});
JS
req.body contains key-value pairs of data submitted in the request body. By default, it is undefined, and is populated when you use body-parsing middleware such as multer().none(), express.json() or express.urlencoded().
...
app.post("/addItem", (req, res) => {
let item = req.body.name;
let category = req.body.category;
let description = req.body.description;
...
// validate parameters, then update <category>-additions.json file with new data
...
});
JS
/addItem
/**
* Adds a new item to the <category>-proposals.json file to be approved later.
* If no <category>-proposals.json yet exists, will add a new file.
* Required POST parameters: name, category, description.
* Optional POST parameter: image - defaults to food.png otherwise.
* Response type: text/plain
* Sends a 400 error if missing one of the 3 required params.
* Sends a 500 error if something goes wrong in file-processing.
* Sends a success message otherwise.
*/
app.post("/addItem", async (req, res) => {
...
});
/addItem
// Adds a new item to the menu.
// Required POST parameters: category, name, description
// Optional POST parameter : image - (defaults to food.png otherwise)
app.post("/addItem", (req, res) => {
// 1. validate parameters.
// - If one of the 3 required are missing, send a 400 error message.
// 2. Otherwise, if no image parameter is sent, set to food.png
// 3. Create JSON with each parameter:
// { name, description, image}
// 4. Read <category>-proposals.json (create if doesn't exist),
// add new JSON to the array.
// 5. Write new JSON to >category>-proposals.json.
});
JS
/addItemapp.post("/addItem", async (req, res) => {
res.type("text");
// 1. validate parameters.
// - If one of the 3 required are missing, send a 400 error message.
let name = req.body.name;
let category = req.body.category;
let description = req.body.description;
let image = req.body.image;
if (!(name && category && description)) {
res.status(400).send("Missing POST parameter: category, name, and/or description");
}
// 2. Otherwise, if no image parameter is sent, set to food.png
if (!image) {
image = DEFAULT_IMAGE;
}
// 3. Create JSON with each parameter:
let result = { "name" : name, "description" : description, "image" : image };
// 4. Read <category>-proposals.json, then add new JSON to the array.
// Fresh Fruit -> fresh-fruit
let dashedCategory = category.toLowerCase().replace(" ", "-");
let jsonFile = dashedCategory + "-proposals.json";
try {
let contents = await readFile(jsonFile, "utf8");
} catch (err) {
if (err.code !== "ENOENT") { // file-not-found error
res.status(500).send(SERVER_ERROR);
} // else continue, writing a new <category>-proposals.json file.
}
contents = JSON.parse(contents);
contents.push(result);
// 5. Write new JSON to same file.
try {
await writeFile(jsonFile, JSON.stringify(contents), "utf8");
} catch (err) {
res.status(500).send(SERVER_ERROR);
}
res.send(req.body.name + " proposal successfully added to " + jsonFile + "!");
});
JS
Is there another way to submit a form without FormData?
app.post instead of app.getreq.body.paramname instead of req.params.paramname/req.query.paramnamemulter (non-core) module with the rest of your modulesfetch and FormData in client-side JS (similar to HW3) - remember you can't test POST requests in the URL!
Especially if you're watching a recording, write your question on PollEverywhere and I'll answer them at the start of next lecture.
Either on your phone or computer, go to PollEv.com/robcse