[REST]REST构架风格介绍之二:CRUD

mikel阅读(881)

上一节我们通过两个例子初步体会了REST状态表述转移的味道,但应该指出这两个例子还仅仅是简单的资源获取。REST是以资源为核心的,没有服务的概念,这的确让人怀疑REST能否像ORBSOA一样支持复杂的应用?在回答这个问题之前,让我们先暂时离开REST,把眼光转向基于关系数据库的3层构架。

通常业务逻辑层对外提供若干的功能接口(如图中定义的IOrderService),对内通过数据访问层访问数据库。我们知道,关系数据库只定义了CRUD(Create, Read, Update, Delete)四种标准的数据操作,分别对应于insert/select/update/delete四种SQL语句。经验告诉我们,关系数据库在若干张表上进行关系运算是足以支持各种复杂业务逻辑的,因为所有业务功能最终都会被映射到数据库上的CRUD四种标准操作。下面这个有趣的三角形能帮助我们理解这个问题:

图中的三角分别代表:数据类型、操作、实例。可以把他们想象成可以调节的按钮,业务逻辑层的方式是:定义了少量的服务实例,把大量的操作放在服务实例下面,形象地比喻为“一扇小门,里面装了很多东西”;而数据库的方式则是提供了大量数据实例和CRUD四种标准操作,可比喻为“很多门,每扇门里面装少量的东西”。

以资源为核心的REST和以数据为核心的关系数据库是类似的。数据和资源本质上都是状态,对状态的操作CRUD少一个不行,多一个多余。因此,REST也采用CRUD四种标准操作,分别对应于HTTP协议的POST/GET/PUT/Delete方法。虽然HTTP协议支持POST/GET/PUT/Delete以外的HEAD等方法,可以把这些非标准方法作为有用的补充,但不应影响REST模型的纯洁性。上一节中,我们看到REST风格的应用像一个状态机;而这里我们则看到它像一个数据库。REST方式定义出的资源(Url)和相应的操作就像下面这个样子(值得重申的是,从Url的含义“统一资源定位符”就可以看出其通用性,这也是REST资源表示的优势所在):

REST完美地结合了HTTP协议,所以更容易无缝接入互联网。另外,有人提到 “一个网站对外暴露的网页数可以作为衡量它为互联网所做贡献的指标”,如果从这个角度来看,以资源为核心的REST方式比服务为核心的SOA对互联网更加友好。但 应该采用哪种风格的构架还是取决于应用本身的特点,一般来讲,对于以提供和管理数据为主的,且希望做SEO的应用适合REST风格,这包括大多数 WEB2.0的应用;而以计算和业务逻辑为主,且强调安全性的应用不太适合REST风格。但应避免不加分析先入为主的采用面向服务的思维,有时候恰当运用 状态表述转移模式的REST构架不但可以实现业务逻辑,而且具有更好的伸缩性,正如上一节谈到的心理测试服务和Google搜索一样。REST的价值就在 于让我们在设计构架的时候多了一种视角,所谓“眼界决定世界”。

Cache

由于RESTUrl表示资源和无状态服务特点,使得Cache机制变得异常简单,且HTTP协议中有直接支持。服务器响应可以通过cache-control:max-age,expires指定资源缓存时间;还可以在响应头的last-modified参数标明资源的最后修改时间,客户端请求可以带上if-modified-since参数,如果资源未过期,服务器只需用返回304 not modified状态即可,这样就避免了服务器端重复工作,也节省网络带宽;etag参数也是常用的cache控制参数,可以解决last-modified时间精度不够的问题。另外,HTTP协议还对Proxy机制有直接的支持,与Cache机制结合,在需要高性能的应用中,可以在服务器与客户端之间部署若干专门用于Cache目的Caching Proxy Server提高系统吞吐量

总结

最后总结一下REST的要点:1. Url表示资源;2.  CRUD操作;3. 状态表述转移。至于无状态服务、Http状态码、Cache控制、Proxy等则属于上面几个要点的推论,理解REST的关键还在于理解以资源为核心的模型。本文是我接触REST不到一年时间的一些体会和总结,深知对REST的掌握和应用还需继续努力,希望得到高手的指点!

相关链接

1.《如何获取一杯咖啡——星巴克REST案例分析》

2.《深入浅出REST》

[Flash]Flash和Flash的通信

mikel阅读(920)

转载:http://www.blueidea.com/tech/multimedia/2004/1712_3.asp

Flash和Flash的通信
通过上面两种方法的混和使用,同一HTML中两个或者更多的Flash直接可以相互传送消息. 从一个flash使用fscommand传送消息给JavaScript,使用Flash的JavaScript methods把消息传给另外一个flash
详细的看这里:Flash影片之间的通信示例
从Flash MX开始,local connection对象可以用来在flash之间传送消息. 这使得同一HTML中的或者位于两个浏览器窗口中的两个flash影片可以相互发送消息,而不必使用JavaScript或者fscommand
详细的看这里:在Flash MX中使用local connection对象(英文)

本站的localConnection教程:http://www.blueidea.com/tech/multimedia/2003/739.asp
附可控制Flash Player的Javascript方法:
一览表:
Play() —————————————- 播放动画
StopPlay()————————————停止动画
IsPlaying()———————————– 动画是否正在播放
GotoFrame(frame_number)—————- 跳转到某帧
TotalFrames()——————————- 获取动画总帧数
CurrentFrame()——————————回传当前动画所在帧数-1
Rewind()————————————-使动画返回第一帧
SetZoomRect(left,top,right,buttom)——-放大指定区域
Zoom(percent)——————————改变动画大小
Pan(x_position,y_position,unit)————使动画在x,y方向上平移
PercentLoaded()—————————-返回动画被载入的百分比
LoadMovie(level_number,path)———– 加载动画
TGotoFrame(movie_clip,frame_number)- movie_clip跳转到指定帧数
TGotoLabel(movie_clip,label_name)—— movie_clip跳转到指定标签
TCurrentFrame(movie_clip)————— 回传movie_clip当前帧-1
TCurrentLabel(movie_clip)—————–回传movie_clip当前标签
TPlay(movie_clip)—————————播放movie_clip
TStopPlay(movie_clip)———————-停止movie_clip的播放
GetVariable(variable_name)—————–获取变量
SetVariable(variable_name,value)———–变量赋值
TCallFrame(movie_clip,frame_number)—call指定帧上的action
TCallLabel(movie_clip,label)—————-call指定标签上的action
TGetProperty(movie_clip,property)——–获取movie_clip的指定属性
TSetProperty(movie_clip,property,number)-设置movie_clip的指定属性

在 flash mx 以后,由于一系列的安全问题,比如载入另一个站点的 flash文件,然后用javascript控制 flash 跳转到不同的帧,让一些 flash 编写者在安全防范上防不胜防。为此 macromedia 改进了 flash player 和一些交互接口。引入了安全沙箱的概念,也就是可以设置一个可以进行控制的站点列表。

当然在同一个域名下,可以不考虑这个安全沙箱的问题。

更详细的接口和用法还有安全限制,请查看下面的PDF文件

另外我再提供一个 flash4 时代最顶尖的闪客 Dean 利用 javascript 和 flash进行交互的例子,这可是当时,最最眩目的页面了,html和flash结合紧密,而且保证了和 netscape 的兼容。

点击查看 看看我们4年前的闪客,在做什么?

另外,我在今年也有类似的应用,大家可以查看 bt 的专栏动画部分 ,在打开一个flash动画弹出框时,为了不影响音乐杂乱,我关闭了主场景的音乐开关。关闭弹出框时,打开主场景的音乐开关,这点,也主要是依靠 javascript 来进行对 flash 的控制的。

出处:蓝色理想
责任编辑:qhwa

[Flash]Flash到Javascript的通信

mikel阅读(692)

Flash到JavaScript的通信
从HTML可以发送数据到Flash,反过来也可以. 这个例子演示了如何应用Flash的Fscommand来发送数据到JavaScript.

查看示例:示例2
下载源文件: flash_to_javascript.zip(10K)
简要步骤:
Flash中
新建一个文件,保存为flash_to_javascript.fla
创建一个文本域,设置成输入文本(Input Text),选择"border"以便我们能看到他,指定他的变量为inputVar
创建一个按钮,在按钮上添加如下的as:

on (release) {
  fscommand ("send_var", inputVar);
}

保存文件,导出HTML和SWF
Dreamweaver中
1.打开导出HTML文件,修改<OBJECT>和<EMBED>标签,结果同上:
<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
codebase="http://download.macromedia.com/pub/shockwave/cabs/
flash/swflash.cab#version=5,0,0,0"
width=366 height=142 id="myFlash">
<embed src="javascript_to_flash.swf" quality=high
width=366 height=142
type="application/x-shockwave-flash"
pluginspage="http://www.macromedia.com/shockwave/download/index.cgi?
P1_Prod_Version=ShockwaveFlash" name="myFlash" swLiveConnect="true">
</embed>

 2. 插入如下的Javascript到<Body>标签内:
<SCRIPT LANGUAGE=JavaScript>
<!–
var InternetExplorer = navigator.appName.indexOf("Microsoft") != -1;
function myFlash_DoFSCommand(command, args)                {
  var myFlashObj = InternetExplorer ? myFlash : document.myFlash;
  alert (args);
}

if (navigator.appName && navigator.appName.indexOf("Microsoft") != -1 &&
  navigator.userAgent.indexOf("Windows") != -1 && navigator.userAgent.indexOf("Windows 3.1") == -1) {
  document.write('<SCRIPT LANGUAGE=VBScript\> \n');
  document.write('on error resume next \n');
  document.write('Sub myFlash_FSCommand(ByVal command, ByVal args)\n');
  document.write(' call myFlash_DoFSCommand(command, args)\n');
  document.write('end sub\n');
  document.write('</SCRIPT\> \n');
}
//–>
</SCRIPT>

出处:蓝色理想
责任编辑:qhwa

[Flash]JavaScript 和 Flash 的通信

mikel阅读(687)

转载:http://www.blueidea.com/tech/multimedia/2004/1712.asp

JavaScript和Macromedia Flash的通信示例
原文地址: www.macromedia.com/support/flash/ts/documents/java_script_comm.htm
说明: 略作修改,主要是一些很初级的操作; 又很多相似的文章,不过这个很权威
下面是一些在Flash和使用JavaScript的HTML文件直接通信的示例,每个示例都有简略的步骤
本文讨论了3种基本的Flash/Javascript通信方式:
Javascript 到 Flash的通信—-使用Flash播放器的javascript方法
Flash 到 Javascript的通信—-使用Flash的fscommand
Flash 到 Flash的通信———-使用本地连接对象或综合上2种技术
并不是所有的浏览器都很重视脚本.为了和Flash播放器通信,浏览器必须有内置的钩子以便Flash播放器可以'监听'.浏览器必须是下列的几种:
Netscape Navigator 3.0-4.7x, 和 Netscape 6.2或更高
(Windows 95/98/NT/2000/XP 或 MacOS; 允许Java和LiveConnect)
Internet Explorer 3.0 或更高
(仅Windows 95/98/NT/2000/XP; 允许ActiveX)
注意:Macintosh上的Internet Explorer和早期版本的Netscape6不支持这种方法.请查看本文的附加信息
Javascript到Flash的通信

这个例子演示了如何使用Flash的method把变量从HTML的input text 发送到该页面中的Flash文件中.HTML input的数据通过Flash的SetVariable方法传送到Flash文件中.

查看示例:示例
下载源文件: javascript_to_flash.zip(17k)
步骤:

Flash中
1.新建一个文件,保存为javascript_to_flash.fla
2.用文字工具在舞台上创建一个文本域
3.选择这个文本域,在属性面板中,从下拉列表中选择动态文本(Dynamic Text),在变量(variable)栏填上"myVar"
注意:最好的习惯是使用Instance,用myVar.text更改myVar的值.为了简单起见和兼容Flash4和Flash5,我们使用的是变量名的形式.
4.保存文件
5.发布HTML文件和SWF文件

Dreamweaver中
下一步的工作转移到Dreamweaver中了,当然也可以是其他的HTML编辑器
1.打开上一步发布的HTML文件
2.插入生成的SWF文件和OBJECT/EMBED标签
(1) Insert>Media>Flash,并选择这个Flash
(2) 切换到代码视图,我们需要修改被选中的<OBJECT>和<EMBED>标签
(3) 在OBJECT标签中,插入id="myFlash"

<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
codebase="http://download.macromedia.com/pub/shockwave/cabs/
flash/swflash.cab#version=5,0,0,0"
width=366 height=142 id="myFlash">

注意:如果你是粘贴复制代码的话,确保删除不必要的换行.否则可能会引起错误; id也可以在属性面板里直接输入
(4) 在EMBED标签中,插入name="myFlash"和swLiveConnect="true",确保没有使用id属性!代码应当是这样子的:

<embed src="javascript_to_flash.swf" quality=high width=366 height=142
type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/shockwave/download/index.cgi?
P1_Prod_Version=ShockwaveFlash"
name="myFlash" swLiveConnect="true">
</embed>

3.创建表单域
(1) 回到设计视图
(2) 插入文本域(Insert> Form Object> Text Field),如果询问是否添加表单域,选择是
(3) 把文本域的HTML标签修改成这样:

<input type="text" name="sendText" maxlength="45" onChange="doPassVar(this)">
每当文本域内容发生变化时,onChange就被触发,doPassVar()函数就被调用

4.创建传递变量值的Javascript函数
复制下面的Javascript到<head></head>标签内

<SCRIPT LANGUAGE=JavaScript>
<!–
function doPassVar(args){
var sendText = args.value;
   window.document.myFlash.SetVariable("myVar", sendText);
}
//–>
</SCRIPT>

5.保存文件,测试一下(F12)

出处:蓝色理想
责任编辑:qhwa

[MVC]ASP.NET MVC雕虫小技 1-2

mikel阅读(826)

转载:http://www.cnblogs.com/chsword/archive/2009/05/08/mvcskill_1.html

看到AnyTao和TerryLee分享的关于ASP.NET MVC使用和优化的技巧,不免手痒,也分享一下这一年多来ASP.NET MVC开发的积累。

其中未必是一些高效的技巧,但是的确是能解决问题,也未必有什么高深的原理,只是我觉得值得分享。

1.Controller来控制HTML的Title

我想大部分朋友都有在Controller里面指定Html页面Title的需求。

我习惯于先写使用的代码再去完善其实现,而指定一个Title最方便的形式莫过于:

   1: public ActionResult Index(int id) {

   2:     var article=Db.GetArticle(id);//获取数据库里的文章

   3:     Title=article.Name;

   4:     return View();

   5: }

当然,这段代码是不能执行的,因为Controller并没有内建的Title属性,不过没关系,我们可以自定义一个:

   1: abstract public class MyBaseController : Controller {

   2:        public string Title {

   3:            set {

   4:                ViewData["Page_Title"] = value;

   5:            }

   6:        }

   7: }

然后将我们的Controller换为这个MyBaseController,之后在Master中写ViewData[“Page_Title”]的输出就好了。

   1: <title><%=ViewData["Page_Title"] %></title>

OK,这个愿意实现了。

当然做SEO的话Keyword和Description也可以这样来搞。

2.ViewModel中传递Controller中定义的上下文

老赵十分推ViewModel于是我也做了不少这方面的实践,发现的确不错。但是有个问题,就是Controller中产生的上下文怎么传到View中去,比如说自定义的用户信息,等一些非static的类型,而我又不想到View中再实例化一遍。

解决方法:ViewModel中另加一上下文属性(在我和程序中这些上下文继承于IContext接口,而在Controller中它的属性是CHContext)

   1: public class HomeIndexViewModel {

   2:     public IContext Context { get; set; }//这个属性就是解决它的方法

   3:     public string Message { get; set; }

   4: }

而我在Controller中:

   1: public ActionResult Index() {

   2:     HomeIndexViewModel model = new HomeIndexViewModel {

   3:         Context = CHContext,//这里传递

   4:         Message="Welcome to ASP.NET MVC!"

   5:     };

   6:     return View(model);

   7: }

而View中:

   1: <%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master"

   2: Inherits="System.Web.Mvc.ViewPage<HomeIndexViewModel>" %>

   3: <asp:Content ID="indexTitle" ContentPlaceHolderID="TitleContent" runat="server">

   4:     Home Page

   5: </asp:Content>

   6: <asp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server">

   7:     <h2><%= Html.Encode(Model.Message) %></h2>

   8: </asp:Content>

这样我们就可以实现将Controller中产生的自定义上下文传递了。不过每个ViewModel都初始化一个IContext,未免太过频繁, 也累人,于是进一步改进,我们利用作用在Controller上的Filter,在Controller的基类我们自定义的 MyBaseController中写如下Filter,而实现这个功能则要所有的ViewModel继承于一个类:MyBaseViewModel:

MyBaseViewModel与Model:

   1: public class MyBaseViewModel {

   2:     public IContext Context { get; set; }

   3: }

   4:  

   5: public class HomeIndexViewModel:MyBaseViewModel {

   6:     public string Message { get; set; }

   7: }

Controller与Filter

   1: abstract public class BaseController : Controller {

   2:     protected override void OnResultExecuting(ResultExecutingContext filterContext) {

   3:         var m = ViewData.Model as BaseViewModel;

   4:         if (m != null){

   5:             m.Context = CHContext;//在这里初始化

   6:         }

   7:     }

   8: }

这回我们在Controller里使用时就清爽了,不用再传递CHContext了。

[Flash]纯AS代码实现可预览本地图片的flash上传客户端

mikel阅读(786)

转载:http://www.klstudio.com/post/182.html
[AS3]纯AS代码实现可预览本地图片的flash上传客户端
    需要Flash Player 10+版本的支持,原理就是主要利用fp10中的FileReference.load(),FileReference.data和 Loader.loadBytes()三个方法通过图片加载到内存中,来实现预览本地图片,但这个方式不太适用大图片预览,图片越大内存消耗就越大。

[注意]
1.我这边图片上传路径是无效的,所以图片上传失败是正常的,你们可以改一下上传路径即可;
2.需要Flash Player 10的支持;
3.这次主要研究是预览本地图片功能。

演示效果

实现代码

  1. package project.test   
  2. {  
  3.     import flash.display.*;  
  4.     import flash.geom.Rectangle;  
  5.     import flash.net.*;  
  6.     import flash.text.*;  
  7.     import flash.filters.*;  
  8.     import flash.events.*;  
  9.     import flash.system.Security;  
  10.       
  11.     import fl.controls.Button;  
  12.     import fl.controls.ProgressBar;  
  13.     import fl.controls.ProgressBarMode;  
  14.       
  15.     /** 
  16.      * @link kinglong@gmail.com 
  17.      * @author Kinglong 
  18.      * @playerversion fp10    
  19.      */  
  20.     [SWF(width="500", height="300", frameRate="24", backgroundColor="#FFFFFF")]  
  21.     public class TestUpload extends Sprite {  
  22.           
  23.         private const DEFAULT_UPLOAD_PAGE:String = "http://test.klstudio.com/upload.asp";         
  24.         private const BOX_WIDTH:uint = 500;  
  25.         private const BOX_HEIGHT:uint = 300;  
  26.           
  27.         private const STATE_CACHE:String = "cache";  
  28.         private const STATE_UPLOAD:String = "upload";  
  29.           
  30.         private var _filters:Array;  
  31.         private var _file:FileReference;  
  32.         private var _loader:Loader;  
  33.         private var _progress:ProgressBar;  
  34.         private var _state:String;  
  35.         private var _buttons:Array;  
  36.         private var _labels:Array;  
  37.         private var _txts:Array;  
  38.         private var _rect:Rectangle;  
  39.         private var _state_txt:TextField;  
  40.           
  41.         public function TestUpload() {  
  42.             Security.allowDomain("*");  
  43.               
  44.             _buttons = [];  
  45.             _txts = [];  
  46.             _labels = ["文件名称:","文件类型:","文件大小:","修改时间:"];  
  47.               
  48.             _rect = new Rectangle(2080180180);  
  49.             _state = STATE_CACHE;  
  50.               
  51.             //背景;  
  52.             this.graphics.beginFill(0x333333);  
  53.             this.graphics.drawRoundRect(00, BOX_WIDTH, BOX_HEIGHT, 1010);  
  54.             this.graphics.endFill();  
  55.             this.graphics.beginFill(0xEFEFEF);  
  56.             this.graphics.drawRoundRect(11, BOX_WIDTH – 2, BOX_HEIGHT – 21010);  
  57.             this.graphics.endFill();  
  58.             this.graphics.beginFill(0x666666);  
  59.             this.graphics.drawRoundRect(1030, BOX_WIDTH – 20, BOX_HEIGHT – 602020);             
  60.             this.graphics.endFill();  
  61.             this.graphics.beginFill(0xFEFEFE);  
  62.             this.graphics.drawRoundRect(1131, BOX_WIDTH – 22, BOX_HEIGHT – 622020);  
  63.             this.graphics.endFill();  
  64.               
  65.             this.graphics.beginFill(0xCCCCCC);  
  66.             this.graphics.drawRect(1170, BOX_WIDTH – 221);  
  67.             this.graphics.endFill();  
  68.               
  69.             this.graphics.beginFill(0x000000);  
  70.             this.graphics.drawRect(_rect.x-1, _rect.y-1, _rect.width+2, _rect.height+2);  
  71.             this.graphics.endFill();              
  72.             this.graphics.beginFill(0xEEEEEE);  
  73.             this.graphics.drawRect(_rect.x, _rect.y, _rect.width, _rect.height);  
  74.             this.graphics.endFill();  
  75.               
  76.               
  77.             //标题;  
  78.             var label:TextField;              
  79.             label = getLabel("图片上传(预览图片版) by Kinglong", getTextFormat(0xFFFFFF14true));  
  80.             label.x = 10;  
  81.             label.y = 5;  
  82.             label.filters = [getLabelFilter(0x000000)];  
  83.             this.addChild(label);  
  84.               
  85.             for (var i:uint = 0; i < _labels.length; i++ ) {           
  86.                 label = getLabel(_labels[i], getTextFormat(0x33333312), falsefalse);                  
  87.                 label.x = _rect.right+5;  
  88.                 label.y = _rect.y + 25 * i;  
  89.                 label.width = 280;  
  90.                 label.height = 20;  
  91.                 _txts.push(label);  
  92.                 this.addChild(label);  
  93.             }             
  94.               
  95.             _state_txt = getLabel("状态:", getTextFormat(0x33333312));  
  96.             _state_txt.x = 10;  
  97.             _state_txt.y = BOX_HEIGHT – _state_txt.height – 5;  
  98.             this.addChild(_state_txt);  
  99.               
  100.             //按钮;  
  101.             var button:Button;  
  102.             button = getButton("选择文件"80);           
  103.             button.move(2040);  
  104.               
  105.             button = getButton("上传文件"80);           
  106.             button.move(10540);  
  107.             button.enabled = false;  
  108.               
  109.             //进度条;  
  110.             _progress = new ProgressBar();  
  111.             _progress.move(19040);  
  112.             _progress.setSize(290,22);  
  113.             _progress.mode = ProgressBarMode.MANUAL;                          
  114.             this.addChild(_progress);  
  115.               
  116.             //文件类型;  
  117.             _filters = [];  
  118.             var filter:FileFilter;            
  119.             filter = new FileFilter("所有支持图片文件(*.jpg,*.jpeg,*.gif,*.png)""*.jpg;*.jpeg;*.gif;*.png");  
  120.             _filters[_filters.length] = filter;  
  121.             filter = new FileFilter("JPEG files(*.jpg,*.jpeg)","*.jpg;*.jpeg");  
  122.             _filters[_filters.length] = filter;  
  123.             filter = new FileFilter("GIF files (*.gif)","*.gif");  
  124.             _filters[_filters.length] = filter;  
  125.             filter = new FileFilter("PNG files(*.png)","*.png");  
  126.             _filters[_filters.length] = filter;   
  127.               
  128.             _file = new FileReference();   
  129.             _file.addEventListener(Event.COMPLETE, fileHandler);  
  130.             _file.addEventListener(DataEvent.UPLOAD_COMPLETE_DATA, fileHandler);  
  131.             _file.addEventListener(Event.Select, fileHandler);  
  132.             _file.addEventListener(Event.OPEN, fileHandler);              
  133.             _file.addEventListener(ProgressEvent.PROGRESS, fileHandler);  
  134.             _file.addEventListener(SecurityErrorEvent.SECURITY_ERROR, fileHandler);  
  135.             _file.addEventListener(IOErrorEvent.IO_ERROR, fileHandler);  
  136.             _file.addEventListener(HTTPStatusEvent.HTTP_STATUS, fileHandler);  
  137.               
  138.             _loader = new Loader();  
  139.             _loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadHandler);  
  140.             this.addChild(_loader);  
  141.         }  
  142.           
  143.         public function get state():String {  
  144.             return _state;  
  145.         }  
  146.           
  147.         private function clickHandler(event:MouseEvent):void {  
  148.             switch(event.target) {  
  149.                 case _buttons[0]:  
  150.                     _file.browse(_filters);  
  151.                     break;  
  152.                 case _buttons[1]:  
  153.                     _file.upload(new URLRequest(DEFAULT_UPLOAD_PAGE));  
  154.                     _state = STATE_UPLOAD;                    
  155.                     _buttons[0].enabled = false;  
  156.                     _buttons[1].enabled = false;  
  157.                     break;  
  158.             }  
  159.         }  
  160.           
  161.         private function loadHandler(event:Event):void {  
  162.             _loader.scaleX = _loader.scaleY = 1;  
  163.             var w:uint = _loader.width;  
  164.             var h:uint = _loader.height;  
  165.             if (w > _rect.width || h > _rect.height) {                  
  166.                 var ip:Number = w / h;  
  167.                 var lp:Number = _rect.width / _rect.height;           
  168.                 _loader.width = (ip > lp)?_rect.width:_rect.height*ip;  
  169.                 _loader.height = (ip > lp)?_rect.width / ip:_rect.height;  
  170.             }  
  171.             _loader.x = _rect.x + (_rect.width – _loader.width) / 2;  
  172.             _loader.y = _rect.y + (_rect.height – _loader.height) / 2;            
  173.             _loader.visible = true;  
  174.         }  
  175.           
  176.         private function fileHandler(event:Event):void {  
  177.             switch(event.type) {  
  178.                 case Event.COMPLETE:  
  179.                     if(state == STATE_CACHE){  
  180.                         _loader.loadBytes(_file.data);  
  181.                     }  
  182.                     break;  
  183.                 case DataEvent.UPLOAD_COMPLETE_DATA:  
  184.                     Debug("图片上传完成!");  
  185.                     _buttons[0].enabled = true;  
  186.                     _buttons[1].enabled = false;  
  187.                     _progress.setProgress(01);  
  188.                     break;  
  189.                 case Event.Select:  
  190.                     _txts[0].text = _labels[0] + _file.name;  
  191.                     _txts[1].text = _labels[1] + _file.type;  
  192.                     _txts[2].text = _labels[2] + ((_file.size > 1024 * 1024)?Math.round(_file.size * 10 / (1024*1024))/10 + "MB":Math.round(_file.size * 10 / 1024)/10 + "KB");                    
  193.                     _txts[3].text = _labels[3] + date2str(_file.modificationDate);  
  194.                     _buttons[0].enabled = true;  
  195.                     _buttons[1].enabled = true;  
  196.                     _file.load();  
  197.                     _state = STATE_CACHE;  
  198.                     _loader.visible = false;  
  199.                     Debug("图片已经准备!");  
  200.                     break;  
  201.                 case Event.OPEN:  
  202.                     if(state == STATE_UPLOAD){  
  203.                         Debug("正在上传图片…");  
  204.                     }  
  205.                     break;  
  206.                 case ProgressEvent.PROGRESS:  
  207.                     if (state == STATE_UPLOAD) {  
  208.                         var pEvent:ProgressEvent = event as ProgressEvent;  
  209.                         _progress.setProgress(pEvent.bytesLoaded, pEvent.bytesTotal);  
  210.                     }  
  211.                     break;  
  212.                 case SecurityErrorEvent.SECURITY_ERROR:  
  213.                 case IOErrorEvent.IO_ERROR:  
  214.                 case HTTPStatusEvent.HTTP_STATUS:                     
  215.                     if (state == STATE_UPLOAD) {  
  216.                         debug("图片上传失败!");  
  217.                         _buttons[0].enabled = true;  
  218.                         _buttons[1].enabled = true;  
  219.                     }else {  
  220.                         debug("图片缓冲失败!");  
  221.                     }  
  222.                     _progress.setProgress(01);  
  223.                     break;  
  224.                   
  225.             }  
  226.         }  
  227.           
  228.         private function getButton(lbl:String,width:uint=120):Button {  
  229.             var button:Button = new Button();  
  230.             button.label = lbl;  
  231.             button.setSize(width, 22);    
  232.             button.setStyle("textFormat", getTextFormat());  
  233.             button.setStyle("disabledTextFormat", getTextFormat(0x999999));  
  234.             button.setStyle("textPadding",4);  
  235.             button.addEventListener(MouseEvent.CLICK, clickHandler);              
  236.             this.addChild(button);  
  237.             _buttons.push(button);  
  238.             return button;  
  239.         }  
  240.           
  241.         private function getLabel(label:String, format:TextFormat, selectable:Boolean = false, autoSize:Boolean = true):TextField {           
  242.             var lbl:TextField = new TextField();  
  243.             lbl.selectable = selectable;  
  244.             lbl.defaultTextFormat = format;  
  245.             if(autoSize){  
  246.                 lbl.autoSize = TextFieldAutoSize.LEFT;  
  247.             }  
  248.             lbl.text = label;  
  249.             return lbl;  
  250.         }  
  251.           
  252.         private function getTextFormat(color:uint=0x000000,size:uint = 12,bold:Boolean=false):TextFormat {  
  253.             var format:TextFormat = new TextFormat();  
  254.             format.font = "宋体";  
  255.             format.color = color;  
  256.             format.size = size;  
  257.             format.bold = bold;  
  258.             return format;  
  259.         }  
  260.           
  261.         private function getLabelFilter(color:uint=0xFFFFFF):BitmapFilter {  
  262.             var alpha:Number = 0.8;  
  263.             var blurX:Number = 2;  
  264.             var blurY:Number = 2;  
  265.             var strength:Number = 3;  
  266.             var inner:Boolean = false;  
  267.             var knockout:Boolean = false;  
  268.             var quality:Number = BitmapFilterQuality.HIGH;  
  269.   
  270.             return new GlowFilter(color,  
  271.                                   alpha,  
  272.                                   blurX,  
  273.                                   blurY,  
  274.                                   strength,  
  275.                                   quality,  
  276.                                   inner,  
  277.                                   knockout);  
  278.         }  
  279.           
  280.         private function date2str(day:Date):String {  
  281.             var str:String = day.getFullYear() + "-";  
  282.             str += num2str(day.getMonth() + 1) + "-";  
  283.             str += num2str(day.getDate()) + " ";  
  284.             str += num2str(day.getHours()) + ":";  
  285.             str += num2str(day.getMinutes()) + ":";  
  286.             str += num2str(day.getSeconds());  
  287.             return str;  
  288.         }  
  289.           
  290.         private function num2str(val:Number):String {  
  291.             var str:String = "00" + val;  
  292.             return str.substr(str.length – 22);             
  293.         }  
  294.           
  295.         private function debug(message:String):void {  
  296.             _state_txt.text = message;  
  297.         }  
  298.           
  299.     }  
  300.       

[Flash]Flash实现多文件上传

mikel阅读(753)

转载:http://blog.iyi.cn/start/2007/06/ajaxflash.html

FancyUpload,用flash和mootools实现的一款多文件无刷新上传工具。
最大的特点是可以一次选择多个文件,无刷新上传。
早些时候曾想过一次选择多个文件的问题,浏览器默认的file标签一次只能选择一个文件,要浏览并读取本地文件就必须调用本地的组件或命令,所以单纯用JavaScript+html无解。
今天查看订阅的feeds时,无意中在Ajaxian看到这个演示图片上选择了多个文件:

非常好奇,过去看了一下demo,果然可以一次选择多个文件!

Browsfile的button没什么特别,就是一个button,肯定是通过js触发了某个动作。前面说过js和html是不能实现这个功能的,那么肯定是flash实现了这个功能。

文件里面有个Swiff.Uploader.swf,就是这个swf实现了文件浏览的功能,as在这:http://digitarald.de/workspace/packages/Uploader/Swiff.Uploader.as

google了一下flash filebrowserflash fileupload果然找到很多内容

这片中文的详细说明了那个flash的原理:
http://www.cnblogs.com/walkingboy/archive/2007/02/09/Flash_FileUpload_FileReference.html
出处可能是这个:
http://www.codeproject.com/aspnet/FlashUpload.asp

原理是用了flash的FileReferenceList API实现的多文件选取。
http://markshu.ca/imm/flash/tutorial/fileReference.html

另外还有几个实例:
http://www.betriebsraum.de/blog/2006/01/13/download-flash-8-file-browser/

http://www.extremefx.com.ar/blog/flash-textarea

[REST]深入浅出REST

mikel阅读(814)

作者 Stefan Tilkov 译者 苑永凯 发布于 2007年12月25日 下午10时10分

社区
Architecture,
SOA
主题
REST,
企业架构
标签
Web服务

不 知你是否意识到,围绕着什么才是实现异构的应用到应用通信的“正确”方式,一场争论正进行的如火如荼:虽然当前主流的方式明显地集中在基于SOAP、 WSDL和WS-*规范的Web Services领域,但也有少数人用细小但洪亮的声音主张说更好的方式是REST,表述性状态转移(REpresentational State Transfer)的简称。在本文中,我不会涉及争论的话题,而是尝试对REST和RESTful HTTP应用集成做实用性的介绍。以我的经验,有些话题一旦触及就会引来众多的讨论,当涉及到这方面话题的时候,我会深入详细地阐述。

REST关键原则

大部分对REST的介绍是以其正式的定义和背景作为开场的。但这儿且先按下不表,我先提出一个简单扼要的定义:REST定义了应该如何正确地使用 (这和大多数人的实际使用方式有很大不同)Web标准,例如HTTP和URI。如果你在设计应用程序时能坚持REST原则,那就预示着你将会得到一个使用 了优质Web架构(这将让你受益)的系统。总之,五条关键原则列举如下:

  • 为所有“事物”定义ID
  • 将所有事物链接在一起
  • 使用标准方法
  • 资源多重表述
  • 无状态通信

下面让我们进一步审视这些原则。

为所有“事物”定义ID

在这里我使用了“事物”来代替更正式准确的术语“资源”,因为一条如此简单的原则,不应该被淹没在术语当中。思考一下人们构建的系统,通常会找到一 系列值得被标识的关键抽象。每个事物都应该是可标识的,都应该拥有一个明显的ID——在Web中,代表ID的统一概念是:URI。URI构成了一个全局命 名空间,使用URI标识你的关键资源意味着它们获得了一个唯一、全局的ID。

对事物使用一致的命名规则(naming scheme)最主要的好处就是你不需要提出自己的规则——而是依靠某个已被定义,在全球范围中几乎完美运行,并且能被绝大多数人所理解的规则。想一下你 构建的上一个应用(假设它不是采用RESTful方式构建的)中的任意一个高级对象(high-level object),那就很有可能看到许多从使用唯一标识中受益的用例。比如,如果你的应用中包含一个对顾客的抽象,那么我可以相当肯定,用户会希望将一个指 向某个顾客的链接,能通过电子邮件发送到同事那里,或者加入到浏览器的书签中,甚至写到纸上。更透彻地讲:如果在一个类似于Amazon.com的在线商 城中,没有用唯一的ID(一个URI)标识它的每一件商品,可想而知这将是多么可怕的业务决策。

当面对这个原则时,许多人惊讶于这是否意味着需要直接向外界暴露数据库记录(或者数据库记录ID)——自从多年以来面向对象的实践告诫我们,要将持 久化的信息作为实现细节隐藏起来之后,哪怕是刚有点想法都常会使人惊恐。但是这条原则与隐藏实现细节两者之间并没有任何冲突:通常,值得被URI标识的事 物——资源——要比数据库记录抽象的多。例如,一个定单资源可以由定单项、地址以及许多其它方面(可能不希望作为单独标识的资源暴露出来)组成。标识所有 值得标识的事物,领会这个观念可以进一步引导你创造出在传统的应用程序设计中不常见的资源:一个流程或者流程步骤、一次销售、一次谈判、一份报价请求—— 这都是应该被标识的事物的示例。同样,这也会导致创建比非RESTful设计更多的持久化实体。

下面是一些你可能想到的URI的例子:

http://example.com/customers/1234
http://example.com/orders/2007/10/776654
http://example.com/products/4554
http://example.com/processes/salary-increase-234 

正如我选择了创建便于阅读的URI——这是个有用的观点,尽管不是RESTful设计所必须的——应该能十分容易地推测出URI的含义:它们明显地标识着单一“数据项”。但是再往下看:

http://example.com/orders/2007/11
http://example.com/products?color=green 

首先,这两个URI看起来与之前的稍有不同——毕竟,它们不是对一件事物的标识,而是对一类事物集合的标识(假定第一个URI标识了所有在2007年11月份提交的定单,第二个则是绿颜色产品的集合)。但是这些集合自身也是事物(资源),也应该被标识。

注意,使用唯一、全局统一的命名规则的好处,既适用于浏览器中的Web应用,也适用于机对机(machine-to-machine,m2m)通信。

来对第一个原则做下总结:使用URI标识所有值得标识的事物,特别是应用中提供的所有“高级”资源,无论这些资源代表单一数据项、数据项集合、虚拟亦或实际的对象还是计算结果等。

将所有事物链接在一起

接下来要讨论的原则有一个有点令人害怕的正式描述:“超媒体被当作应用状态引擎(Hypermedia as the engine of application state)”,有时简写为HATEOAS。(严格地说,这不是我说的。)这个描述的核心是超媒体概念,换句话说:是链接的思想。链接是我们在HTML中常见的概念,但是它的用处绝不局限于此(用于人们网络浏览)。考虑一下下面这个虚构的XML片段:


23


 

如果你观察文档中product和customer的链接,就可以很容易地想象到,应用程序(已经检索过文档)如何“跟随”链接检索更多的信息。当然,如果使用一个遵守专用命名规范的简单“id”属性作为链接,也是可行的——但是仅限于应用环境之内。使用URI表示链接的优雅之处在于,链接可以指向由不同应用、不同服务器甚至位于另一个大陆上的不同公司提供的资源——因为URI命名规范是全球标准,构成Web的所有资源都可以互联互通。

超媒体原则还有一个更重要的方面——应用“状态”。简而言之,实际上服务器端(如果你愿意,也可以叫服务提供者)为客户端(服务消费者)提供一组链 接,使客户端能通过链接将应用从一个状态改变为另一个状态。稍后我们会在另一篇文章中探究这个方面的影响;目前,只需要记住:链接是构成动态应用的非常有 效的方式。

对此原则总结如下:任何可能的情况下,使用链接指引可以被标识的事物(资源)。也正是超链接造就了现在的Web。

使用标准方法

在前两个原则的讨论中暗含着一个假设:接收URI的应用程序可以通过URI明确地一些有意义的事情。如果你在公共汽车上看到一个URI,你可以将它输入浏览器的地址栏中并回车——但是你的浏览器如何知道需要对这个URI做些什么呢?

它知道如何去处理URI的原因在于所有的资源都支持同样的接口,一套同样的方法(只要你乐意,也可以称为操作)集合。在HTTP中这被叫做动词 (verb),除了两个大家熟知的(GET和POST)之外,标准方法集合中还包含PUT、Delete、HEAD和OPTIONS。这些方法的含义连同 行为许诺都一起定义在HTTP规范之中。如果你是一名OO开发人员,就可以想象到RESTful HTTP方案中的所有资源都继承自类似于这样的一个类(采用类Java、C#的伪语法描述,请注意关键的方法):

class Resource {
Resource(URI u);
Response get();
Response post(Request r);
Response put(Request r);
Response delete();
} 

由于所有资源使用了同样的接口,你可以依此使用GET方法检索一个表述(representation)——也 就是对资源的描述。因为规范中定义了GET的语义,所以可以肯定当你调用它的时候不需要对后果负责——这就是为什么可以“安全”地调用此方法。GET方法 支持非常高效、成熟的缓存,所以在很多情况下,你甚至不需要向服务器发送请求。还可以肯定的是,GET方法具有幂等性[译 注:指多个相同请求返回相同的结果]——如果你发送了一个GET请求没有得到结果,你可能不知道原因是请求未能到达目的地,还是响应在反馈的途中丢失了。 幂等性保证了你可以简单地再发送一次请求解决问题。幂等性同样适用于PUT(基本的含义是“更新资源数据,如果资源不存在的话,则根据此URI创建一个新 的资源”)和Delete(你完全可以一遍又一遍地操作它,直到得出结论——删除不存在的东西没有任何问题)方法。POST方法,通常表示“创建一个新资 源”,也能被用于调用任过程,因而它既不安全也不具有幂等性。

如果你采用RESTful的方式暴露应用功能(如果你乐意,也可以称为服务功能),那这条原则和它的约束同样也适用于你。如果你已经习惯于另外的设计方式,则很难去接受这条原则——毕竟,你很可能认为你的应用包含了超出这些操作表达范围的逻辑。请允许我花费一些时间来让你相信不存在这样的情况。

来看下面这个简单的采购方案例子:

Sample Scenario

可以看到,例子中定义了两个服务程序(没有包含任何实现细节)。这些服务程序的接口都是为了完成任务(正是我们讨论的 orderManagement和CustomerManagement服务)而定制的。如果客户端程序试图使用这些服务,那它必须针对这些特定接口进行 编码——不可能在这些接口定义之前,使用客户程序去有目的地和接口协作。这些接口定义了服务程序的应用协议(application protocol)。

在RESTful HTTP方式中,你将通过组成HTTP应用协议的通用接口访问服务程序。你可能会想出像这样的方式:

Sample Scenario, done RESTfully

可以看到,服务程序中的特定操作被映射成为标准的HTTP方法——为了消除歧义,我创建了一组全新的资源。“这是骗人的把戏”,我听见你叫嚷着。 不,这不是欺骗。标识一个顾客的URI上的GET方法正好相当于getCustomerDetails操作。有人用三角形形象化地说明了这一点:

Knobs one can turn

把三个顶点想象为你可以调节的按钮。可以看到在第一种方法中,你拥有许多操作,许多种类的数据以及固定数量的“实例”(本质上和你拥有的服务程序数 量一致)。在第二种方法中,你拥有固定数量的操作,许多种类的数据和许多调用固定方法的对象。它的意义在于,证明了通过这两种方式,你基本上可以表示任何 你喜欢的事情。

为什么使用标准方法如此重要?从根本上说,它使你的应用成为Web的一部分——应用程序为Web变成Internet上最成功的应用所做的贡献,与 它添加到Web中的资源数量成比例。采用RESTful方式,一个应用可能会向Web中添加数以百万计的客户URI;如果采用CORBA技术并维持应用的 原有设计方式,那它的贡献大抵只是一个“端点(endpoint)”——就好比一个非常小的门,仅仅允许有钥匙的人进入其中的资源域。

统一接口也使得所有理解HTTP应用协议的组件能与你的应用交互。通用客户程序(generic client)就是从中受益的组件的例子,例如curl、wget、代理、缓存、HTTP服务器、网关还有Google、Yahoo!、MSN等等。

总结如下:为使客户端程序能与你的资源相互协作,资源应该正确地实现默认的应用协议(HTTP),也就是使用标准的GET、PUT、POST和Delete方法。

资源多重表述

到目前为止我们一直忽略了一个稍微复杂的问题:客户程序如何知道该怎样处理检索到的数据,比如作为GET或者POST请求的结果?原因是,HTTP 采取的方式是允许数据处理和操作调用之间关系分离的。换句话说,如果客户程序知道如何处理一种特定的数据格式,那就可以与所有提供这种表述格式的资源交 互。让我们再用一个例子来阐明这个观点。利用HTTP内容协商(content negotiation),客户程序可以请求一种特定格式的表述:

GET /customers/1234 HTTP/1.1
Host: example.com
Accept: application/vnd.mycompany.customer+xml  

请求的结果可能是一些由公司专有的XML格式表述的客户信息。假设客户程序发送另外一个不同的请求,就如下面这样:

GET /customers/1234 HTTP/1.1
Host: example.com
Accept: text/x-vcard 

结果则可能是VCard格式的客户地址。(在这里我没有展示响应的内容,在其HTTP Content-type头中应该包含着关于数据类型的元数据。)这说明为什么理想的情况下,资源表述应该采用标准格式——如果客户程序对HTTP应用协 议和一组数据格式都有所“了解”,那么它就可以用一种有意义的方式与世界上任意一个RESTful HTTP应用交互。 不幸的是,我们不可能拿到所有东西的标准格式,但是,或许我们可以想到在公司或者一些合作伙伴中使用标准格式来营造一个小环境。当然以上情况不仅适用于从 服务器端到客户端的数据,反之既然——倘若从客户端传来的数据符合应用协议,那么服务器端就可以使用特定的格式处理数据,而不去关心客户端的类型。

在实践中,资源多重表述还有着其它重要的好处:如果你为你的资源提供HTML和XML两种表述方式,那这些资源不仅可以被你的应用所用,还可以被任意标准Web浏览器所用——也就是说,你的应用信息可以被所有会使用Web的人获取到。

资源多重表述还有另外一种使用方式:你可以将应用的Web UI纳入到Web API中——毕竟,API的设计通常是由UI可以提供的功能驱动的,而UI也是通过API执行动作的。将这两个任务合二为一带来了令人惊讶的好处,这使得 使用者和调用程序都能得到更好的Web接口。

总结:针对不同的需求提供资源多重表述。

无状态通信

无状态通信是我要讲到的最后一个原则。首先,需要着重强调的是,虽然REST包含无状态性(statelessness)的观念,但这并不是说暴露功能的应用不能有状态——
事 实上,在大部分情况下这会导致整个做法没有任何用处。REST要求状态要么被放入资源状态中,要么保存在客户端上。或者换句话说,服务器端不能保持除了单 次请求之外的,任何与其通信的客户端的通信状态。这样做的最直接的理由就是可伸缩性—— 如果服务器需要保持客户端状态,那么大量的客户端交互会严重影响服务器的内存可用空间(footprint)。(注意,要做到无状态通信往往需要需要一些 重新设计——不能简单地将一些session状态绑缚在URI上,然后就宣称这个应用是RESTful。)

但除此以外,其它方面可能显得更为重要:无状态约束使服务器的变化对客户端是不可见的,因为在两次连续的请求中,客户端并不依赖于同一台服务器。一 个客户端从某台服务器上收到一份包含链接的文档,当它要做一些处理时,这台服务器宕掉了,可能是硬盘坏掉而被拿去修理,可能是软件需要升级重启——如果这 个客户端访问了从这台服务器接收的链接,它不会察觉到后台的服务器已经改变了。

理论上的REST

我承认:以上我所说的REST不是真正的REST,而且我可能有点过多地热衷于简单化。但因为我想有一个与众不同的开场,所以没有在一开始就介绍其正式的定义和背景。现在就让我们稍微简要地介绍一下这方面的内容。

首先,先前我并没有明确地区分HTTP、RESTful HTTP和REST。要理解这些不同方面之间的关系,我们要先来看看REST的历史。

Roy T. Fielding在他的博士学位论文(实际上你应该访问这个链接——至少对于一篇学术论文来说,它是相当易读的。此论文已被翻译成中文) 中定义了术语REST。Roy曾是许多基本Web协议的主要设计者,其中包括HTTP和URIs,并且他在论文中对这些协议提出了很多想法。(这篇论文被 誉为“REST圣经”,这是恰当的——毕竟,是作者发明了这个术语,所以在定义上,他写的任何内容都被认为是权威的。)在论文中,Roy首先定义一种方法 论来谈论架构风格——高级、抽象的模式,来表达架构方法背后的核心理念。每一个架构风格由一系列的约束(constraints)定义形成。架构风格的例子包括“没有风格”(根本没有任何约束)、管道和过滤器(pipe and filter)、客户端/服务器、分布式对象以及——你猜到它了——REST。

如果对你来说这些听起来都太抽象了,那就对了——REST在本质上是一个可以被许多不同技术实现的高层次的风格,而且可以被实例化——通过为它的抽 象特性赋上不同的值。比如,REST中包含资源和统一接口的概念——也就是说,所有资源都应该对这些相同的方法作出反应。但是REST并没有说明是哪些方 法,或者有多少方法。

REST风格的一个“化身”便是HTTP(以及一套相关的一套标准,比如URI),或者稍微抽象一些:Web架构自身。接着上面的例子,HTTP使 用HTTP动词作为REST统一接口的“实例”。由于Fielding是在Web已经(或者至少是大部分)“完善”了之后才定义的REST风格,有人可能 会争论两者是不是100%的匹配。但是无论如何,整体上来说Web、HTTP和URI仅仅是REST风格的一个主要实现。不过,由于Roy Fielding即是REST论文的作者,又对Web架构设计有过深远的影响,两者相似也在情理之中。

最后,我在前面一次又一次地使用着术语“RESTful HTTP”,原因很简单:许多使用HTTP的应用因为一些理由并没有遵循REST原则,有人会说使用HTTP而不遵循REST原则就等同于滥用HTTP。 当然这听起来有点狂热——事实上违反REST约束的原因通常是,仅仅因为每个约束带来的设计权衡可能不适合于一些特殊情况。但通常,违背REST约束的原 因可归咎于对其好处认知的缺乏。来看一个明显的反面案例:使用HTTP GET调用类似于删除对象的操作,这违反了REST的安全约束和一般性常识(客户程序不应为此负责,服务器端开发人员大概不是有意而为之)。但在随后的文 章中,我会提及更多这样或那样的对HTTP的滥用。

总结

本文试图对REST(Web架构)背后的概念提供快速的介绍。RESTful HTTP暴露功能的方式与RPC、分布式对象以及Web Services是不相同的;要真正理解这些不同是需要一些心态的转变。不管你构建的应用是仅仅想暴露Web UI还是想把API变成Web的一份子,了解下REST的原则还是有好处的。

Stefan Tilkov是InfoQ SOA社区的首席编辑,并且是位于德国和瑞士的innoQ公司的共同创始人、首席顾问和REST狂热分子首领。

查看英文原文A Brief Introduction to REST

[REST]什么是REST

mikel阅读(780)

什么是REST?
  REST是Roy Fielding在他的博士论文中提出的词汇,是对网络系统构架的一个描述。REST是表现性状态传输首字母缩写(Representational State Transfer)。
Roy Feilding这么解释表现性状态传输的意义:

引用 “表现性状态传输试图描述一个设计优良的Web是如何运转的:用户在web网络(虚拟状态机)里通过点击链接处理应用(状态传输),结果就是传输并渲染下一个页面给用户(应用程序的下一个状态的表现性)。“

REST的目的
  REST的目的是寻找让Web如此成功的特性。然后再使用这些特性指导Web变革。
REST是一个结构设计,而不是一个标准
   REST不是一个标准,你永远也不会看到W3C发布REST白皮书。你也不会看到微软或者IBM销售REST开发工具。为什么?REST它仅仅是一个结 构设计而不是一个标准。你不能控制这个设计,你仅能够理解并且应用在你的Web服务中。(类似的C/S结构也是一种结构设计,而不是一个C/S标准)。
虽然REST不是一个标准,但它确实使用标准:
HTTP
URL
XML/HTML/GIF/JPEG等等(状态表现)
text/xml, text/html, image/gif, image/jpeg等等(MIME类型)
经典的REST系统
  Web就是REST系统,有很多你使用了很多年的服务就是基于REST的Web服务,例如:订书服务、搜索服务、在线电子字典。
  REST是关于Web的一张“大蓝图”,它不负责处理具体的执行细节(例如:使用Java还是CGI来实现一个Web服务)。所以让我们用REST“大蓝图”的观点来设计一个Web服务。
REST Web服务的特征
C/S结构:一个完全基于交互的设计
无状态:每个发送给服务器的信息必须包含服务器处理所需的所有信息,不能通过在服务器保存相关信息而获益。
缓存:为了提供网络的效率,响应必须标志出是否可以缓存。
统一的接口:所有的资源都可以通过一个通用的接口访问(例如:HTTP GET、POST、PUT、Delete)。
命名的资源:组成系统的资源使用URL来命名。
互联资源表现性:资源的表现性通过URL实现互联,因此客户可以一个状态接着一个状态处理。
组件层:可以在客户和资源间插入用于提高效率,增强安全的的中间件,象代理服务、缓存服务、网关等。
REST Web服务设计原则
1.以REST网络建立Web服务的关键(例如Web)是确定想作为服务展现的所有概念化的实体。
2.为每个资源提供URL。资源应该是名词,不是动词
3.根据客户处理资源的方式分类,可以分为客户仅接收资源的表现性,客户能够修改(增加)资源。对于前者,提供HTTP GET方法访问。对于后者,提供HTTP POST、PUT和(或)Delete。

我们在 Web 应用中处理来自客户端的请求时,通常只考虑 GET 和 POST 这两种 HTTP 请求方法。实际上,HTTP 还有 HEAD、PUT、Delete 等请求方法。而在 REST 架构中,用不同的 HTTP 请求方法来处理对资源的 CRUD(创建、读取、更新和删除)操作:

  • POST: 创建
  • GET: 读取
  • PUT: 更新
  • Delete: 删除

经过这样的一番扩展,我们对一个资源的 CRUD 操作就可以通过同一个 URI 完成了:

http://www.example.com/photo/logo(读取)
仍然保持为 [GET] http://www.example.com/photo/logo

http://www.example.com/photo/logo/create(创建)
改为 [POST] http://www.example.com/photo/logo

http://www.example.com/photo/logo/update(更新)
改为 [PUT] http://www.example.com/photo/logo

http://www.example.com/photo/logo/delete(删除)
改为 [Delete] http://www.example.com/photo/logo

从而进一步规范了资源标识的使用。

通过 REST 架构,Web 应用程序可以用一致的接口(URI)暴露资源给外部世界,并提供对资源的操作服务。这对于以资源为中心的 Web 应用来说非常重要。例如照片共享网站、用户社区等。

 

 

4.所有通过HTTP GET访问的资源应该是无副作用的,也就是说,这些资源仅仅向客户返回一个资源的表现性,客户调用他们不应该对它们产生影响。
5.没有孤立的人,同样,没有孤立的表现性。换言之,在资源的表现性中提供链接,让客户能更深入的获取更多的信息,或者相关信息。
6.逐步的提供数据,不应该在一个文档里提供所有的内容,可以为更详细的信息提供链接。
7.使用模式(DTD、W3C Schema、RelaxNG或者Schematron)指定响应数据的格式。如果服务需要POST或PUT,也同样使用一个模式规定这些响应。
8.通过WSDL文档或者HTML页面描述如何调用你的服务。

(转载)

[REST]REST构架风格介绍之一:状态表述转移

mikel阅读(855)

转载:http://www.cnblogs.com/weidagang2046/archive/2009/05/08/1452322.html

REST(Representational State Transfer)HTTP协议的作者Roy Fielding博士在其博士论文中提出的一种互联网应用构架风格。与以远程对象为核心的ORB和以服务为核心的SOA相比,以资源为核心的REST让我们从崭新的视角审视互联网应用。REST为互联网应用量身定做的简洁模型和其高扩展性,为互联网应用构架设计和异构系统集成设计带来了一股清新的空气。

本文希望与大家分享REST风格构架设计的体会,错误不足之处欢迎批评指正。

语言生态环境

计算机发展至今,产生了许许多多不同的语言,每种语言都定义了自己独特的生态环 境。在这个生态环境内,所有程序共享相同的类型系统、运行时环境、并发模型等。虽然所有程序的本质是相同的:从问题领域到机器领域的映射,但无法回避的是 不同生态环境的程序很难跨越彼此的边界。同样是int,在AB语言通常截然不同(CLRJVM能部分解决类型共享问题),更不用说A语言具有但B语言不具有的某些语言特性(CLRJVM没法解决)

当系统可以在单一的生态环境中自给自足时,跨越生态环境的问题并不存在;但在多数互联网应用中,系统的各个部分通常既是生产者又是消费者,必须要打破生态环境的界限才能相互协作。比如,A公司的Service A,需要对外提供服务,而Service A又依赖于B公司的Service BC公司的Service C;由于无法保证不同公司都采用同样的语言,因此各服务的接口必须保证语言无关性。在我所了解的范围内,有3种跨域生态环境的方式:

1.      ORB(Object Request Broker)

CORBA为代表,其核心概念是远程对象(remote object)。熟悉.Net Remoting的朋友应该能体会其风格(需要说明的是.Net Remoting只跨越微软的生态环境)。不同生态环境的程序可以像调用本地对象一样调用远程对象代理的方法,ORB会负责连接到远程的对象,并处理数据的序列化与反序列化。

2.      SOA

其核心概念是服务(Service)。比如:我们要提供整数加法Web服务,我们会很自然地想到通过类似下面的url来表达服务接口:

http://www.example.com/add?a=1&b=2

并通过xml结构表达结果:

<sum>3</sum>

3.      REST

其核心概念是资源(Resource)。在REST的世界中,没有服务的概念,同样是上面的例子,在REST的世界中,http://www.example.com/add?a=1&b=2是一个xml网页资源的id,而非服务的接口。所以,REST让我们从资源的角度来审视互联网应用并指导我们的设计,这是它与ORB和SOA最本质的区别。下面我们将更详细的介绍,REST以资源为核心的模型和相应的设计风格。

状态表述转移

REST的世界中,互联网就是一个巨大的状态机:每个网页是其一个状态(资源);url则是状态的表述。互联网应用是地从一个状态迁移到下一个状态的状态转移过程。这个状态表述转移过程正是互联网形成初期通过超链接在静态网页间浏览跳转的page->link->page->link…模式。REST风格应用可以实现交互,但它却天然地具有服务器无状态的特征。PS:更准确地说,除资源本身是状态以外,服务器不需要保存会话状态,本文提到的无状态服务均指无会话状态服务。在状态迁移的过程中,服务器不需要记录任何Session,所有的状态都通过url的形式记录在了客户端。

举个例子:一个心理测试的应用,需要用户做2次选择题,每次可选AB两种答案,2次选择完毕之后将告知用户属于何种心理类型。

如果按SOA的服务思维,很容易想到在服务器端保存Session,每次选择以后修改Session。但如果按REST的状态转移思维方式,我们会得出这样设计:

每一个页面表示一个状态(存在于客户端),页面包含了到下一个页面的超链接,当用户选a和选b时分别转移到相应的状态。这样,所有的会话状态其实都是通过url的形式保存在了客户端,服务器端实现了无状态模式。需要说明的是,虽然上图有7个状态,但并非一定需要在服务器预先生成7个静态页面,它们完全可以是动态页面,这不影响状态转移的概念模型以及服务器无状态的特征。

有构架设计经验的朋友应该很清楚,与有状态服务设计相比,无状态服务容易实现系统性能的横向扩展。通过增加硬件,部署多个无状态服务,并进行load balance不会受到制约;而有状态服务模式,Session的存储、共享都会带来性能瓶颈,且无法通过增加硬件消除。

Google搜索就是一个典型的无状态服务。试想一下,当你搜索“周杰伦”以后,Google提示你有数百万的结果,并每10条一页分成若干 页,Google会把结果保存进服务器Session吗,然后当你翻页的时候,再从Session中取吗?显然这样庞大的Session,即使是 Google也无法承受。看看Google的url就清楚了:

第一页:http://www.google.cn/search?q=%E5%91%A8%E6%9D%B0%E4%BC%A6&hl=zh-CN&newwindow=1&start=0&sa=N 

第二页:http://www.google.cn/search?q=%E5%91%A8%E6%9D%B0%E4%BC%A6&hl=zh-CN&newwindow=1&start=10&sa=N

当你点击第二页的链接时,无非是从一个状态跳到了下一个状态而已。对于Google而言,其实是一条新的查询,而两次查询很可能是由不同的服务器在处理,而用户却感觉Google记住了会话。

最后,需要说明的是,REST风格包含了无状态服务的特征,但并非具有无状态服务特征的都是REST。SOA同样可以是无状态的,REST的核心还是资源,下一节将会继续就这个话题进行探讨,谢谢关注!