我正在创建一个MEAN堆栈,我想澄清以下内容。
从编码标准的 Angular 来看,我知道验证应同时在客户端和服务器端执行。我想实现的是执行完全相同的验证代码,以便不再重复代码。这更像是客户端和服务器端的共享代码。
因此,如何让angular js和Express js调用相同的.js文件来执行验证?甚至有可能吗?
谢谢!
最佳答案
您确定可以做到这一点。 RemObjects DataAbstract(http://old.wiki.remobjects.com/wiki/Business_Rules_Scripting_API)使用此方法。这里的原理是定义将适用于客户端和服务器或仅适用于服务器的业务规则。您几乎永远不必仅在客户端上检查业务规则,因为您永远都不能“信任”客户端来检查您的业务规则。
CQRS和DDD是两条可以在这里为您提供帮助的架构原则。域驱动设计将“清洁”或“优化”您的代码,从而使基础架构脱离核心的“域”逻辑。而且业务规则仅适用于域,因此保持域与其余域隔离是一个好主意。
命令查询责任隔离。我很喜欢这个。基本上,您定义了一组在应用之前将被验证的命令。不再有类似Model.Set('a',2)的类似机器的代码。使用此原理的代码将看起来像MyUnderstandableBusinessObject.UnderstandableCommand(aFriendlyArgument)。在应用业务规则时,这非常方便,因为您的实际命令可以反射(reflect)您域的用例。
当我处理node.js/javascript项目时,我也总是遇到此问题。问题是您没有客户端和服务器都可以理解的标准化ORM。这很矛盾,因为node.js和浏览器使用相同的语言运行。当我被Node.js吸引时,我告诉自己,客户端和服务器都运行相同的语言,这将节省很多时间。但这是错误的,因为即使npm真的很活跃,也没有那么多成熟和专业的工具。
我也想构建一个客户端/服务器都可以理解的ORM,并向它添加一个关系方面(以便它与SQL兼容),但我还是放弃了该项目。 https://github.com/ludydoo/affinity
但是,还有其他两种解决方案。 Backbone是一个,它很轻巧。
您在这里要进行的业务规则检查的实际实现。您需要将模型中的“验证”部分提取到另一个可以共享的对象中。可以帮助您入门的东西:
https://jsfiddle.net/ludydoo/y0otcvrf/
BusinessRuleRepository = function() {
this.rules = [];
}
BusinessRuleRepository.prototype.addRule = function(aModelClass, operation, callback) {
this.rules.push({
class: aModelClass,
operation: operation,
callback: callback
})
}
BusinessRuleRepository.prototype.validate = function(object, operation, args) {
_.forIn(this.rules, function(rule) {
if (object.constructor == rule.class && operation == rule.operation) {
rule.callback(object, args)
}
})
}
MyObject = function() {
this.a = 2;
}
MyObject.prototype.setA = function(value) {
aBusinessRuleRepo.validate(this, 'setA', arguments);
this.a = value;
}
// Creating the repository
var aBusinessRuleRepo = new BusinessRuleRepository();
//-------------------------------
// shared.js
var shared = function(aRepository) {
aRepository.addRule(MyObject, 'setA', function(object, args) {
if (args[0] < 0) {
throw 'Invalid value A';
}
})
}
if (aBusinessRuleRepo != undefined) {
shared(aBusinessRuleRepo);
}
//-------------------------------
// Creating the object
var aObject = new MyObject();
try {
aObject.setA(-1); // throws
} catch (err) {
alert('Shared Error : ' + err);
}
aObject.setA(2);
//-------------------------------
// server.js
var server = function(aRepository) {
aRepository.addRule(MyObject, 'setA', function(object, args) {
if (args[0] > 100) {
throw 'Invalid value A';
}
})
}
if (aBusinessRuleRepo != undefined) {
server(aBusinessRuleRepo);
}
//-------------------------------
// on server
try {
aObject.setA(200); // throws
} catch (err) {
alert('Server Error :' + err);
}
我为您展示的代码小示例非常简单,并且仅在应用命令之前检查业务规则。也许您可以添加参数'beforeCommand'和'afterCommand'来检查更改前后的业务规则。然后,如果您添加了在应用命令后检查业务规则的可能性,那么您必须能够回滚更改(我认为主干网具有此功能)。
祝你好运
您可以通过自动获取您所使用的方法的名称来实现一点自动化(Can I get the name of the currently running function in JavaScript?)
function checkBusinessRules(model, arguments){
businessRuleRepo.validate(model, getCalleeName, arguments);
}
Model.prototype.command = function(arg){
checkBusinessRules(this, arguments);
// perform logic
}
编辑2
我想在我的第一个答案上更正一个小细节。不要在属性(property)设定者上实现您的业务规则!改用业务操作名称:
您必须确保始终通过方法设置模型属性。如果直接通过分配值来设置模型属性,那么您将绕过整个业务规则处理器。
便宜的方法是通过标准的 setter 来做到这一点,例如
SetMyProperty(value);
SetAnotherProperty(value);
这是一种低级业务规则逻辑(基于getter和setter)。然后,您的业务规则也将是低级的。哪一种不好。
更好的是,您应该通过业务上可理解的高级方法名称(例如
RegisterClient(client);
InvalidateMandate(mandate);
然后,您的业务规则将变得更加易于理解,并且您将几乎有一个很好的时间来实现它们。
BusinessRuleRepository.add(ModelClass, "RegisterClient", function(){
if (!Session.can('RegisterClient')) { fail('Unauthorized'); }
})
关于angularjs - 如何在客户端和服务器之间共享Javascript业务规则?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/34241315/