nodejs: 真正的一份代码,到处运行
发布于 4年前 作者 fengmk2 5237 次浏览 最后一次编辑是 3年前

现状: 冗余


在web开发中,我们是否常常会使用不同的编程语言实现相同的功能?

如一个文件上传功能,需要对上传文件进行文件格式限制。我们通常会使用后缀名做限制。

前端


为了用户体验,会在页面对用户选择的文件进行判断,合法才让用户可以上传。
function is_filetype(filename, types) {

types = types.split(‘,’);
var pattern = '.(';
for(var i=0; i<types.length; i++) {
if(0 != i) {
pattern += '|’;
}
pattern += types[i].trim();
}
pattern += ')$’;
return new RegExp(pattern, ‘i’).test(filename);
};

// 此处省略N行代码
if(!is_filetype($(‘#uploadfile’).val(), ‘doc,pdf,txt,wps,odf,md,png,gif,jpg’)){
can_submit = false; // 不允许上传
$(‘#uploadfile’).val(‘’);
alert('只允许上传: ' + constant.RESUME_FILETYPES);
}
// 此处省略N行代码

后端


由于担心恶意上传,无法避免地需要重新对用户上传的文件进行判断。
于是我又用python写了一个判断文件后缀的逻辑
import re


def is_filetype(filename, types):
types = types.split(‘,’)
pattern = '.(' + '|’.join([t.strip() for t in types]) + ')$’;
return re.search(pattern, filename, re.I) != None

# web request handler
# 此处省略N行代码

导致这样重复工作的原因是为什么?



  1. 前端永远不可信;

  2. 前端和后端使用不同的编程语言;


这样的冗余会带来什么代价?



  1. 修改业务逻辑,需要重复做2次:如突然发现少支持了 docx 文件类型,需要同时修改javascript代码和python代码

  2. 增加确保javascript代码和python代码业务逻辑一致的代价。需要分别写2种测试,unit test跑多一倍。


nodejs时代:DRY


Use nodejs no more DRY !

一份代码,前端后端同时运行


// constant.js

(function(exports){

exports.RESUME_FILETYPES = 'doc,docx,pdf,txt,wps,odf,md,png,gif,jpg’;

})( (function(){
if(typeof exports === ‘undefined’) {
window.constant = {};
return window.constant;
} else {
return exports;
}
})() );

// util.js
(function(exports){

/**
* 移除字符串两端的空白字符
*
* [[[[[[[[[[[[[[[[[[[[[[[@return](/user/return)](/user/return)](/user/return)](/user/return)](/user/return)](/user/return)](/user/return)](/user/return)](/user/return)](/user/return)](/user/return)](/user/return)](/user/return)](/user/return)](/user/return)](/user/return)](/user/return)](/user/return)](/user/return)](/user/return)](/user/return)](/user/return)](/user/return) {String}
* [[[[[[[[[[[[[[[[[[[[[[[[@api](/user/api)](/user/api)](/user/api)](/user/api)](/user/api)](/user/api)](/user/api)](/user/api)](/user/api)](/user/api)](/user/api)](/user/api)](/user/api)](/user/api)](/user/api)](/user/api)](/user/api)](/user/api)](/user/api)](/user/api)](/user/api)](/user/api)](/user/api)](/user/api) public
/
String.prototype.trim = function(){
return this.replace(/(^\s
)|(\s*$)/g, “”);
};

/**
* 判断是否自定类型的文件
*
* [[[[[[[[[[[[[[[[[[[[[[[[@param](/user/param)](/user/param)](/user/param)](/user/param)](/user/param)](/user/param)](/user/param)](/user/param)](/user/param)](/user/param)](/user/param)](/user/param)](/user/param)](/user/param)](/user/param)](/user/param)](/user/param)](/user/param)](/user/param)](/user/param)](/user/param)](/user/param)](/user/param)](/user/param) {String}filename
* [[[[[[[[[[[[[[[[[[[[[[[[@param](/user/param)](/user/param)](/user/param)](/user/param)](/user/param)](/user/param)](/user/param)](/user/param)](/user/param)](/user/param)](/user/param)](/user/param)](/user/param)](/user/param)](/user/param)](/user/param)](/user/param)](/user/param)](/user/param)](/user/param)](/user/param)](/user/param)](/user/param)](/user/param) {String}types, 多个类型使用,号分隔,如 doc,docx,txt
* [[[[[[[[[[[[[[[[[[[[[[[@return](/user/return)](/user/return)](/user/return)](/user/return)](/user/return)](/user/return)](/user/return)](/user/return)](/user/return)](/user/return)](/user/return)](/user/return)](/user/return)](/user/return)](/user/return)](/user/return)](/user/return)](/user/return)](/user/return)](/user/return)](/user/return)](/user/return)](/user/return) {Boolean} true or false
* [[[[[[[[[[[[[[[[[[[[[[[[@api](/user/api)](/user/api)](/user/api)](/user/api)](/user/api)](/user/api)](/user/api)](/user/api)](/user/api)](/user/api)](/user/api)](/user/api)](/user/api)](/user/api)](/user/api)](/user/api)](/user/api)](/user/api)](/user/api)](/user/api)](/user/api)](/user/api)](/user/api)](/user/api) public
*/
var is_filetype = exports.is_filetype = function(filename, types) {
types = types.split(‘,’);
var pattern = '.(';
for(var i=0; i<types.length; i++) {
if(0 != i) {
pattern += '|’;
}
pattern += types[i].trim();
}
pattern += ')$’;
return new RegExp(pattern, ‘i’).test(filename);
};

})( (function(){
if(typeof exports === ‘undefined’) {
window.util = {};
return window.util;
} else {
return exports;
}
})() );

前端


<script src="/js/util.js"></script>

<script src="/js/constant.js"></script>

// 此处省略N行代码
if(!util.is_filetype($(‘#uploadfile’).val(), constant.RESUME_FILETYPES)){
can_submit = false; // 不允许上传
$(‘#uploadfile’).val(‘’);
alert('只允许上传: ' + constant.RESUME_FILETYPES);
}
// 此处省略N行代码

后端


var util = require(‘./public/js/util.js’),

constant = require(‘./public/js/constant.js’);
app.post('/resume/upload/:job_id’, function(req, res, next){
req.form.complete(function(err, fields, files){
if(!util.is_filetype(filepath, constant.RESUME_FILETYPES)) {
// 由于客户端已做判断,所以这样的情况都是恶意上传的,直接提示
res.send(‘文件格式错误: ' + filepath
+ ' , 请上传’ + constant.RESUME_FILETYPES + ‘格式的文件’);
return;
}
// save file …
// 此处省略N行代码
});
});

wow,没冗余了吧!done

其他常用场景



  • 常量定义

  • 各种有用的工具模块,如字符串操作


有爱


^_^ 希望本文对你有帮助

22 回复

本质上还是前后端用相同的编程语言导致的复用

但是给我们一个启示:在nodejs下,前端和后端是一个整体,代码一样可以复用

是的,不用过度去区分前端和后端

我觉得后端只靠扩展名检测无法有效防止恶意上传吧?

但现状还是很遗憾前端的各种兼容性问题还是得处理,因为JS引擎不一致,至少需要一个adapter,比如trim方法…

正解!扩展名是个小儿科的把戏,识别mime type才是王道。

Node. 如果是这样,还有必要在前后端都调用完全一样的逻辑判断过程吗?对于文中提到的两点:前端永远不可信;前端和后端使用不同的编程语言。并没有解决第一个担忧啊。

前后两端的复用能力,是值得实践和积累的。前端的兼容问题,并不妨碍未来的开发趋势。

本人平常一直在强调并信奉前端工程师的职责范围包括:客户端开发(PC/手持设备)、Web页面制作、与用户的交互(JS/AS/…)、与服务器的交互(Ajax/JSONP/…)、服务器端应用展现层开发。
虽然因人而异、因业务而异,只要认可这一点,那个人就会最大限度的利用各种技术和资源来提高复用和开发效率,推广本nodejs也会更加自然。

前端和后端统一了语言,对程序员而言nodejs最大的好处莫过于此。

本来所有用户的输入都是不可信的,你所谓的第一个担忧实际上只是一个原则不要完全相信用户输入,由于前端的验证等都可能被绕过。
而正常逻辑下,用户在页面上填写的内容没有填写完毕,即可立即进行客户端校验,这只是减轻服务器端压力的一种做法,你完全可以客户端不做任何验证,但是这一方面增加服务器压力,另外一方面也在一定程度上降低了可用性。

Google搜索Nodejs路过。
对JS不太过敏,还是习惯用Python,Java类的语言来写后端。而且前后台根本不存在DRY的问题,前台作后缀分析只是为了降低一部分后端的负担,无论前端是否作判断,后端都一样要作判断。
而且这种趋势感觉会让WEB入门难度更大,同样的语言,分别用来开发前台和后台,对于刚刚入门的人可能会感到异常困惑…~而且JS的prototype继承方式也感觉比较怪。

语言统一还是比较支持的,但有一个的问题,前后端代码相互复用成为潮流的话,由于js在前端对用户来说是可见的,攻击者连猜测都不需要,就明确知道服务器端的验证逻辑了,+_+。

由于js在前端对用户来说是可见的,攻击者连猜测都不需要,就明确知道服务器端的验证逻辑了

这个问题确实比较严重

原来的情况也是一样的

js引擎对html dom解析的支持现在所有后台语言都无法企及,如果在服务器端跑上jquery,那么从一个网页中抽取内容就不用正则表达式这种容易出错的方式了。

另外js对json的支持也是最方便的。

支持前后端可以共享一小部分逻辑。
不过,个人认为前后端同样的处理逻辑不可取:
1. 前端主要是相对简单的校验,提高反应速度和减轻后端负担 (庞大的JS也影响载入速度)
2. 后端主要是完整的检验,保证安全性和一致性

前后端不要划分的太清,不过后端的代码还是不要暴漏在外部的好

我想说一下我的理解,前端和后段的问题在于设计时候的问题而不是语言单方面可以解决的。

如果按照你这么说的话,用Java或者CGI在后面直接输出HTML和Javascript一样可以做到你所谓的“非冗余”开发。

业务逻辑的判断和界面的判断本身就是要分开处理,只能说明你平时设计系统的时候没有真正做到层之间的完善的解耦处理。

我认为不能复用,代码都透明了~有些做成公用函数透明我觉得无所谓~

从项目管理和配合来说,前后端开发语言的统一具有非常积极的意义,至少在中国,沟通成本是软件开发的最大成本。

回到顶部