我是 Javascript 新手,我正在尝试学习 express 并创建一个应用程序,该应用程序将允许用户创建新食谱、浏览现有食谱和查看食谱。
我通过在 cmd 栏中输入 recipeserver.js 然后在我的 google chrome 上的地址栏中输入 localhost:3000 来运行我的服务器。到目前为止,它加载了 index.html 主页,然后,我可以单击标题为“创建食谱”的链接,该链接将我引导至如下所示的 create.html 页面:
最初,服务器上只有三个配方,它们包含在我在下面包含的 recipeserver.js 代码中的数据库对象中。 create.html 页面允许用户输入配方信息。当单击 Save Recipe 按钮时,addrecipe.js 文件应该使用对资源 /recipes
的 POST 请求将配方数据发送到服务器在服务器代码中,所有配方都将存储在一个名为数据库的对象中。此对象的键将是唯一 ID,值将是与这些 ID 关联的配方。我被困在一个任务上,我应该在服务器代码中添加一个路由来处理对 /recipes 资源的 POST 请求。此路由的处理程序应该:
通过向我的服务器添加一些配方来测试我的代码时,我应该能够只记录配方对象的内容以查看它是否存储了正确的数据,如下图所示(这张图片不是我的):
所以如我屏幕的第一张图片所示,我填写了我想在 create.html 中添加的食谱的内容。然而,当我点击“保存食谱”按钮时,我没有将食谱的内容加载到我的 cmd 窗口中,而是出现错误:
TypeError: C:\Downloads\recipeApplication\views\recipes.pug:8
6| div#main
7| h1 List of Recipes:
> 8| each recipe in recipes
9| a(href="/recipes/" + recipe.id) #{recipe.name}
10| br
11|
Cannot read property 'length' of undefined
我对如何在服务器代码中路由以处理对 /recipes 资源的 POST 请求感到有些困惑。我创建了一个名为 loadRecipes() 的函数,我正在尝试在其中执行所有这些操作。我尝试创建一个新的 id 并将其增加 1,就像任务建议的那样。我在提取 POST 请求正文中包含的配方对象时遇到问题。我尝试了这个并最终将其注释掉,因为它产生了相同的错误。我只是想让 Save Recipe 按钮起作用,以便添加的食谱在 cmd 栏中打印其内容,就像第二张图片一样,但我真的迷失了,并且被出现的信息量淹没了我尝试寻找解决方案,并希望得到一些帮助以使其发挥作用。
这是我所有的代码,以防有人想运行它,但我相信我的问题只是出在 recipeserver.js 文件中。单击 Save Recipe 按钮后,addrecipe.js 文件使用对资源 /recipes 的 POST 请求将配方数据发送到服务器。
recipeserver.js:
const express = require('express');
const fs = require("fs");
const shortId = require("short-id");
const session = require('express-session');
const app = express();
const pug = require("pug");
const port = 3000;
let database = {
"0":{
"ingredients":
[
{"name":"Crab","unit":"Tsp","amount":3},
{"name":"Peas","unit":"Cup","amount":12},
{"name":"Basil","unit":"Tbsp","amount":10},
{"name":"Cumin","unit":"Liter","amount":3},
{"name":"Salt","unit":"Tbsp","amount":1}
],
"name":"Boiled Crab with Peas",
"preptime":"13",
"cooktime":"78",
"description":"A boring recipe using Crab and Peas",
"id":"0"
},
"1":{
"ingredients":
[
{"name":"Peanuts","unit":"Liter","amount":10},
{"name":"Artichoke","unit":"Tsp","amount":3},
{"name":"Basil","unit":"Cup","amount":11},
{"name":"Sage","unit":"Grams","amount":13},
{"name":"Pepper","unit":"Cup","amount":1}
],
"name":"Boiled Peanuts with Artichoke",
"preptime":"73",
"cooktime":"74",
"description":"A exciting recipe using Peanuts and Artichoke",
"id":"1"
},
"2":{
"ingredients":
[
{"name":"Lobster","unit":"Tsp","amount":14},
{"name":"Brussel Sprouts","unit":"Liter","amount":14},
{"name":"Sage","unit":"Tbsp","amount":3},
{"name":"Thyme","unit":"Tbsp","amount":12},
{"name":"Pepper","unit":"Tsp","amount":10},
{"name":"Cumin","unit":"Tbsp","amount":11}
],
"name":"Spicy Lobster with Brussel Sprouts",
"preptime":"86",
"cooktime":"19",
"description":"A tasty recipe using Lobster and Brussel Sprouts",
"id":"2"
}
}
let recipes = {};
for (let recipe in database) {
recipes[recipe.id] = recipe;
};
app.set("view engine", "pug");
app.use(express.static("public"));
app.use(express.json());
app.use(express.urlencoded({extended: true}));
app.get("/addrecipe.js", getAddRecipeJS);
app.get("/recipes", loadRecipes);
app.get("/recipe", loadRecipe);
app.route("/recipes", loadRecipes);
app.post("/recipes", loadRecipes);
let id = 1;
function loadRecipes(request, response, next){
response.status(200).render("recipes.pug", {"session": request.session});
/*
console.log("Request received!", request.body);
const newId = shortId.generate();
recipes[newId] = {
id: newId,
type: "recipe",
...request.body,
};
response.sendStatus(201);
*/
id++;
}
function loadRecipe(req, res, next){
res.status(200).render("recipe.pug", {"session": req.session});
}
function getAddRecipeJS(req, res, next){
fs.readFile("addrecipe.js", function(err, data){
if(err){
res.statusCode = 500;
res.end("Error reading file.");
return;
}
res.status(200).send(data);
return;
});
}
app.listen(port);
console.log(`Server listening at http://localhost:${port}`);
index.html:
<html>
<head><title>Recipe App Home Page</title></head>
<body>
<h1>Welcome to the Recipe App</h1>
<br>
<a href="/create.html">Create a Recipe</a><br>
<a href="/recipes">Browse Recipes</a><br>
</body>
</html>
create.html:
<html>
<head><title>Create a Recipe</title></head>
<body>
<script src="/js/addrecipe.js"></script>
<button type="button" onclick="genRandom()">Generate Random Recipe Data</button>
<button type="button" onclick="submit()">Save Recipe</button>
<br><br>
Recipe Name: <input type="textbox" id="recipename" size="50"><br>
Prep Time: <input type="textbox" id="preptime" size="50"><br>
Cook Time: <input type="textbox" id="cooktime" size="50"><br>
Description: <textarea rows="5" cols="50" id="description"></textarea><br><br>
Add ingredients:<br>
Unit: <select id="unit">
<option value="Tsp">Teaspoon</option>
<option value="Tbsp">Tbsp</option>
<option value="Cup">Cup</option>
<option value="Liter">Liter</option>
<option value="Gram">Gram</option>
</select><br>
Amount: <input type="textbox" id="amount"><br>
Ingredient: <input type="textbox" id="ingredient"><br>
<button type="button" id="add" onclick="addIngredient()">Add Ingredient</button>
<br><br>
<div id="ingredients">
</div><br>
<button type="button" id="submit" onclick="submit()">Save Recipe</button>
</body>
</html>
addrecipe.js:
let descriptors = ["Sweet", "Spicy", "BBQ", "Braised", "Deconstructed", "Broiled", "Boiled", "Flambeed", "Raw", "Smoked", "Butterflied", "Cured", "Grilled", "Poached"];
let proteins = ["Chicken", "Beef", "Lobster", "Shrimp", "Crab", "Turkey", "Duck", "Tofu", "Chickpeas", "Lentils", "Peanuts", "Kangaroo", "Human", "Goose", "Fish", "Pork", "Eggs", "Deer"];
let accompany = ["Broccoli", "Carrots", "Peas", "Potato", "Kale", "Banana", "Artichoke", "Asparagus", "Beans", "Broccoli", "Brussel Sprouts", "Celery", "Melon", "Mushrooms", "Pumpkin"];
let spices = ["Salt", "Pepper", "Basil", "Thyme", "Sage", "Cumin"];
let mealDescriptors = ["tasty", "mediocre", "very good", "boring", "exciting", "delicious", "easy", "ridiculously complex"];
let units = ["Tbsp", "Tsp", "Cup", "Liter", "Grams"]
let recipe = {ingredients: []};
function addIngredient(){
let name = document.getElementById("ingredient").value;
let amount = document.getElementById("amount").value;
let unit = document.getElementById("unit").value;
let ingredient = {name, amount, unit};
recipe.ingredients.push(ingredient);
updateIngredients();
}
function updateIngredients(){
let innerHTML = "";
recipe.ingredients.forEach(ingredient => {
innerHTML += ingredient.amount + " " + ingredient.unit + " " + ingredient.name + "<br>";
});
document.getElementById("ingredients").innerHTML = innerHTML;
}
function submit(){
recipe.name = document.getElementById("recipename").value;
recipe.preptime = document.getElementById("preptime").value;
recipe.cooktime = document.getElementById("cooktime").value;
recipe.description = document.getElementById("description").value;
let req = new XMLHttpRequest();
req.onreadystatechange = function() {
if(this.readyState==4 && this.status==200){
alert("recipe saved");
}
}
//Send a POST request to the server containing the recipe data
req.open("POST", `/recipes`);
req.setRequestHeader("Content-Type", "application/json");
req.send(JSON.stringify(recipe));
}
recipes.pug:
html
head
title Recipes
body
a(href="/create.html") add a recipe
div#main
h1 List of Recipes:
each recipe in recipes
a(href="/recipes/" + recipe.id) #{recipe.name}
br
recipe.pug:
html
head
title #{recipe.name}
body
div#main
h1 #{recipe.name}
br
答案 0 :(得分:0)
首先,感谢您努力详细解释您的问题。一个建议是,您可以共享存储库而不是代码片段(因为它很长,而且文件夹的结构确实会影响我们如何让它运行)。
尽管如此,您得到的错误是由于 recipes
中的 recipes.pug
实际上是 undefined
。
index.js
function loadRecipes(request, response, next) {
response
.status(200)
// Here, you only pass `session` object to the template engine
// So template engine does not know about `recipes`
// So `recipes` is undefined, and you can't loop it
.render('recipes.pug', { session: request.session});
id++;
}
recipes.pug
html
head
title Recipes
body
a(href="/create.html") add a recipe
div#main
h1 List of Recipes:
// You're having issue here, since `recipes` is not passed to the template
// engine, it will throw an error
each recipe in recipes
a(href="/recipes/" + recipe.id) #{recipe.name}
br
使用此更新您的 index.js
function loadRecipes(request, response, next) {
response
.status(200)
.render('recipes.pug', { session: request.session, recipes: database });
id++;
}
现在,您应该能够查看 /recipes
页面并继续处理该项目。
我注意到您的代码中有相当多的错误。
app.get('/addrecipe.js', getAddRecipeJS);
app.get('/recipes', loadRecipes);
app.get('/recipe', loadRecipe);
app.route('/recipes', loadRecipes);
app.post('/recipes', loadRecipes);
根据此路由列表,您不应使用相同的 function
来处理 POST
的 GET
和 /recipes
请求。
GET
可用于检索 recipes
的列表
POST
应该用于处理提交的数据,并将其保存到 index.js 中的 database
变量
我会给你一个简单的方法(你也应该真正探索自己)
app.post('/recipes', saveRecipes);
function saveRecipes(req, res, next) {
// Data submitted from your page is available in `req.body`
console.log(req.body);
// I'm trying to get a new `key` for the database
// This is because you're using number as `key` for each recipe
// Can skip this and use some random uuid as well
const dbLength = Object.keys(database).length;
// Add this to the database variable, and you're DONE!
database.dbLength = req.body;
res.send('ok');
}