【问题标题】:Node.js, Express, Mongoose - input validation - within route or model?Node.js,Express,Mongoose - 输入验证 - 在路由或模型中?
【发布时间】:2014-12-03 17:37:50
【问题描述】:

我有一个接受 JSON 帖子的 rest api 资源。示例:

{
"location": {
    "coordinates": [
        -122.41941550000001,
        37.7749295
    ]
}

然后由 Express 从请求中收集坐标:

module.exports.create = function(req, res, next) {

    var coordinates = req.body.location.coordinates;
    ....

然后将这些提交给 Mongoose 模型。我正在针对缺少 location.coordinates 的地方编写测试,例如

{
"foo": {
    "bar": [
        -122.41941550000001,
        37.7749295
    ]
}

然后在模型的验证部分失败:

locationSchema.path('location.coordinates').validate(function(coordinates){
                                            ^
TypeError: Cannot call method 'validate' of undefined

所以我的问题是如何验证输入是否正确?这应该在到达模型之前在路线中完成,还是应该在模型中完成?任何有关如何的示例也将不胜感激。

作为参考,Mongoose 模型类似于:

var locationSchema = new Schema({
    userid: { type: Number, required: true },
    location: {
        type: [{
            type: "String",
            required: true,
            enum: ['Point', 'LineString', 'Polygon'],
            default: 'Point'
        }], required: true,
        coordinates: { type: [Number], required:true }
    },
    create_date: { type: Date, default: Date.now }
});


locationSchema.path('location.coordinates').validate(function(coordinates){
    ...
}, 'Invalid latitude or longitude.');

【问题讨论】:

    标签: node.js validation express mongoose


    【解决方案1】:

    我的典型方法是在路由和模型之间引入一个服务层,这就是验证发生的地方。不要从“Web 服务”的意义上去想“服务”;它只是围绕给定域提供了一个抽象级别。这有以下好处:

    • 它为您提供了处理持久数据和/或外部数据的通用抽象。也就是说,无论您是与来自 Mongoose 的数据还是与外部 Web 服务进行交互,您的所有路由逻辑都可以简单地与一致的界面进行交互。
    • 它提供了围绕持久性细节的合理封装,允许您在不影响所有路由的情况下更换实现。
    • 它允许您对非路由消费者(例如集成测试套件)重用代码。
    • 它提供了一个很好的模拟层(例如用于单元测试)。
    • 它提供了一个非常清晰的“验证和业务逻辑发生在此处”层,即使您的数据分布在多个不同的数据库和/或后端系统中也是如此。

    下面是一个简化示例:

    location-service.js

    var locationService = module.exports = {};
    
    locationService.saveCoordinates = function saveCoordinates(coords, cb) {
        if (!isValidCoordinates(coords)) {
            // your failed validation response can be whatever you want, but I
            // like to reserve actual `Error` responses for true runtime errors.
            // the result here should be something your client-side logic can
            // easily consume and display to the user.
            return cb(null, {
                success: false,
                reason: 'validation',
                validationError: { /* something useful to the end user here */ }
            });
        }
    
        yourLocationModel.save(coords, function(err) {
            if (err) return cb(err);
    
            cb(null, { success: true });
        });
    };
    

    some-route-file.js

    app.post('/coordinates', function(req, res, next) {
        var coordinates = req.body.location.coordinates;
    
        locationService.saveCoordinates(coordinates, function(err, result) {
            if (err) return next(err);
    
            if (!result.success) {
                // check result.reason, handle validation logic, etc.
            } else {
                // woohoo, send a 201 or whatever you need to do
            }
        });
    });
    

    目前,我已将这种结构应用于 3 或 4 个不同的网络应用程序和 API,并且越来越喜欢它。

    【讨论】:

    • 绝对将其保存为 sn-p!
    • @jmar777 - 非常有帮助。在我的应用程序中引入该抽象层非常有意义。感谢您花时间分享您的想法,这对我帮助很大!
    【解决方案2】:

    在我看来,验证应该在一开始就发生,首先在客户端,然后在路由中。

    人们对传递无效数据并没有太大兴趣,白白使用资源,所以越早将其标记为无效,就越早释放资源。

    要检查您的坐标是否存在,您可以使用:

    if(req.body.location.coordinates){
    //do your thing
    }
    

    【讨论】:

    • 感谢 cmets。那么我将如何验证路由中的输入呢?
    • 您要验证坐标是数字,还是仅验证 req.body.location.coordinates 是否存在?
    • Nooo... 客户端验证与此无关。客户端验证很方便用户使用,但在安全性方面什么都没有,因为用户可以禁用它。
    • 我说:如果可能的话。第一层验证避免发送半填的表格、不正确的地址等......
    • @xShirase - 如果存在。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-02
    • 2011-11-27
    • 2017-08-11
    • 1970-01-01
    • 2017-07-11
    相关资源
    最近更新 更多