Chinaunix

标题: Web开发技术讨论之二:如何编写高效JavaScript代码 [打印本页]

作者: send_linux    时间: 2014-02-28 16:21
标题: Web开发技术讨论之二:如何编写高效JavaScript代码
获奖名单已公布,详情请看:http://bbs.chinaunix.net/thread-4132998-1-1.html

活动背景:
学会编写JavaScript程序容易,但要成为专家却实属不易。一方面是由于JavaScript语言的设计思想与Java、C#等大众语言区别很大,另一方面是由于其设计时的仓促性导致JavaScript语言本身精华与糟粕并存。

另外因为跨平台的缘故,JavaScript历史悠久且实现多样化,因此我们很难确定哪些特性在哪些平台上是可用的。而令事态更加严峻的事实是JavaScript的主要生态系统——Web浏览器,它并不支持让程序员指定某个JavaScript的版本来执行代码。由于最终用户可能使用不同Web浏览器的不同版本,因此,我们必须精心地编写Web程序,使得其在所有的浏览器上始终工作如一。

而且随着JavaScript语言本身的不断发展,JavaScript并不只是针对客户端Web编程。JavaScript的其他应用包括服务器端程序、浏览器扩展以及针对移动和桌面应用程序的脚本。某些情况下你可能需要一个特定的JavaScript版本。对于这些情况,利用特定平台支持的JavaScript特定实现的额外特性是有意义的。

所以,对于任何一个想要做到严谨编程的JavaScript开发者来说,如何写出高效的现代JavaScript代码,是我们不断提升道路上一个必经之路。

本期话题:
1、谈谈您的JavaScript的学习和提升的经验。
2、谈谈您的一些JavaScript的高效编程的技巧。

活动时间:
2014年2月28日-3月20日

本期嘉宾:Web开发版资深版主:HonestQiao

活动奖品:
优秀奖6名:奖励《Effective JavaScript:编写高质量JavaScript代码的68个有效方法》图书一本

奖品信息:


    原书名:Effective JavaScript: 68 Specific Ways to Harness the Power of JavaScript (Effective Software Development Series)
    原出版社: Addison-Wesley Professional
    作者: (美)David Herman   
    译者: 黄博文 喻杨
    丛书名: Effective系列丛书
    出版社:机械工业出版社
    ISBN:9787111446231
    上架时间:2013-11-13
    出版日期:2014 年1月
    开本:16开
    页码:164

样张阅读:
http://wenku.it168.com/d_001329728.shtml

活动要求:
1、 要言之有物,不能低于20个字。
2、 本次话题主要关注JavaScript程序设计的讨论,其他问题可能不做重点



作者: lz小骆驼    时间: 2014-02-28 16:51
好活动,支持一下
作者: jimmy-_-lixw    时间: 2014-02-28 17:45
板凳。好活动,支持一下
作者: qingduo04    时间: 2014-02-28 18:45
好活动支持一下。
没有接触这门编程语言,有个大学同学 对这个研究很深,搞了一本书。

最主要的一句话: 一定要有兴趣.......
作者: starwing83    时间: 2014-02-28 21:08
好活动,支持。

我最近正在写JS,主要是node.js。node.js用的引擎是V8,相对来说是很成熟的。比起需要兼容浏览器的同学们可幸福多了。反正js我觉得是过于灵活了,所以给自己规定一些规则还是蛮好的。

网上有很多规则,不过总结出一套自己的还是蛮有意义的。很多规则都是踩过坑以后才总结出来的。所以规则总是要看,只是不要当教条就行,毕竟js的优势就是灵活。

我个人觉得,在规则之上,也得有一套“元规则”,比如说,预先分配好模块、决定接口这些。大家都知道的。至于“逗号写在前面”这种,我觉得是见仁见智的规则。

切分成模块以后,单个模块可以用node或者glue这些东西跑,可以做单元测试,这样就很大程度上避免了bug。

工具的话,intellj Idea的补全很爽,会提示很多问题。不过我用Vim习惯了,幸好idea有Vim的插件。

另外,写js最重要的一点是,多关注一些开源的js项目,我个人比较喜欢Q这个项目(https://github.com/kriskowal/q),它提供了一套控制流的包装,用起来很爽。配合orm简直就是神器啊……

恩,上面算抛砖引玉,希望大家有更多的心得可以分享~
作者: forgaoqiang    时间: 2014-03-01 08:21
本帖最后由 forgaoqiang 于 2014-03-09 23:52 编辑

Javascript 经常用 这个是个好活动

1、谈谈您的JavaScript的学习和提升的经验。
最初认识到Javascript应该是在高中的时候,记得当时在机房鼠标划过网页的时候,鼠标后面跟随着一排特效文字,通过编辑替换文字内容感觉很有意思。最初开始学习是在大一的时候,虽然不是计算机专业的,学习简单的网页制作的时候,HTML标签学习差不多之后就会遇到Javascript,当时学习JS主要用作表单判断,一些简单的页面效果,很少涉及DOM操作。后来工作的时候要写功能性网站(管理系统非CMS类),因为主要使用草根的PHP,Javascript也就混杂的书写,基本上都是直接嵌入到页面当中,直接事件触发,比如 <button onclick="doSth()"> 这样的语法,虽然能够工作但是算不上高效。
因为以前有C语言等基础,学习Javascript也不算吃力,学习的时候才发现Javascript和其他的语言又不太一样,经常出现匿名函数,闭包的概念很难理解。2011年一些jS的框架库还不是很流行,大部分功能都得自己去实现。后来接触到JQuery,发现非常好用,特别是它的选择器功能特别强大,和CSS的语法兼容,操作DOM变得简单轻松。
使用一段jQuery后就开始看下jQuery的源代码,不管怎么说,开源的源码经过很多大神的修改后,变得很精妙,很多地方仍然看不明白,慢慢的看别人的JS写法,从中进行学习。去年从Cu活动中拿到了第六版的《Javascript权威指南》,大体翻阅了下,发现系统的学习和零散的就是不一样,很多没有注意到的细节。现在JS已经能够应用到服务器端,不再是客户端脚本语言这么简单,Node.js也大体看了下,最终还是不太习惯这种编程方式。
学习的话可以自己画下思维导图,把一些JS的知识点串一下,真正生产环境中虽然很多原理不用弄明白,直接调用别人写好的函数或方法(这个class概念也和其他的语言不太一样)就能够完成功能,有时间去研究下实现细节还是挺有意思的。


2、谈谈您的一些JavaScript的高效编程的技巧。
Javascript大多都在用JQuery这个库,所以本身对JS理解不深入,现在是能用的程度,要说技巧的话倒是有些心得:
①首先是要有一个智能的编辑器,个人还是比较习惯Sublime的,智能感应相当不错,而且切换到vim模式无需鼠标即可。
②因为我使用JS主要是用在前端显示,好的调试工具也是很重要的,虽然Firebug相当有名,个人还是习惯用Chrome的调试工具,但是Firefox浏览器的兼容检验还是有必要的,对于IE已无力吐槽,真心不好调试,还好不是做传统意义上的网站,完全可以不去兼容IE浏览器,直接为客户指定浏览器就行,省了不少功夫。
③格式规范,虽然JS本身不强制要求排版,但是漂亮的缩进更容易回头来阅读修改。
④ajax使用适当,单页应用什么的时候只是很新潮的理念,个人感觉ajax还是少用为妙,适当即可。
⑤大型应用或站点一般都提供JS版本的API,比如微博、QQ的,可以考虑使用JS的SDK。


作者: 2009532140    时间: 2014-03-01 12:49
不错不错!
作者: jieforest    时间: 2014-03-01 23:26
版权声明,以上内容为我原创。
作者: seesea2517    时间: 2014-03-04 15:14
JavaScript很强大,却谦虚地自称为Script唉。
作者: howge    时间: 2014-03-04 15:20
  努力学习js中~  
作者: yuyunliuhen    时间: 2014-03-04 18:18
作为刚使用三个多月的新手,使用js还是比较爽的。
作者: pitonas    时间: 2014-03-05 13:29

如何写出高效的现代代码,是我们不断提升道路上一个必经之路。
作者: ddd010    时间: 2014-03-05 16:20
最近好多都是些前端开发的讨论。。。


作者: lsstarboy    时间: 2014-03-07 08:39
自从用了jquery,感觉清爽多了,原来定位个对象那真叫一个痛苦,无数个点和中括号才能找到一个对象。

firebug是标准配备了。平时多练,多分析一些成熟应用,对水平提高很有帮助,我是业余的,偶尔写一段,遇到麻烦的都问题是去网上找相似的应用做参考,从中学到很多东西。
作者: mqiezi    时间: 2014-03-07 08:57
1、谈谈您的JavaScript的学习和提升的经验。
最早接触jS还是在大学时候,那时候网页开发还在asp,jsp,php混战状态。js的调试一直很麻烦,最理想的学习方式就是down些大网站的代码。
后面firebug等一些工具出来后,调试才相对方便了些。那时候js不被看好,甚至有很多公司想推出他的替代品。转折是在ajax的出现,真的改变了很多
人的思考方式。以后jQuery、Node.js等等一系列的js库开始出现。js好像迎来了第二春一样。
2、谈谈您的一些JavaScript的高效编程的技巧。
js语法相对来说轻量简单,如果深刻理解编程自然高效。不过在应用上来说还是要结合一些实际的库。如果想要高效的完成任务,深刻的理解库的目的架构是
必不可少的,磨刀不误砍柴工。
作者: fyyizu    时间: 2014-03-09 18:03
工具如何也要看工人如何看待使用它,优点也是缺点,若类型动态创建都使得它使用更简单方便,但却带来的问题也不少,不经过稳妥的处理根本不知道它是什么能做什么
作者: rogantianwz    时间: 2014-03-09 21:02
本帖最后由 rogantianwz 于 2014-03-09 21:04 编辑

正如LZ所说,javascript里面的精华和糟粕并存,所以要想学好它难度很大。很多人遇到js问题的时候都喜欢上网上去搜,然后找一段贴在自己的代码里,这样做是最不好的,因为你不知道那段代码到底是精华还是糟粕,如果是糟粕的话,恭喜你,你会付出很大代价的。



要想学好,我觉得要学好它第一是不能先去学那些框架,比如jQuery,prototype什么的,要先学习原生的javascript,那些框架只是对原生javascript的一种封装,要知道,淘宝的FE们花几个小时就能写出一套jQuery来。当你学好原生的了之后,各种框架都是手到擒来的事。

学习原生javascript的时候就需要取其精华,去其糟粕,推荐一本书,O'Relly出的《Javascript语言精粹》,很薄的一本书,里面把精粹和糟粕描述的很清楚。

我记得我学习的时候用javascript写了一个网页游戏,类似于塔防。写的过程中遇到了很多问题,但是收益匪浅。有时候单位的网站前端出bug了,找不着前端工程师的时候,我看看代码,三下五除二就给改了


说得有点儿乱七八糟

补充一个:调试的话,用chrome,很好用。
作者: starwing83    时间: 2014-03-10 00:30
回复 18# rogantianwz


    这个观点比较常见,但是其实里面(可能)会有某些误解。

是这样的,JS实际上是Mozilla搞得,然后被标准化为ECMAScript,这也是浏览器里面用的。而Mozilla的JS本身有很多超出ECMAScript的特性。现在的ECMAScript已经是第五版了。其实已经很不错了,说实在的还是有坑,但是真的好很多了。

因为以前的一些糊涂账,所以很多人会觉得JS怎么怎么样,其实你只要当他是一门现代而又方便的脚本语言即可。Chrome用一段时间,你肯定会很喜欢JS的。

千万别从微软的JScript开始学习JS(这完全不是同一门语言!!)我之前就被坑过。

反正,分清楚JavaScript 1.8、ECMAScript V5和JScirpt的区别就好~
作者: rogantianwz    时间: 2014-03-10 09:54
本帖最后由 rogantianwz 于 2014-03-10 09:55 编辑

回复 19# starwing83


    哈哈,我曾近也差点儿让JScript坑了!
我最近也想搞搞node,有没有推荐的书或者论坛什么的没?
作者: hbsycw    时间: 2014-03-10 10:18
学会编写JavaScript程序容易,但要成为专家却实属不易!  
作者: starwing83    时间: 2014-03-10 11:37
回复 20# rogantianwz


    node.js的话,从express开始搞就行(expressjs.com),推荐可以看看数据库ORM(sequelizejs.com),另外就是学习一点Promise,比如说bluebird或者Q(推荐bluebird)。最后,可以看看libuv库(github.com/joyent/libuv)这个是node的底层,纯C。

国内有个cnode社区很不错。

反正,多看开源项目,多看论坛博客就OK,书的话我也不知道……这个还是比较新的,没发现有什么比较好的书~
作者: asd3418182    时间: 2014-03-10 13:07
坐等JS大婶出现啊~
作者: duanfuyou    时间: 2014-03-12 11:07
本帖最后由 duanfuyou 于 2014-03-15 22:27 编辑

挺喜欢JS的,给我的感觉就是:
1、入门门槛较低,我就是在对编程一无所知的时候通过它入行的
2、自由(相对而言),这个特点的话就造就了很多良莠不齐的代码,但是优秀的JS code有时候就感觉是一种艺术。

现在回答下问题:
Q:谈谈您的JavaScript的学习和提升的经验。
A:我的JS学习都是通过不断的写不断的读中进行的,多看看别人是怎么去写JS代码,怎么去实现一个功能,怎么去组织一个模块的,好的就汲取,不好的就当做是给自己的提醒。

Q:谈谈您的一些JavaScript的高效编程的技巧。
A:我也还在学习阶段,只能说一下平时我都是怎么写的吧。1、在开始写之前,都先把代码模块划分好,各个模块的API也尽量考虑到;2、为模块分配命名空间;3、在代码块内使用严格模式;4、如果工期不是赶得不行的话,最好自己要求自己的去写高质量的JS实现,这样对自己的提高是很有帮助的;5、自己或同事写的代码在一段时间以后都可以看看,会发现很多问题和可以探究的地方,这样的话映像比较深刻;……

回答的不一定正确,我是这样做的,希望大家指正,一起进步。

// 想到一点补充一点
要想编写高效优雅的JAVASCRIPT代码,我想还是必须清楚这门语言的细节,也可以叫做编写他们的小技巧或者注意事项。例如:
1、严格模式"use strict"的使用
2、变量作用域,function、var的装载时间(这里描述可能不正确)
作者: OwnWaterloo    时间: 2014-03-19 03:53
1、谈谈您的JavaScript的学习和提升的经验。

因为主要工作重心不在(无论是客户端还是服务端的)JS上于是没有太多经验可以分享。
最开始显然走过一段只为IE写的黑暗时期。。。后来随着IE的份额不断下降以及对语言规范与语言实现的区别有了解之后才发现它是最坑的一个。。。
为了系统的学习也找过一些书籍。最终选定先把《JavaScript权威指南》看完再说。但直到现在都没能看完。。。

2、谈谈您的一些JavaScript的高效编程的技巧。

工具方面前面已经提得差不多了。Chrome或Firefox自带的console或者Firebug等等。
这里提一个比较偏门的东西。。。 https://github.com/bard/mozrepl
简单的说它可以让Firefox监听一个端口,然后通过tcp发送js在Firefox里执行。于是可以编程地操作Firefox。

至于编辑器,在投靠Emacs之前用过的里面印象最深的是1st javascript editor。图标好像是个龙。
有免费版本。。。但会定期地将智能提示的那个check box给去掉。。。不过可以写个程序(或者直接用游戏修改器)不断地将内存中的一个值设置为1就可以了。。。
Emacs的话js2和js3的静态语法分析挺好的。基本不会误报。
作者: OwnWaterloo    时间: 2014-03-19 04:36
回复 5# starwing83

关于Q(以及JS里许多类似物)的设计,我感觉有一个问题。
比如就以Q的readme中的例子来说:

  1. step1(function (value1) {
  2.     step2(value1, function(value2) {
  3.         step3(value2, function(value3) {
  4.             step4(value3, function(value4) {
  5.                 // Do something with value4
  6.             });
  7.         });
  8.     });
  9. });
复制代码
它忽略了一个重点。
注释部分不仅仅可以Do something with value4之外其实还可以Do something with value1,value2,value3。
并且还可以访问前面几个匿名函数中的局部变量。 只是例子里面被简化了。
并step4也不一定就是使用value3作为实际参数。 它可以使用value2, value1甚至是它们的组合。

而作为对比的例子里面就失去了这个能力:

  1. Q.fcall(promisedStep1)
  2. .then(promisedStep2)
  3. .then(promisedStep3)
  4. .then(promisedStep4)
  5. .then(function (value4) {
  6.     // Do something with value4
  7. })
  8. .catch(function (error) {
  9.     // Handle any error from all above steps
  10. })
  11. .done();
复制代码
每个promisedStep的作用域都是分离的。


因为重心不在JS上所以没有什么实际例子。实际使用中类似管道的结构是大多数? 当遇到不是的时候就必须将前面需要的信息包裹在promise(?)里继续传递下去?
如果promisedStepI是打算被复用的,在实现的时候怎么能预先知道究竟应该传递多少?
而如果整个Q.fcall.then.then...done就是one shot的, 那它与generator或者让那人肉CPS能自动化的方案相比的优势在哪里?
移植性? 可以在不同浏览器对JS高级特性支持参差不齐的情况下也都能够实现? 不依赖某个具体的JS实现(V8,SpiderMonkey,Node,Rhino...)? 也不需要一个以JS为目标的编译器?
作者: send_linux    时间: 2014-03-19 10:39
starwing83 发表于 2014-03-10 11:37
回复 20# rogantianwz


欢迎更多分享~
作者: starwing83    时间: 2014-03-23 15:16
回复 26# OwnWaterloo


    是的,你提的的确是开发中会遇到而且会比较头疼的一些问题。

简单说来,promise相对cps——特别是经过coroutine/node-fiber包装的cps来说,其实没有什么太大的优势。真正的优势是,这个过程是“标准的”,“清晰的”,换句话说,出问题了你很容易知道是哪儿有问题。而代价就是,你必须得多写一点东西(比如说每个then的function(...) { ... })。

我现在用的是bluebird,不过这不重要。你提到的问题,在promise里面的解决方法是这样的——他不只是只有一个then,还有其他很多的方法可以用,比如可以传递多个参数的spread,比如说十分方便的map(一次处理超多的promise——注意这里是并行的,换句话说就是个poll,这一点在cps或者是coroutine里面几乎是做不到的)。解决你说的问题的一个办法是tap——即忽略本次的返回值,下个then还是得到上次的返回值——这用来做“不影响上下文的插件处理”。另外的办法就是两个了:
1. 外面用var,里面回调赋值
2. 嵌套的then,但是只是在需要某些参数的时候嵌套。

其实还有一个办法,就是把v1v2v3v4这些一并传下去(只需要传递需要的参数即可)。

其实promise并不会用Q的example那么用,几乎每一个then都是一个匿名函数——说白了,除了能节省嵌套的缩进以外,目前我还没用它做太多事。

另外,可以对promise进行修改——我的做法是往prototype里面插入一些函数,比如我插入了一个这样的函数:

  1. Promise.prototype.sendResponse = function(res) {
  2.   return this.done(function(data) { ... }, function(err) { ... });
  3. }
复制代码
这样,每次处理完以后加一行.sendResponse,处理后的请求就自动发送给客户端了。十分方便。

说白了,这东西就是一个尽量不改变语法,尽量保持一致性的前提下做的一个管道过滤系统。实际开发中的确很好用。

最后说一下,node.js的ORM——sequelize(http://sequelizejs.com)已经内建了promise了,用的就是bluebird。我现在已经用这玩意写了很多代码了。稳定性和特性都不错(有一些很重要的功能没有实现,但是还是可以自行hack,所以勉强不影响使用吧——一个最重要的hack是多表查询加分页,这个不支持太蛋疼了,不过应该已经提交到下次版本的特性列表了)。去年要是用了这玩意儿那写网站的速度至少快一倍啊。所以现在推荐使用这个库,而不是node-orm2了。

另外,最近找了找Java下的Promise库——因为最近发现后台前端都TMD要我自己写= =发现Java实在是残疾,完全没法用Promise的——一个没有lambda函数的东西怎么用Promise = =只能够等Java7了= =

Java方面的网络前端开发我也要说几句,Java开发,唯一一个能“元编程”的就是所谓的记号(Annotate)了,基于记号可以大幅缩减Java代码量,把注意力集中在没有OO……咳咳,业务逻辑上,这里有几个非常非常好用的基于记号元编程的库:
1. retrofit:Square开源出来的REST客户端的自动生成接口,只需要写Java Interface就自动产生能够同步或者异步发送REST请求的对象,十分方便。
2. ActiveAndroid:Android的ORM库,只需要继承Model类并且写记号就可以自动支持写入/读出/查询sqlite,相对sequelize功能弱的一逼,但是好歹还能用,而且基本用法也很方便。
3. AndroidAnnotations:超级好用的基于记号的快速开发库,支持Android上面各种控件和代码的直接绑定,支持Adapter绑定,支持Bean绑定,支持REST等等等等,简直就是解放劳(daima)动(min)力(gong)的必杀技啊- -

有了这三个基本上界面+数据库+网络请求就OK了,剩下的就是一些比较好用的控件了:
1. 第一名当然是slidingmenu,不解释= =
2. 要用slidingmenu当然得有actionbarsherlock了
3. 然后就是图片自动下载、缓存、显示的库,同样的square开源的picasso,非常好用,接口独特。

另外可以看一下的是谷歌的gson,挺有意思的。

反正一句话:Java没有元编程完全不是人写的东西啊!!!!(某安卓苦逼默默飘过……)


作者: OwnWaterloo    时间: 2014-03-23 19:03
回复 28# starwing83

信息量太大,一个一个回。。。 今天还不一定回得完。。。

先说最悲伤的Java。 java7出了很久了。lambda要等java8。还得看究竟是怎么实现的。
如果只是语法糖,最终依然是产生一个匿名class还好。,而如果是通过增加新的字节码指令实现的话, 发布时就需要目标机器上也有对应的vm才行。
增加字节码指令的问题好像已经在jvm上的动态语言里出现了,比如invokedynamic。 不过按java语言的尿性来看,估计会按前一种方法实现。

作者: OwnWaterloo    时间: 2014-03-23 19:23
本帖最后由 OwnWaterloo 于 2014-03-23 19:25 编辑

回复 28# starwing83

关于ORM。 O和R怎么能M得起来? 我了个去。  目前感觉它只是 O -> M而不是 O <-> M。
用了ORM, 会不会出现需要绕过它依然需要直接写sql的情况?即使排除下面这些情况:
1. 某些情况为了性能而绕过。
2. 为了一些one shot的任务而绕过。 并不属于用户日常使用的功能。 比如,假设因为物价飞涨CU打算将所有用户的积分上调10%。 这个是单条sql语句就可以完成的事情。
等等。。。
总之,会不会为了一些很常见的功能而绕过ORM直接用sql?

如果是的话,那ORM的意义就是让80%的情况可以轻松一点, 剩下20%自己看着办?
作者: OwnWaterloo    时间: 2014-03-23 20:39
回复 28# starwing83

既然说到ORM,就继续说R。。。 O就不想说了。。。
谦虚点说是我理解不了R的深意。 不客气点说。。。 这种非人道的东西是怎么占据统治地位这么多年的?

比如许多optional field。
有无数的relational evangelist写了无数的反对EAV的文章。
但optional field的需要是客观存在的。
There’s an old saying in the relational database world: on a long enough timeline, all fields become optional.
- Seven Databases in Seven Weeks

反对了半天也不给个更好的解决方案。

比如evangelist说:用foreign key维持data integrity!
听上去很美好,但前段时间ZR的一个问题彻底把我问傻了。。。
比如可以对游戏、相册、用户、文章等等进行评论。而所有的commet都有共同的属性。于是问题就来了。
如果要按正统的、王道的使用方法,就需要game_comment,album_comment,user_comment,article_comment。。。否则没法建立FK。
但这种使用方法的弊端 —— 比如增加或删除comment本身的属性时 —— 没人提。

我很怀疑大量与R有关的文章的作者到底用不用R做实际开发??????

PostgreSQL本身很美好。 有array/intarray, hstore/json/xml, custome type/table inheritance来作为one to many, optional fields, 以及后续那个都不知道该怎么命名的该死的问题的另一种解决方式。
但都要被jdbc卡住。。。  而且jdbc(或者java without jni)还不支持unix domain socket。。。  有精力我都想绕过jdbc了。。。
或者逼急了直接存Clojure(语言强相关无所谓时)或JSON(需要语言无关时)数据进去。。。

感觉R是有一些brilliant idea在里面的。 但从它的发明、发展到现在已经有一些东西失传了 —— 尤其是R适合做什么。
而requirement is always changing在7,80年代可能根本就不是一个requirement。 类似的也许还有modular, reusable, composable。

比如authentication。它只需要account里的login与password(暂时把password究竟应该怎么处理抛开)信息。如果需要reset password,可能还需要email。
但根据具体的系统需要,注册的时候可能还需要填写其他一些信息avatar,age,invite_code...。

且不说具体实现时是分开的table还是monolithic table,问题在于怎么把sign up得到的信息分派到不同组件里去。
就只对authentication这个具体情况来说, 让sign up时只填写login与password, 其他信息等注册完后再慢慢填写可能恰好是一个比较好的方案。 让用户注册时少想,少填一些乱七八糟的东西。
但其他案例不一定都可以这样。

我现的打算之一是让不同组件只提需求(不仅仅是查询还包括修改的)而不实际进行操作,然后在处理request的时候将它们组合起来一并处理, 将结果分派下去。
在request的时候就把transaction打开,所有组件在查询时可以获得一致的信息, 在某个组建修改失败时可以整体回滚。
至于怎么实现或者能不能实现还不清楚。

打算之二是直接Restful -> Relational,然后再上面加一个权限控制。至于另一个方向。。。 我越是深入的看SQL的东西越是觉得双向映射不可能。

一陷web深似海,从此节操是路人。。。
作者: OwnWaterloo    时间: 2014-03-23 20:55
回复 28# starwing83

继续说lightweight? CPS transform。
有这样一个库,或者可能说它是工具更恰当一些: A compiler for JavaScript asynchronous Continuation-Passing Style transformation
说它轻量是因为写的依然是Javascript,只是添加了几个伪关键字。 并且翻译后的结果依然是可读的。 而不像{Clojure,Coffee,Typed,...}Script那样写的完全是另一种语言。
没有实际使用过,因为。。。 你知道目前前端是谁在写。。。

BYW: CoffeeScript的wiki里有一条List of languages that compile to JS
我突然对它有好感了。。。 倒不是说对CoffeeScript本身, 而是它的这种态度。
不是自己闷头搞一堆, 然后各种宣传: 哦, 看我这XXX, 多美妙啊。 而是让自己以及顾客货比三家。。。
作者: starwing83    时间: 2014-03-23 21:11
回复 31# OwnWaterloo


    太好了,我今天上午还在犹豫是不是继续回个贴……因为有东西没说完……而且刚刚好就是ORM方面的经验……对了,等会儿也顺带说一下关于REST->ORM的经验,因为最近客户端正在这么用。

是这样的,如果我没理解错,ORM是指Object Relation Model,Object指的是你程序里面能操纵的数据结构(表、对象、结构体、XML——这SB东西有人用?),R貌似是有两层意思的,第一,是指这玩意儿最终映射的是关心型数据库——至于这东西(包括SQL)有多坑爹你也知道的。所以,这里的关系其实还有另外一个含义——把所有的Model之间的关系,很简单的表示成为下面的几种:
  - 1对1
  - 1对多
  - 多对1
  - 多对多

分别有不同的实现方法,我们先说接口,再说实现。

接口上,sequelize和ORM2是不一样的,先说一样的部分,一样的部分是,都有两个核心的接口:hasOne和hasMany,至于是什么意思你肯定懂的。不一样的部分在于,sequelize有一个很特殊的接口叫做belongTo,而ORM2则是采取在options里面放reversed的方法来指定反向的连接。现在倾向于认为sequelize的方法更好——初期来看可能觉得反直觉——而且我到现在为止都觉得这肯定是个Bug,不是库的就是作者脑子的bug……但是不管怎么说,将“我有啥”和“啥属于谁”分离,的确是正确的做法。

至于这些接口如何和上面四种类型对应,那你自己都能脑补出来,我就不细说了。

然后就是实现,实现无非就是外键,而外键又有两种方式:嵌入表,或者是采用所谓的“连接表”(只有外键的表)。后者在sequelize里面本质上还是一个Model,所以其实就一种方式(其实RDB也就这种好吧?)

然后说说你提到的什么数据冗余性的事儿——我才不管呢,能工作就成,管你optional不optional,反正现在大部分的域都是optional,也没觉得有多不好,反正硬盘便宜。

在这种情况下,我觉得ORM和R是没什么太大关系的,因为这套东西照样也可以用NoSQL的东西实现。我觉得这种关系分类(上面提到的四种,三个接口——hasOne,hasMany,belongsTo)就完全可以应付所有的需求了——换句话说,我觉得就已经完备了。完备神马的你懂的。

最后说写SQL的问题,说句实话,有的,但是主要原因其实是库的接口渣(明明对着一个Model可以进行的操作,对着其他model(比如连接表model)就失效),其实如果你对这个关系有足够多的理解,你会发现所有的查询还是可以用接口写出来,只不过是效率高低的问题,这里的”足够理解”是指:
1. R其实就是外键
2. 连接表也是表
3. 接口的最大支持什么

ORM的查询接口的最大支持,就可以支持对任意信息的查询。举个例子,假设有个接口叫findAndCountAll,能一边查一边数。OK,那就意味着所有的Model(包括【看上去】不支持的)都支持了这个操作,只是你会不会写罢了。

退一万步说,如果你真的想写SQL也不会有人阻止你,如果你想写SQL又不想SQL的麻烦,还有很多侵入式的DSL可以给你用的。

这里的核心思想是,ORM最重要的点是把关系给限制到四种了(其实这四种可以表达所有的关系了),这个“关系限制”瞬间让我想到了“操作限制”——没错,就是REST。

我前段时间找了很多REST的文章看看——主要目的是看看这货是不是又是一个卖名词的,后来发现还是有点儿货的。什么状态转换都是装逼的,其实就是很简单的一件事儿:
1. 所有东西都有地址
2. 所有东西都支持共同的操作

你看,像不像被完全阉割版本的OO?其实如果按照OO的标准消息模型来看,OO这玩意儿本身就应该是没状态的,但是问题就是这里面完全没保证——现在REST保证了这点。

说到这里问个问题:我在REST里面传一个token,然后服务器保有一份状态,然后REST的操作就改变这份状态,这不算传输过程中传递状态吧?其实我觉得传输就是传输,传输哪里会有状态,不明白,解释一下哈……

反正现在服务器最开始的时候完全没想着REST,只是觉得“怎么漂亮怎么来”,这边的安卓小哥之前还抱怨过说用户id嵌入请求路径怎么怎么个难用。结果我自己跑去写Android的时候就发现了retrofit了,然后终于为了这货下决心看了REST(理解就是上面的东西),发现这完全值得啊= =

然后就是这么一个结构,服务器Java定义了一堆的Beans(就是结构体啦,Javaer神马的,你懂的),然后这些Beans继承自ActiveAndroid的Model,一下子瞬间就从网络打通到了数据库了——通过retrofit,数据可以通过REST获得,然后通过gson转换成Beans,然后Beans继承自Model——这就意味着可以直接save,bingo!

恩,反正我的经验就是这样的——REST传输的representative本身,是可以转换成Model的,这样就直接可以搭上ORM的车了,中间可能需要一些处理——很显然,这些处理是类似“管道过滤器的”——哈,Promise就用上了!

目前服务器和客户端就都是这个架构了,数据从客户端->Model->REST->Model->数据库,写起来非常爽。

额,我还有什么忘了回的么?


作者: starwing83    时间: 2014-03-23 21:13
回复 32# OwnWaterloo


    恩,这种感觉的确很好,选bluebird也是因为这个,github页直接就有和其他promise的比较,瞬间就好感了……
作者: OwnWaterloo    时间: 2014-03-23 22:06
本帖最后由 OwnWaterloo 于 2014-03-23 22:30 编辑

回复 28# starwing83

关于poll。

1. 假设需要两个异步操作。但后一个的结果是依赖与前一个的。比如先通过用户名查询id,然后通过id查询该用户的其他信息。
这种情况下无论是CPS还是Promise都没有poll或并发的需求?

2. 如果两个异步操作的结果是没有依赖的。比如同时搜索CSP的文章与搜索Communicating Sequential Processes的图片,然后展示它们。
哪个结构先到就先展示哪个。有并发需求但依然没有poll的需求?
CPS需要传递两次render —— 如果render不是一个简单的函数就需要写两次, 你说的是这个问题?
而Promise的那个map, 呃, 完成的工作是什么?

  1. (a -> b) -> [Promise a] -> [Promise b]?
  2. (a -> Promise b) -> [a] -> [Promise b]?
  3. (a -> b) -> [Promise a] -> Promise [b]?
复制代码
还是别的什么?

3.再假设,同样是发起两个搜索, 但增加一个超时。 超时后就给用户报告一个错误并且忽略后续可能得到的搜索结果。
这种情况有并发且有poll的需求。

看这个: Google Search 2.1

  1.     c := make(chan Result)
  2.     go func() { c <- Web(query) } ()
  3.     go func() { c <- Image(query) } ()
  4.     go func() { c <- Video(query) } ()

  5.     timeout := time.After(80 * time.Millisecond)
  6.     for i := 0; i < 3; i++ {
  7.         select {
  8.         case result := <-c:
  9.             results = append(results, result)
  10.         case <-timeout:
  11.             fmt.Println("timed out")
  12.             return
  13.         }
  14.     }
  15.     return
复制代码
依然发起搜索操作。得到的结果往一个channel里塞。然后发起一个timeout操作,得到的结果往另一个channel里塞。最后select(poll)两个channel。
搜索依然是并发的。先到先收集。并且在timeout到了之后就放弃收集结果。
同样可以解决2里面render需要写两处的问题。

只是怎么在JS里实现还不清楚。

而如果用promise该怎么处理这种需要coordination的操作?
感觉promise就是一个one element的定长channel。 还不一定能找到poll多个promise的方法。。。
作者: OwnWaterloo    时间: 2014-03-23 22:33
回复 33# starwing83

纳尼? ORM的M是model不是mapping?
作者: OwnWaterloo    时间: 2014-03-23 23:23
回复 28# starwing83

关于剩下的一些东西。

>> 其实promise并不会用Q的example那么用,几乎每一个then都是一个匿名函数——说白了,除了能节省嵌套的缩进以外,目前我还没用它做太多事。
也就是前面提到的这种情况?
>> 而如果整个Q.fcall.then.then...done就是one shot的, 那它与generator或者让那人肉CPS能自动化的方案相比的优势在哪里?
而非:
>> 如果promisedStepI是打算被复用的,在实现的时候怎么能预先知道究竟应该传递多少?

那确实可以用下面这些办法:
>> 另外的办法就是两个了:
>> 1. 外面用var,里面回调赋值
>> 2. 嵌套的then,但是只是在需要某些参数的时候嵌套。
>> 其实还有一个办法,就是把v1v2v3v4这些一并传下去(只需要传递需要的参数即可)。

>> 说白了,这东西就是一个尽量不改变语法,尽量保持一致性的前提下做的一个管道过滤系统。实际开发中的确很好用。
如果promise的目的是不改变语法,能在现有的各种JS基础上容易实现的话。。。 研究的动力小一些了。。。
我还是更倾向于转换为CPS的方式。。。 无论是轻量的(JS+一些特殊关键字)还是重量的(干脆就是另一门语言)。。。 不在乎写的还是不是真正的JS。。。 我已经把JS看作web上的asm或C了。。。

一方面是因为JS在这方面太混乱了。。。
ES6的promise貌似现在都没有定论,而其他的类似的库太多太多。。。不知道如果ES6早一点会不会改善这个情况。

并且另一方面,因为JS这方面现在很混乱,就不敢轻易地学甚至尝试。
对于这个
>> 解决你说的问题的一个办法是tap——即忽略本次的返回值,下个then还是得到上次的返回值——这用来做“不影响上下文的插件处理”。
也是一种特殊的组合的方式。
得去找选的那个promise库里面有没有。。。 如果没有又该怎么办? 如果不能像tap这样在库外可以实现时该怎么办?
至少得确定有一个绕过抽象的方式吧。。。

而且需要的转换并不多。。。 比如do

  1. do x1 <- action1
  2.    x2 <- action2
  3.    action3 x1 x2
复制代码

  1. bind(action1,function (x1) {
  2.   bind(action2,function (x2) {
  3.     action3(x1,x2);
  4.   });
  5. });
复制代码
很多其他语言求而不得的语法糖却被Considered Harmful。。。
作者: OwnWaterloo    时间: 2014-03-23 23:30
starwing83 发表于 2014-03-23 21:13
回复 32# OwnWaterloo

    恩,这种感觉的确很好,选bluebird也是因为这个,github页直接就有和其他promise的比较,瞬间就好感了……


wiki home,wiki pages里貌似都没有。
难道是首页里features列出的那些?
Promises A+ 2.0.2
Cancellation
Progression
Synchronous inspection
.bind
Complete parallel for C# 5.0 async and await
Collection methods such as All, any, some, settle, map, filter, reduce, spread, join, race...
Practical debugging solutions such as unhandled rejection reporting, typed catches, catching only what you expect and very long, relevant stack tra

这些里面提到的都支持?比如 A+, async/await, 等等?
作者: OwnWaterloo    时间: 2014-03-24 00:40
starwing83 发表于 2014-03-23 21:11
回复 31# OwnWaterloo

说到这里问个问题:我在REST里面传一个token,然后服务器保有一份状态,然后REST的操作就改变这份状态,这不算传输过程中传递状态吧?其实我觉得传输就是传输,传输哪里会有状态,不明白,解释一下哈……

没懂。。。

感觉S更倾向与application state:
* 请求的是那个uri
* 请求的方法
* 带有的query-string
* body
* Headers(content-type,cookies...)
就是完成该request所需要的所有信息。

而T部分,有一个与RAII不相上下的黑话:Hypermedia as the Engine of Application State。。。
用HATEOAS来控制引导客户端转变(T)application state。

http/html里的Hypermedia就是在返回的Representation里包含
*

  1. <a href="http://DOMAIN/hello&x=12">click me</a>
复制代码
用户点击后application state就变成了 method=get,  uri=hello, query=x=12。。。
*

  1. <form method="post" action="https://DOMAIN/world">
  2.   x: <input name="x" value="12" />
  3.   <input type="submit" value="post" />
  4. </form>
复制代码
用户提交后,application state就变成了 method=post, uri=world, scheme=https。Header里, Host=DOMAIN, Content-Type=application/x-www-form-urlencoded, 而body里有 x=12。。。
* 如果返回的status=401,并且header里带上WWW-Authenticate就会会引导浏览器向用户索要用户名与密码, 并且继续产生请求。
之后application state的header里就会多一个Authentication。

诸如此类。。。

不清楚token是打算做什么。。。 不过可以顺带吐槽一下REST。。。 尤其是关于登入的问题。。。
It doesn’t really matter which authentication mechanism I choose since I’m not actually implementing this service, but let’s say I go with the simplest choice: HTTP Basic authentication.
- RESTful Web Services

我去, 一句话就把难题给绕过去了。。。
作为Web Services,其实Basic authentication+HTTPS也没什么问题。
而如果是website, user-agent是浏览器。。。 浏览器收到401后弹出的那个框是没法改的。。。  要让浏览器停止发送Authentication header(logout)是很困难的。。。
怎么实现login/logout才地道? 至今没找到答案。。。 和SQL一样, 一堆不要怎么不要怎么不要怎么, 但就是不给出解决问题的方式。。。 自己悟去。。。
作者: OwnWaterloo    时间: 2014-03-24 01:12
回复 33# starwing83

如果将M理解为Modeling而不是Mapping,感觉问题突然就变容易了。。。
Modeling好了之后(有那些,有那些关系,支持什么操作)只要能实现出来就完备了。
Modeling是主体而存储只是实现方式。至于实现是SQL还是NoSQL都不管。甚至那个R都可以淡化。。。 直接是OM。

而如果是Object-relational mapping呢?比如前面说的那个:

  1. UPDATE 账户 SET 积分 = 积分 * 1.10;
复制代码
或者CU鼓励新用户,只为积分小于1212的用户加分:

  1. UPDATE 账户 SET 积分 = 积分 * 1.10 WHERE 积分 < 1212;
复制代码
这该怎么映射回Object里去?选出所有(或者积分小于1212的)用户,增加10%的积分,然后又挨个存回去?
有ORM库能直接映射到上面两种UPDATE语句么?

relational database本身描述的是集合。。。 SELECT,INSERT,UPDATE,DELETE操作的也是集合而不是独立的Object。。。
即使有上面说的库,它还叫ORM么。。。

而且SQL还有一些很复杂的语句。。。
子查询,查询的交、并、差集,将查询的结果用来INSERT/UPDATE/DELETE,Common Table Expressions...
又该怎么映射回去?

相比映射回去的方法,更关心的是这种方法到底是否存在。。。我很怀疑它不存在。。。
如果真的不存在也不用纠结了。只要不是让简单的问题更简单让复杂的问题更复杂,能满足:
* 快速解决80%的问题
* 为剩下的20%留有后路
就行了。

作者: OwnWaterloo    时间: 2014-03-24 01:28
starwing83 发表于 2014-03-23 21:11
然后说说你提到的什么数据冗余性的事儿——我才不管呢,能工作就成,管你optional不optional,反正现在大部分的域都是optional,也没觉得有多不好,反正硬盘便宜。

比如。。。 用户有联系邮箱、头像图片链接、性别、生日等等,它们都是可选填的。
你的意思是说,它们都在一个表里,没填的NULL就行了? 那估计是会被SQL evangelist批评的

前面说的EAV:

  1. id user_id attribute value
  2. 1  1       contact   CU@gmail.com
  3. 2  1       avatar    http://cu.img168.net/static/image/common//folder_new.gif
  4. ...
复制代码
也是时不时就被选出来作为反面例子示众。。。

user_contact, user_avatar, user_gender, user_birthday 分别一张表才是正统,其他都是邪门歪道!!!

作者: starwing83    时间: 2014-03-24 02:16
回复 39# OwnWaterloo


    虽然大家都说mapping,但是很显然mapping是不可能的——SQL的操作对象是集合,ORM都映射到独立Object了,怎么个集合法儿?

要直接操纵集合,现代的ORM应该还不可能——sequelize有BlukCreate,支持集合创建,还有集合删除,至于集合修改嘛= =

所以,M当作Model其实更简单——就是独立进行的操作,如果你要对一堆东西进行操作,烦请SQL(或者上面提到了的,SQL的某种DSL方言= =)

反正总的来说,你觉得纠结还不是因为SQL功能太强了么= =你要是用不到那些超级功能那关那些干嘛= =反正我现在基本上只是处理单个用户请求——服务器嘛——至于多个用户要怎么怎么样,我目前能想到的唯一办法就是自己写脚本——是SQL脚本还是SQL-DSL脚本有区别么= =

恩,所以你说对了,的确就是只解决80%问题的——我说的完备性是在操作单个元素与其对应关系上,集合和集合的关系嘛,这个有时间可以做做理论研究的说~
作者: starwing83    时间: 2014-03-24 02:19
回复 41# OwnWaterloo


    对,就是没有就是NULL

批评就批评,管我鸟事,又不是客户,又没给钱,随便批评。哪天要是捐我一台服务器什么的我再去改改……
作者: starwing83    时间: 2014-03-24 02:28
bluebird不是提到了Q么= =别的好像还有,不记得了,我就是在README.md看到的,其他的不知道诶。 。

反正说是比Q好,而我之前用着Q,再然后我的ORM用的它,我不用它用啥= =

我想想上面还有啥问题= =哦,Promise标准是吧?这个是有的,Promise/A+(我不知道是不是啊,但是Q和bluebird都这么说,感觉是标准吧?)反正都实现了这个接口了。其他的扩展什么的多得要命,没用到的就不看,我现在很没节操的。

并行那个,map是等待所有完成然后返回,没有超时,只要有一个失败就全部失败,而且没有rollback,不管那么多了,够用就成= =

Q貌似还有其他的各种选项,比如any(所有操作完成返回,不管失败,没有超时),感觉做超时应该也成,就是没人做罢了

反正还是那句话,也没打算研究,有什么用什么,一切以节约代码为第一优先级,以后出问题——那不是还有以后嘛= =

token就是用户标记,类似cookie的东西,但是不存信息,就是代表你是谁的。至于操作的状态都存数据库了,数据库说可以就可以= =反正REST是没状态的就是了= =客户端这边也维护一套状态,免得知道不行还发请求,大体就是这样了,不知道这算不算REST,反正这玩意儿听起来挺玄乎的,唯一靠谱的就是我说的那两点:
1. 所有资源都有地址
2. 所有资源都最多支持四种操作

如果算上错误也算是操作,那么就是所有资源都支持四种操作(最多出错嘛)

那么按照你说的,我是不是可以这么理解S,所谓状态就是:资源的表示能提供访问更多操作的可能性?

也就是说,我不知道有多少用户,我GET /user,得到一个列表,这样我就能对我获取的用户进行操作了——因此状态(就有机会)改变了?不然我就只知道这一个接口,谈不上能改变状态。

这么说,S其实说的不是状态,而是【获取可以进行其他操作的必要信息】,是这个理解么


作者: OwnWaterloo    时间: 2014-03-24 02:37
starwing83 发表于 2014-03-23 21:11
我前段时间找了很多REST的文章看看——主要目的是看看这货是不是又是一个卖名词的,后来发现还是有点儿货的。什么状态转换都是装逼的,其实就是很简单的一件事儿:
1. 所有东西都有地址
2. 所有东西都支持共同的操作


前面把这看掉了。 状态转换其实是REST(in Roy Fielding's mind)的要点之一。
有一个给REST评级的网站。 链接忘了。。。  具体的评级顺序也忘了。。。
单一的end point(/index.php, /api.aspx)是最初级的。 被成为rpc 为不同的资源命名可以继续升级。
用http verb(DELETE /user/1)而不是action code(GET /user?action=delete&id=12) — 被称为rpc-hybrid —又可以升。

在REST被炒起来后,这两者就开始逐步被接受了。后续还有继续升级的地方,就是前面提到的HATEOAS,还有code-on-demand。

rpc的问题是不同的rpc提供者使用的协议不同。 影响互通性。 这是官方回答。
居心叵测地推敲。。。 也许还有另一些原因在里面。。。
http是被设计为应用层协议的, 而各种rpc将它当作传输层协议使用, 于是设计者们不高兴了, 就站出来说: http不是你们这样用的。


相比是rpc还是uri, 感觉http verb的理由更明显一些。 它直接使用http本身的语义。
GET,HEAD,OPTIONS是safe的, 是可以随意发送的, 甚至在用户没有明确操作的情况下。(Chrome在地址栏里补全的时候就会发)
PUT,DELETE是unsafe且idempotent的。 虽然不能随意发, 但确实想发的情况下就可以重复发。。。 尤其是在网络这种不可靠的环境下。 失败后简单retry即可。
最复杂的,不安全且不幂等的就留给POST了。
已经发现一些http客户端(浏览器,浏览器插件,http库)会在用户没有确认的情况下发送GET或者连续发送多个GET然后取一个返回。
GET /user?action=delete&id=12 就完蛋了。
如果http客户端敢在用户没有明确指出的情况下发送unsafe methods,应该可以算bug。
而如果在用户明确指出的情况下,没留意到idempotent methods会重复发送的情况。


而HATEOAS和code-on-demand想解决的问题是server与client解偶。
server满足了addressability和uniform interface之后, client应该从哪里知道这些addressable uri在哪? 至于支持那些操作倒是可以用OPTIONS。
从一个文档页面里找? 如果uri有过改动? 文档是否有同步的更新? 以及之前的客户端的代码又该怎么更新?
而HATEOAS是想让REST提供者满足Connectedness,让client可以从REST app的根开始寻找REST提供了那些resources。
在Hypermedia不足以满足需要的时候还可以产生代码让client去执行。。。

REST APIs must be hypertext-driven
A REST API should be entered with no prior knowledge beyond the initial URI (bookmark) and set of standardized media types that are appropriate for the intended audience (i.e., expected to be understood by any client that might use the API). From that point on, all application state transitions must be driven by client selection of server-provided choices that are present in the received representations or implied by the user’s manipulation of those representations. The transitions may be determined (or limited by) the client’s knowledge of media types and resource communication mechanisms, both of which may be improved on-the-fly (e.g., code-on-demand). [Failure here implies that out-of-band information is driving interaction instead of hypertext.]


回过头来想想。。。  满足这些条件的不就是一个website么不就是一个website么不就是一个website么。。。
感觉restful就是从http/html/website里总结出了一些东西,指导如何写一个好的网站。 然后摇身一变就挂了个service的名。。。

上面这些条件对一个human consumer来说是很好的。 尤其是最后的HATEOAS与code-on-demand。
人类可以通过搜索或者别人的推荐从一个url直接跳转到网站的某个资源, 也可以从网站的根开始一步一步通过链接、表单、AJAX等等寻找他想要的资源。
在网站结构改变后人也有能力(或者说被迫)使用新的url。 producer和consumer完全解耦。 网站说变就变了, 人自己去找吧。

但如果是打算作为programmable service,要让machine consumer也能理解HATEOAS和code-on-demand就没这么容易了。

因此真正的REST evangelist是不推荐使用json的。 比如github的api就带了很多links。 但要解读它们需要github-specific的方法。
他们推荐使用xml,因为xml有一些官方的micro format。 如果这些micro format足够流行就会对machine consumer理解REST app以及REST app之间的互通有帮助。
现实情况是。。。 这些micro format不流行。。。 xml也逐步开始退出流行。。。

总之,这就是我理解的Roy Fielding以及REST evangelist所想的东西。 至于它是否实际、是否会流行甚至传承下去? 可以与Alan Kay之于OO作个比较。。。
作者: OwnWaterloo    时间: 2014-03-24 02:48
starwing83 发表于 2014-03-24 02:16
反正总的来说,你觉得纠结还不是因为SQL功能太强了么= =你要是用不到那些超级功能那关那些干嘛= =


嗯,纠结的就是这点。 那些超级功是因为没有遇到复杂的需求才没用上? 还是因为一开始就是这么考虑的,所以下意识地将那些复杂需求给排除掉或者转换成独立object进行操作?
总之就是缺经验。

starwing83 发表于 2014-03-24 02:16
恩,所以你说对了,的确就是只解决80%问题的


那其实这样也行。。。 能轻松一些是一些。。。

starwing83 发表于 2014-03-24 02:19
哪天要是捐我一台服务器什么的我再去改改……

捐了服务器还改? 正好用来横向扩展。。。

作者: starwing83    时间: 2014-03-24 03:11
回复 46# OwnWaterloo


    好吧= =去碎觉了= =明天据说要出版本(出来的概率跟找到马航其实差不多吧)所以嘛= =
作者: OwnWaterloo    时间: 2014-03-24 04:38
回复 44# starwing83

关于JS。 我也没时间去看。。。  只是现在有Lutz顶着于是我就没压力了。。。

关于那两点,我觉得后一句改成: 所有资源都都可以用统一的动词 — GET,HEAD,PUT,DELETE,POST,OPTIONS —去操作。 更合适一些。
没有findItem和findItems,findItemsWhenMatch等不同的动词, 而是用资源来代表单一对象GET /post/ID,集合对象 GET /post,算法(filter) GET /post?after=yesterday 等等。
至于资源是否支持那是资源说了算。 可以用OPTIONS 查询, 可以返回405并给出Allowed, 可以返回404代表这个资源根本不存在。

关于state。  以ftp服务和一个支持上传删除的http文件服务器作为对比。

服务器上肯定是有状态的 —— 目前有哪些文件。 这个状态也是可以改变的 —— 同上传和删除。
RWS里把这个叫做server state。

ftp连接后,可以认证、改变当前目录,列出当前目录,列出当前目录里的文件,下载/上传/重命名/删除文件,创建删除目录等等。
后面那些命令都与认证已经当前目录这两个状态有关。这两个状态会保持到连接断开为止。

而http协议是无状态的。每一个请求都必须包含认证和打算操作的文件的全路径这两个状态,服务器才有足够的信息来完成这个请求。
RWS里把这个叫做application state。 REST api的消费者(application)的状态。

REST application每次都必须把自己的state传递给server,让server有足够的信息完成请求。 这个请求可能会影响server state(unsafe methods)也可能不会(safe methods)。
假设这样实现一个支持匿名用户操作的http文件服务器:
1. GET /files/path/to。 如果 /path/to 是一个文件,就在header里放置相应的content-type,content-length,并且将文件内容放到body里作为响应。 而如果是一个目录,就返回405 Method Not Allowed。
同样还可以通过HEAD查询文件的元信息而并不下载文件。
这两种请求必须将application state —— /path/to 传递给服务器。 但不会影响服务器的状态。
2. PUT /files/path/to 。 上传文件。 如果该路径是一个目录就405 Method Not Allowed。
3. PUT /files/path/to/ 。 创建目录。 如果该路径是一个文件就405 Method Not Allowed。
4. DELETE /files/path/to 。 删除文件或目录。
后面几个如果成功都会影响server state。


问题是这样的api即使是curl消费起来都很困难。。。 至少得有ls功能吧?
于是GET /files/path/to 如果是目录的话, 就可以给出这个目录下的文件和子目录的链接。 于是curl或者浏览器就知道存在哪些资源(文件)以及怎么转换到另一个application state去产生相应的request。
浏览器进入一个目录之后地址栏就会有相应的改变,以显示出当前的application state的一部分。 展示出的链接都是可以转换到的其他的application state。
同时还可以继续带上form表单以引导用户提交或删除文件 —— 同样是转换到其他的application state。 提倡REST的人同时也希望form这种hypermedia能被程序理解 —— 可以这样上传、删除文件哦。

继续假设现在要添加认证功能。 初始的application state里不包含Authorization这个状态。于是服务器无法获得当前是那个用户这一信息。
假设 /files/anonymous/ 下是允许任何人随便弄的。  对这下面的请求服务器都可以完成。
而如果对这之外的请求, 服务器发现没有Authorization时就可以返回401 Unauthorized并带上WWW-Authenticate。
浏览器就会弹出一个丑陋的框, 要求用户输入账户名与密码。 之后浏览器继续发送上一个application state以及新增加的Authorization到服务器。
这下服务器就有足够的信息判断该用户是否有足够的权限访问。 并根据授权规则显示部分允许被操作的资源的链接。
通过这种方式REST就让application添加了新的Authorization状态。 只是这个状态不容易消除。。。
同样, 提倡REST的人希望401和WWW-Authenticate能被程序理解。

starwing83 发表于 2014-03-24 02:28
那么按照你说的,我是不是可以这么理解S,所谓状态就是:资源的表示能提供访问更多操作的可能性?
也就是说,我不知道有多少用户,我GET /user,得到一个列表,这样我就能对我获取的用户进行操作了——因此状态(就有机会)改变了?不然我就只知道这一个接口,谈不上能改变状态。
这么说,S其实说的不是状态,而是【获取可以进行其他操作的必要信息】,是这个理解么

按我目前的理解, http request里所包含的让服务器足够区别是谁打算做什么操作的信息应该算application state。
上面的例子中还有很多request里的信息是server暂时不感兴趣的。 它们算不算application state就不知道了。

对资源请求产生的representation里可以包含改变application state的方式。 比如link, form, 401等等。
GET /user 里, GET和 /user 应该都是application state。 返回一个列表, 里面的链接就可以引导消费者 GET /user/sw 或 GET /user/ow , 于是application state就从 GET /user 转换到了后两者之一。

资源的表示能提供迁移到其他application state的可能性。
GET /user 可以得到一个列表, 因此application state就有机会改变 —— 就可以请求特定的某个用户。
不然只知道 /user 就不知道应该怎么获取特定的user。 就像最开始那个不支持ls的http文件服务器一样。
"获取可以进行其他操作的信息"就是HATEOAS的内容,这些信息可以引导application 进入其他的S,并将S传递给服务器。


关于token。
假设一开始的application state里是没有的。 当访问了一个需要权限的资源时就会引导用户认证, 然后让application添加这个state。
比如认证成功后在header里加一个 Set-Cookie: token=sw;   暂时抛开安全性问题。。。
于是后续的请求就会发送 Cookie: token=sw。 服务器就知道这是谁在发送请求, 并能根据这点进行授权。
服务器里没有和token相关的记录, 每次请求都必须把这个Cookie重新发送。

而另一种做法。 认证成功后在header里添加一个 Set-Cookies: {J,PHP,...}SESSION_ID=random_key。
同样后续的请求会有 Cookie: PHPSESSION_ID=random_key。 服务器也可以通过random_key查询一个lookup table获得该SESSION里的数据,比如user。
于是RWS(不太记得到底是不是这本书里这么说的,还是其他地方)就分情况了。。。
如果lookup table是服务器内存里的一个数据结构, 那这就是将application state(哪个用户发起的请求)放在了server state里。

这样的区分确实有好处。  假设有多个logic server。
前一种 Cookie: token=sw的方式, load balancer无论将它转发到哪个logic server都是可以工作的。 并且每一个logic server都是crash only。 挂了后重启就是。
而后一种 Cookie: PHPSESSION_ID=random_key 的话, load balancer就需要支持session stick。 比如另外添加一个 Set-Cookie: server=1。 然后根据传回的Cookie: server=1将这个请求继续转发回server1。
或者修改PHPSESSION_ID为S1PHP_SESSION_ID,然后根据它选择server1后又改回PHPSESSION_ID。
同时,如果server1挂了, 之前粘着在server1上的用户的登录状态就一起挂了。
也就是说前一种方式每个请求与每个服务器之间没有亲缘性, 而后一种就会有。

但有时候一些机密的东西不能发回到客户那里。。。 于是RWS松口了。。。
说在这种情况下,不要把那个lookup table放在逻辑服务的内存里。。。 应该放在持久化存储里。。。 比如数据库或redis(开启持久化机制)之类的东西里。。。

总之。。。 No Silver Bullet。。。
作者: OwnWaterloo    时间: 2014-03-24 04:57
本帖最后由 OwnWaterloo 于 2014-03-24 04:59 编辑

回复 44# starwing83

虽然和web开发有关。。。 但貌似和楼顶的书没关系了。。。  于是又跑题这么多楼?
最后跑一帖。。。 关于session stick。。。 这应该是从RWS里最大的收获之一。 另一个可能是http verb,方法的安全与幂等。 其他的,感觉必须照着这样做的理由还不够充分。。。

这完全改变了我一开始的一个想法。。。
网站不是可能会有一些需要多个交互步骤才能完成的流程嘛。。。
第1步完成一些工作,然后将后续需要的状态放到session里。 后续步骤继续从session里读,然后可能会添加更多的状态。 最后完成它。。。
编写GUI时这种人肉维护之前的状态的方式已经受够了。。。  现在知道coroutine和continuation后总不会这么苦逼了吧?
看了RWS以及类似The twelve-factor的文章之后彻底傻了。。。
它们说得有道理。。。 于是我再也不提coroutine或continuation之于web开发了。。。 除非能将它们序列化和反序列化。。。

而随后关于session stick的想法还在继续转变。。。  因为NoSQL的关系。。。
relational database(或者说支持强一致性的存储)不是扩展起来很麻烦嘛。。。  于是有eventually consistent的存储。。。
一旦用了它。。。 那又需要session stick了。。。 虽然需求可能不如Cookie: SESSION_ID=random_key 这样强。。。

等有多步交互或eventually consistent存储的需要时再说。。。
作者: starwing83    时间: 2014-03-24 14:35
回复 49# OwnWaterloo


    妈的,鼠标没电了,恢复的数据没了,我回了好多字的= =

不管了,晚上再说好了= =




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2