首页 > 解决方案 > Express JS - 在服务器中添加路由来处理 POST 请求

问题描述

我是 Javascript 的新手,我正在尝试学习 express 并创建一个允许用户创建新食谱、浏览现有食谱和查看食谱的应用程序。

我通过在 cmd 栏中键入 recipeserver.js 然后在 google chrome 上的地址栏中键入 localhost:3000 来运行我的服务器。到目前为止,它加载了 index.html 主页,从那里,我可以单击标题为“创建食谱”的链接,该链接将我带到如下所示的 create.html 页面:

create.html 页面

最初,服务器上只有三个配方,它们包含在我在下面包含的 recipeserver.js 代码中的数据库对象中。create.html 页面允许用户输入配方信息。单击 Save Recipe 按钮时,addrecipe.js 文件应该使用对资源 /recipes 的 POST 请求将配方数据发送到服务器

在服务器代码中,所有配方都将存储在一个名为数据库的对象中。此对象的键将是唯一的 ID,值将是与这些 ID 关联的配方。我被困在一个任务上,我应该在服务器代码中添加一个路由来处理对 /recipes 资源的 POST 请求。此路由的处理程序应:

  1. 提取 POST 请求正文中包含的配方对象
  2. 为新配方生成一个唯一 ID(等等。一个基本整数,每次添加配方时都会增加。)
  3. 在配方对象中添加一个新条目,键是唯一 ID,值是配方对象。

通过向我的服务器添加一些食谱来测试我的代码时,我应该能够只记录食谱对象的内容以查看它是否存储了正确的数据,如下图所示(这张图片不是我的):

在 cmd 中加载配方

所以如我屏幕的第一张图片所示,我在 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 请求正文中包含的配方对象。我尝试了这个并最终将其注释掉,因为它产生了相同的错误。我只是想让“保存食谱”按钮工作,以便添加的食谱在 cmd 栏中打印其内容,就像在第二张图片中一样,但我真的迷失了并且被大量的信息所淹没我尝试寻找解决方案,并希望能得到一些帮助以使其发挥作用。

这是我的所有代码,以防有人想运行它,但我相信我的问题只出在 recipeserver.js 文件中。单击保存配方按钮时,addrecipe.js 文件使用对资源 /recipes 的 POST 请求将配方数据发送到服务器。

食谱服务器.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}`);

索引.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>

创建.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));
}

食谱.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

食谱.pug:

html
    head
        title #{recipe.name}
body

    div#main
        h1 #{recipe.name}
        br

标签: javascriptnode.jsexpresshttppost

解决方案


首先,感谢您努力详细解释您的问题。一个建议是,您可以共享存储库而不是代码片段(因为这很长,并且文件夹的结构确实会影响我们如何使其运行)。

尽管如此,你得到的错误实际上是由于recipesin 。recipes.pugundefined

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来处理POSTGET请求/recipes.

GET可用于检索recipes POST应用于处理提交的数据的列表,并将其保存到database您的 index.js 里面的变量中

我会给你一个简单的方法(你也应该探索自己)

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');
}

推荐阅读