[转载]REST on ASP.Net MVC (and Future) – chelsea – 博客园.
See also: <<Enterprise REST = Customize, Invent and Standardize Media Types>>
MVC和REST是两种不同的世界观. 前者更多的是对行为的建模,后者则更强调数据(状态及状态的变迁). 前者给出的是内部实现方面的指导, 把程序结构分离为Model, View及Controller. 后者提供的则是从外部观察系统的视角: 一组被超媒体诱导的系统变迁.
MVC和REST从两种不同的维度来描述世界, 而不是同一纬度上两种相反的观点, 因此基于现存的大量MVC框架来开发符合REST要求的应用是可能的. 但工具是世界观的反映, MVC框架对REST的支持总不是那么直接. 我们在使用MVC框架来开发REST应用时,要注意避开可能的冲突, 而专注于那些符合REST要求的特性. 下面我们来看看ASP.NET MVC对REST的支持
Client–server
客户-服务器约束背后的原则是分离关注点, 比如用户接口和数据存储. 对于Web来说, 最重要的是这种关注点的分离允许组件独立地进化.
ASP.NET MVC 是server端开发框架, 在这个约束上它没有贡献也没什么过失. 它产生符合标准的HTML代码, 对客户端的能力没什么特别的要求.
Stateless
ASP.NET MVC提供了Session的概念. 可以把它理解成server端保存的针对某个特定客户端的状态. 使用这个特性将会违反Stateless的约束, 造成可伸缩性的下降. 开发者若要确保不会无意中破坏这个约束, 可以通过一系列配置来禁用session, 比如在Web.config加上<sessionState mode=”Off” />, 或者通过代码来对单个Controller进行配置:
ASP.NET MVC也提供了对Cookie的支持, 而Cookie是违反REST约束的一种特性. “Cookie被定义为被附加在任何将来的请求上, 对于特定的一组资源标识符, Cookie通常是与一个完整的站点相关联, 而不是与浏览器中特定的应用状态(当前呈现的表述的那一组状态)相关联. 并且它允许数据在没有充分表明其语义的情况下进行传递”. ASP.Net提供了cookieless的选项, 使我们可以关闭对cookie的支持.
Cacheable
ASP.NET MVC提供了一个叫做 OutputCache 的ActionFilter来支持缓存. OutputCache 有点过于强大, 它不仅支持客户端与服务器之间的缓存, 还支持服务端应用代码与数据库之间的缓存. 还可以设定缓存的位置, 比如是放在客户端浏览器(即在Response中加上标准的Http Cache Control Headers)还是放在服务端. 除了支持整页缓存, 它还支持部分页面缓存(我理解这时它就只能缓存在服务端了)
当大量独立的客户端请求同样的内容时, 比如同一幅图片或视频, 服务端缓存要比客户端缓存更有效. 然而针对这个问题, 有一种叫做反向代理的组件, 可以将这部分职责从服务端应用代码中剥离出来, 把它变成Web基础设施的一部分, 前提是服务端要设置标准的那些Http Cache Control Headers. 在此情况下, 这类缓存通常被称为共享缓存. 后面谈到分层的时候我们会再提一下这个问题.
所以对REST应用来讲, 个人感觉还是优先应用客户端缓存, 这样可以最大程度的利用现有的各种Web基础设施.
Layered system
跟Client-server约束类似, ASP.Net MVC在此无功无过. 但是一些特性会导致不能完全发挥那些中间层的作用.
比如说Session对Load Balancer的影响. 如果服务端将session存在同一应用进程如w3wp.exe的内存中, 则Load balancer将不得配置 Session Sticky 的策略, 从而不能做到真正的load balancing
再比如服务端缓存将使反向代理发挥不出应有的作用.
Code on demand (optional)
.Net 平台上native的Mobile Code Style的支持, 就是Silverlight, 但ASP.Net MVC对此并没有提供任何支持, 比如并没有一种基于Silverlight的View Engine.
对于JavaScript, ASP.Net MVC提供的支持也有限, 只是提供了一些辅助性的代码, 可以在View中产生一些AJAX Call的代码, 比如AjaxHelper.ActionLink, AjaxHelper.BeginForm等
Server端也提供了一种叫 JsonResult 的Representation, 可供客户端JavaScript方便的操纵. 但这更多的是对多种Representation的支持, 后面会提到.
Uniform interface
“使REST架构风格区别于其他基于网络的架构风格的核心特征是,它强调组件之间要有一个统一的接口. 通过在组件接口上应用通用性的软件工程原则, 整体的系统架构得到了简化, 交互的可见性也得到了改善. 实现与它们所提供的服务是解耦的, 这促进了独立的可进化性. 然而, 付出的代价是, 统一接口降低了效率, 因为信息都使用标准化的形式来转移, 而不能使用特定于应用的需求的形式. REST接口被设计为可以高效地转移大粒 度的超媒体数据, 并针对Web的常见情况做了优化, 但是这也导致了该接口对于其他形式的架构交互并不是最优的
为了获得统一的接口, 需要有多个架构约束来指导组件的行为. REST由四个接口约束来定义: 资源的识别(identification of resources), 通过表述对资源执行的操作(Manipulation of resources through these representations), 自描述的消息(self-descriptive messages), 以及作为应用状态引擎的超媒体(Hypermedia as the engine of application state)” — from <<REST_cn >>
Identification of resources
资源的识别或标识是一种概念或语义上的抽象, 它明确的区分了资源本身的物理实现和返回给客户端的资源的表述. 如何从概念上为一个资源定义一个标识是开发者的责任, ASP.Net MVC帮不上任何忙. ASP.NET MVC能做的, 只是帮忙将资源的标识映射到资源的实现(这是通过UrlRoutingModule来实现的). 换句话说, 一个URL是否是 RESTful的, 不在于你是否通过高超的路由技术让它看起来很干净没参数, 而在于它本身是否是一个永久性的概念, 是否可以更改底层实现而无需调整.
由于同一个URL上可以进行多种标准的HTTP操作, 比如POST, PUT, GET, DELETE等, 一个好的路由映射应该能够将不同的HTTP操作映射到不同的对象或方法上. ASP.NET MVC提供了这种支持:
Manipulation of resources through these representations
在ASP.Net MVC中, 资源的Representation是通过一个叫做ActionResult的概念来表达的. 比如产生HTML的 ViewResult, 产生Json的 JsonResult, 产生JavaScript的JavaScriptResult等. 当我们需要定义自己的媒体类型的时候, 扩展ActionResult就可以了.
Self-descriptive messages
通常来讲, 这里指的是Http Header中的各种信息的存取. ASP.Net MVC通过HttpRequest和HttpResponse两个class提供了对元数据的支持. 当然, 解析这些数据, 理解其含义, 遵循其指示, 并设置合适的响应, 还是开发者的责任.
Hypermedia as the engine of application state
终于到了REST最核心的特性, ASP.Net MVC并没有有意识的提供支持. 不奇怪, 首先MVC更倾向于一种RPC的设计思路而不是REST, 其次这个约束还是更多的靠开发者来实现而不是什么神奇的框架.
HTML是ASP.Net MVC支持的唯一一种Hypermedia. 在一个叫做HtmlHelper的类中, 提供了大量方法来方便的产生合法的HTML, 以及最重要的, 到其它资源的link, 比如HtmlHelper.ActionLink(…)
到其它资源的Link是Hypermedia的核心. 它可以被用在非HTML媒体类型中, 比如ATOM Feed. ASP.Net MVC提供了UrlHelper来产生各种link.
ASP.Net MVC Future
ASP.Net MVC Future 包含了一组ASP.NET MVC team认为将来可能会加到ASP.Net MVC正式Release中的特性. 在目前的版本中(ASP.Net MVC 3 RTM), ASP.Net MVC Future加入了更多对REST的支持.
更多的超媒体类型比如Atom Feed : AtomEntryActionResult和AtomFeedActionResult两个类为Atom提供了支持. 这两个类其实很简单就是设置了一下Content-type, 真正核心的功能都委托给了SyndicationFeed和SyndicationItem. 后者是.Net Framework提供的.
JSON和Xml的Model Binder : 可以支持将请求中Json和Xml类型的负载直接绑定为Action以对象形式定义的参数.
流行的”RESTful”的路由 : 这是通过RouteCollectionExtensions和ResourceControllerFactory实现的. 比如前者可以帮你建立如下的路由:
然而对于一个很多开发者期待的特性, 就是根据HttpRequest Header中Accept的值进行路由, 无论是ASP.Net MVC还是Future, 都没有提供. 比如目前并不支持如下的写法:
See also: <<Enterprise REST = Customize, Invent and Standardize Media Types>>