【问题标题】:Express Routes not functioning on Single Page Web AppExpress Routes 在单页 Web 应用程序上不起作用
【发布时间】:2017-03-02 18:05:22
【问题描述】:

我正在将旧的 LAMP 堆栈项目转换为使用 Node/Express/MongoDB 而不是 PHP/Laravel/MySQL。我以与 Laravel 相同的方式构建了我的路线,并测试了我使用 Postman 构建的路线。通过在 Postman 中的测试,每条路线都可以正常工作。

但是,当我尝试在我编写的 display.js 文件中使用这些路由时,唯一有效的路由是 GET /players。我正在寻找有关这些路线和我正在使用的显示文件的一些见解。

当我单击编辑按钮时,我的 GET /player/:id 路由会获取正确的信息(状态代码 200 和预览显示 JSON 对象),但不会填充表单中的字段。

当我单击删除按钮时,我收到一个 404 状态代码,响应为“Cannot GET /players/123/delete”。如何调整我的显示代码以使用 DELETE 而不是 GET? (如果我能弄清楚如何在 display.js 代码中传递请求方法,我相信我可以使用正确的 http 请求方法将我的所有路由重构为仅 /players/:id)。

当我尝试提交新记录时,我收到一个 400 状态代码,其中包含验证错误,提示需要路径 age。每个字段名称实际上是相同的错误,所以我假设我没有正确地将字段中的值传递到 JSON 对象中。

我无法测试补丁,但我认为应该通过修复 DELETE /players/:id 和 GET /players/:id 来修复它。

感谢任何帮助。下面是显示和服务器 js 文件的代码。

display.js

// this is the base url to which all your requests will be made
var baseURL = window.location.origin;
$(document).ready(function(){
// $.ajaxSetup({
//     headers: {
//         'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
//     }
// });
$('#table').click(function(event) { // generates the table
    // change the url parameters based on your API here
    // Using an JQuery AJAX GET request to get data form the server
    $.getJSON(baseURL+'/players', function(data) {
        generateTable(data, $('#container'));
    });
});

$('#form').click(function(event) {
    // creating an empty form
    generateForm(null, $('#container'));
});
// Handle table click event for delete
$('#container').on('click', '.delete', function(event) {
    var id = $(this).val();
    // change the url parameters based on your API here
    // remember to create delete functionality on the server side (Model and Controller)
    // Using an JQuery AJAX GET request to get data form the server
    $.getJSON(baseURL+"/players/"+id+"/delete", function(data) {
        //Generate table again after delete
        //change the url based on your API parameters here
        // Using an JQuery AJAX GET request to get data from the server
        $.getJSON(baseURL+'/players', function(data) {
            generateTable(data, $('#container'));
        });
    });
});

// Handle form submit event for both update & create
// if the ID_FIELD is present the server would update the database otherwise the server would create a record in the database
$('#container').on('submit', '#my-form', function(event) {
    var id = $('#id').val();
    console.log(id);
    if (id != "") {
        event.preventDefault();
        submitForm(baseURL+"/players/"+id+"/edit", $(this));
    } else {
        event.preventDefault();
        submitForm(baseURL+"/player", $(this));
    }
});

// Handle table click event for edit
// generates form with prefilled values
$('#container').on('click', '.edit', function(event) {
    // getting id to make the AJAX request
    var id = $(this).val();
    // change the url parameters based on your API here
    // Using an JQuery AJAX GET request to get data form the server
    $.getJSON(baseURL+'/players/'+id, function(data) {
        generateForm(data, $('#container'));
    });
});

// function to generate table
function generateTable(data, target) {
    clearContainer(target);
    //Change the table according to your data
    var tableHtml = '<table><thead><tr><th>Name</th><th>Age</th><th>Position</th><th>Team</th><th>Delete</th><th>Edit</th></tr></thead>';
    $.each(data, function(index, val) {
        tableHtml += '<tr><td>'+val.playername+'</td><td>'+val.age+'</td><td>'+val.position+'</td><td>'+val.team+'</td><td><button class="delete" value="'+val._id+'">Delete</button></td><td><button class="edit" value="'+val._id+'">Edit</button></td></tr>';
    });
    tableHtml += '</table>';
    $(target).append(tableHtml);
}

// function to generate form
function generateForm(data, target){
    clearContainer(target);
    //Change form according to your fields
    $(target).append('<form id="my-form"></form>');
    var innerForm = '<fieldset><legend>Player Form</legend><p><label>Player Name: </label>'+'<input type="hidden" name="id" id="id"/>'+'<input type="text" name="playername" id="playername" /></p>' + '<p><label>Age: </label><input type="text" name="age" id="age" /></p>'+ '<p><label>Hometown: </label><input type="text" name="city" id="city" />'+ ' ' + '<input type="text" name="country" id="country" /></p>' + '<p><label>Gender: </label><input type="text" name="gender" id="gender" /></p>'+ '<p><label>Handedness: </label><input type="text" name="handedness" id="handedness" /></p>'+ '<p><label>Broom: </label><input type="text" name="broom" id="broom" /></p>'+ '<p><label>Position: </label><input type="text" name="position" id="position" /></p>'+ '<p><label>Team: </label><input type="text" name="team" id="team" /></p>'+ '<p><label>Favorite Color: </label><input type="text" name="favoritecolor" id="favoritecolor" /></p>'+ '<p><label>Headshot: </label><input type="text" name="headshot" id="Headshot" /></p>'+ '<input type="submit"/>';
    $('#my-form').append(innerForm);
    //Change values according to your data
    if(data != null){
        $.each(data, function(index, val) {
            $('#id').val(val._id);
            $('#playername').val(val.playername);
            $('#age').val(val.age);
            $('#city').val(val.city);
            $('#country').val(val.country);
            $('#gender').val(val.gender);
            $('#handedness').val(val.handedness);
            $('#broom').val(val.broom);
            $('#position').val(val.position);
            $('#team').val(val.team);
            $('#favoritecolor').val(val.favoritecolor);
            $('#Headshot').val(val.headshot);
        });
    }
}

function submitForm(url, form){
    $.post(url, form.serialize(), function(data) {
        showNotification(data, $('#notification'));
    });
}

function showNotification(data, target){
    clearContainer(target);
    target.append('<p>'+data+'</p>');
}

function clearContainer(container){
    container.html('');
}
});

server.js

const _ = require('lodash');
const {ObjectID} = require('mongodb');
const express = require('express');
const path = require('path');
const bodyParser = require('body-parser');

var {mongoose} = require('./db/mongoose');
var {Player} = require('./models/player');

var app = express();
const port = process.env.PORT || 3000;

app.use(express.static(__dirname+'./../public'));
app.use(bodyParser.json());

app.get('/', (req, res) => {
  res.sendFile('index.html');
});

app.post('/player', (req, res) => {
  var player = new Player({
    playername: req.body.playername,
    age: req.body.age,
    city: req.body.city,
    country: req.body.country,
    gender: req.body.gender,
    handedness: req.body.handedness,
    broom: req.body.broom,
    position: req.body.position,
    team: req.body.team,
    favoritecolor: req.body.favoritecolor,
    headshot: req.body.headshot
  });
  player.save().then((doc) => {
    res.send(doc);
  }, (e) => {
    res.status(400).send(e);
  });
});

app.get('/players', (req, res) => {
  Player.find().then((players) => {
    res.send(players);
  }, (e) => {
    res.status(400).send(e);
  });
});

app.get('/players/:id', (req, res) => {
  var id = req.params.id;
  if (!ObjectID.isValid(id)) {
    return res.status(404).send();
  }
  Player.findById(id).then((player) => {
    if (player) {
      res.send(player);
    } else {
      return res.status(404).send();
    }
  }).catch((e) => {
    res.status(400).send();
  });
});

app.delete('/players/:id/delete', (req, res) => {
  var id = req.params.id;
  if (!ObjectID.isValid(id)) {
    return res.status(404).send();
  }
  Player.findByIdAndRemove(id).then((player) => {
    if (player) {
      res.send(player);
    } else {
      return res.status(404).send();
    }
  }).catch((e) => {
    res.status(400).send();
  });
});

app.patch('/players/:id/edit', (req, res) => {
  var id = req.params.id;
  var body = _.pick(req.body, ['playername', 'age', 'city', 'country', 'gender', 'handedness', 'broom', 'position', 'team', 'favoritecolor', 'headshot']);
  if (!ObjectID.isValid(id)) {
    return res.status(404).send();
  }
  Player.findByIdAndUpdate(id, {$set: body}, {new: true}).then((player) => {
    if (!player) {
      return res.status(404).send();
    } else {
      res.send(player);
    }
  }).catch((e) => {
    res.status(400).send();
  })
});

app.listen(port, () => {
  console.log(`Started on port ${port}`);
});

module.exports = {app};

【问题讨论】:

    标签: javascript jquery ajax node.js express


    【解决方案1】:

    当我单击删除按钮时,我收到一个 404 状态代码,响应为“Cannot GET /players/123/delete”。如何调整我的显示代码以使用 DELETE 而不是 GET? (如果我能弄清楚如何在 display.js 代码中传递请求方法,我相信我可以使用正确的 http 请求方法将我的所有路由重构为仅 /players/:id)。

    这是因为您的应用具有app.delete('/players/:id/delete') 而不是app.get('/players/:id/delete')。但是,您的服务器端代码应该不会有太大变化,只需稍作调整即可:

    • 如果你有一个app.delete(),那么真的不需要在资源末尾有动词delete

    您还需要使用 HTTP 方法 DELETE 发出请求,而不是在 display.js 中发出 GET。只需使用$.ajax() 发出DELETE 请求,并将type 设置为'DELETE'

    当我单击编辑按钮时,我的 GET /player/:id 路由会获取正确的信息(状态代码 200 和预览显示 JSON 对象),但不会填充表单中的字段。

    这是因为您的$.each() 实现假定data 将始终是一个数组,而不是永远和对象,但是,您的generateForm() 正在传递一个Player 对象。更改您的 $.each() 以对数组处理和对象处理进行类型检查。请参阅以下更改。

    当我尝试提交新记录时,我收到一个 400 状态代码,其中包含验证错误,提示需要路径年龄。每个字段名称实际上都是相同的错误,所以我假设我没有正确地将字段中的值传递到 JSON 对象中。

    如果您对generateForm() 进行上述更改,它应该可以解决此问题。


    只需使用下面的 sn-ps 并替换您应用中的这些部分,它应该可以工作。

    server.js

    app.delete('/players/:id', (req, res) => {
      var id = req.params.id;
      if (!ObjectID.isValid(id)) {
        return res.status(404).send();
      }
      return Player.findByIdAndRemove(id).then((player) => {
        if (player) {
          res.status(200).json(player);
        } else {
          return res.status(404).send();
        }
      }).catch((e) => {
        res.status(400).send();
      });
    });
    
    app.patch('/players/:id', (req, res) => {
      var id = req.params.id;
      var body = _.pick(req.body, ['playername', 'age', 'city', 'country', 'gender', 'handedness', 'broom', 'position', 'team', 'favoritecolor', 'headshot']);
      if (!ObjectID.isValid(id)) {
        return res.status(404).send();
      }
      Player.findByIdAndUpdate(id, {$set: body}, {new: true}).then((player) => {
        if (!player) {
          return res.status(404).send();
        } else {
          res.status(200).json(player);
        }
      }).catch((e) => {
        res.status(400).send();
      })
    });
    
    app.post('/players', (req, res) => {
      var player = new Player({
        playername: req.body.playername,
        age: req.body.age,
        city: req.body.city,
        country: req.body.country,
        gender: req.body.gender,
        handedness: req.body.handedness,
        broom: req.body.broom,
        position: req.body.position,
        team: req.body.team,
        favoritecolor: req.body.favoritecolor,
        headshot: req.body.headshot
      });
    
      return player.save().then((doc) => {
        return res.status(200).json(doc);
      }, (e) => {a
        return res.status(400).send(e);
      });
    });
    

    display.js

    // Handle form submit event for both update & create
    // if the ID_FIELD is present the server would update the database otherwise the server would create a record in the database
    $('#container').on('submit', '#my-form', function(event) {
        var id = $('#id').val();
        let formData = {};
    
        $.each($('#myForm').serializeArray(), function(_, kv) {
              if (formData.hasOwnProperty(kv.name)) {
                formData[kv.name] = $.makeArray(formData[kv.name]);
                formData[kv.name].push(kv.value);
              }
              else {
                formData[kv.name] = kv.value;
              }
            });
    
        console.log(id);
        if (id != "") {
            event.preventDefault();
            $.ajax({
                url: baseURL + '/players/' + id,
                type: 'PATCH',
                success: function(edited) {
                    // Handle returned edited player
                }
            })
        } else {
            event.preventDefault();
            $.ajax({
                url: baseURL + '/players',
                type: 'POST',
                success: function(created) {
                  // Handle created player
                }
            })
        }
    });
    
    // Handle table click event for delete
    $('#container').on('click', '.delete', function(event) {
        var id = $(this).val();
        // change the url parameters based on your API here
        // remember to create delete functionality on the server side (Model and Controller)
        // Using an JQuery AJAX GET request to get data form the server
        $.ajax({
            url: baseURL + '/players/' + id,
            type: 'DELETE',
            success: function(data) {
              //Generate table again after delete
            //change the url based on your API parameters here
            // Using an JQuery AJAX GET request to get data from the server
            $.getJSON(baseURL+'/players', function(data) {
                generateTable(data, $('#container'));
            });
            }
        });
    });
    
    // function to generate form
    function generateForm(data, target){
        clearContainer(target);
        //Change form according to your fields
        $(target).append('<form id="my-form"></form>');
        var innerForm = '<fieldset><legend>Player Form</legend><p><label>Player Name: </label>'+'<input type="hidden" name="id" id="id"/>'+'<input type="text" name="playername" id="playername" /></p>' + '<p><label>Age: </label><input type="text" name="age" id="age" /></p>'+ '<p><label>Hometown: </label><input type="text" name="city" id="city" />'+ ' ' + '<input type="text" name="country" id="country" /></p>' + '<p><label>Gender: </label><input type="text" name="gender" id="gender" /></p>'+ '<p><label>Handedness: </label><input type="text" name="handedness" id="handedness" /></p>'+ '<p><label>Broom: </label><input type="text" name="broom" id="broom" /></p>'+ '<p><label>Position: </label><input type="text" name="position" id="position" /></p>'+ '<p><label>Team: </label><input type="text" name="team" id="team" /></p>'+ '<p><label>Favorite Color: </label><input type="text" name="favoritecolor" id="favoritecolor" /></p>'+ '<p><label>Headshot: </label><input type="text" name="headshot" id="Headshot" /></p>'+ '<input type="submit"/>';
        $('#my-form').append(innerForm);
    
        //Change values according to your data
        if(data instanceof Array){
            $.each(data, function(index, val) {
                $('#id').val(val._id);
                $('#playername').val(val.playername);
                $('#age').val(val.age);
                $('#city').val(val.city);
                $('#country').val(val.country);
                $('#gender').val(val.gender);
                $('#handedness').val(val.handedness);
                $('#broom').val(val.broom);
                $('#position').val(val.position);
                $('#team').val(val.team);
                $('#favoritecolor').val(val.favoritecolor);
                $('#Headshot').val(val.headshot);
            });
        }
        else if (typeof data === 'object') {
            $.each(data, function(key, value) => {
                $('#' + key).val(value);
            });
        }
    }
    

    【讨论】:

    • 这绝对解决了 DELETE 问题。谢谢! JSON 对象问题,当使用 JSON.parse() 时,我得到一个 SyntaxError,上面写着“位置 1 的 json 中的意外标记 o”。难道是我需要使用 JSON.stringify() 代替吗?尝试这样做时,应用程序从表格移动到表单,但会产生一个 TypeError,显示“无法使用 'in' 运算符在...中搜索 'length'”。想法?
    • @SepticReVo 尝试在成功执行操作时将您执行res.send() 的所有位置替换为res.status(200).json()。这将保证Content-Type 标头将设置为application/json。所有响应都是字符串,这就是您需要执行 JSON.parse() 的原因。对 JSON 字符串执行 JSON.stringify() 只会导致双重字符串化 JSON 值。
    • 没有变化。 JSON.parse() 仍然给出语法错误,而 JSON.stringify() 仍然给出 TypeError。
    • 这解决了我的 GET /players/id 问题。谢谢!当我发出 PATCH 或 POST 请求时,我仍然遇到 400 问题。我意识到表单中隐藏 id 的值是 id 而不是 _id,但总体工作仍然没有骰子。
    【解决方案2】:

    尝试使用res.json(player); 而不是res.send(player);,以便以 JSON 格式发送数据。

    另外,请尝试使用this answer 中的代码代替$.post(url, form.serialize(), function(data) {,以便以JSON 格式发送数据。

    【讨论】:

      猜你喜欢
      • 2021-04-14
      • 2016-11-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-16
      相关资源
      最近更新 更多