Node.js,Express和Mongoose - 输入验证 - 在路由或模型中处理?

5

我有一个REST API资源,可以接受JSON POST请求。例如:

{
"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.');
2个回答

6
我的典型做法是在路由和模型之间引入服务层,这就是验证发生的地方。不要把“服务”理解为“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
        }
    });
});

我已经在三到四个不同的网络应用程序和API中应用了这种结构,并且越来越喜欢它。

一定将其保存为代码片段! - xShirase
@jmar777 - 非常有帮助。在我的应用程序中引入这个抽象层是非常明智的选择。感谢您花时间分享您的想法,这对我帮助很大! - Ben

1
在我看来,验证应该在最开始时就在客户端进行,然后在路由中进行。
没有太多兴趣传递无效数据,浪费资源,所以你越早标记它为无效,你就越早释放资源。
要检查坐标的存在性,可以使用以下方法:
if(req.body.location.coordinates){
//do your thing
}

谢谢您的评论。那么我该如何在路由中验证输入呢? - Ben
不要啊...客户端验证与此无关。客户端验证对用户方便很有帮助,但在安全方面却毫无作用,因为用户可以禁用它。 - jmar777
@xShirase - 如果存在。 - Ben
@xShirase 或许我误解了你的意思,但是你说:“在我看来,验证应该在最开始时就进行,如果可能的话,在客户端或路由中进行。”。那个“或”让我有些担心。 :) - jmar777
@jmar777 好的,已编辑😉你的回答更加完整,不过仅仅为了测试变量的存在而言有点过度了。通过问题的描述,我也感到困惑,无法区分“存在”和“验证”的差别。结果证明,OP只是想要验证存在性。 - xShirase
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接