HTML clipboard
其它所有问题都是这个问题的某种解决方案引入的. 这个论断有一个推论, 就是不需要识别用户的Web应用是最简单的Web应用.
一种方案是所有的业务都设计成一次请求就可以完成, 比如让用户每次都输入用户名密码, 填写所有的表单, 然后一次性提交. 从用户体验的角度显然不可行...
状态保存
需要保存状态的原因是要在无连接的协议上模拟有连接的用户体验
状态保存的地点无非是客户端或者服务端, 以及URL本身
客户端一般是cookie, 类似Hash的键值对. 存放小块数据, 通常是真正状态的某种标识, 可加密, 可设置有效期及适用的域, 由浏览器每次请求时发送给服务端. cookie的问题在于, 有些用户在浏览器中禁止了对cookie的支持, 然而实际上有什么类型的网站需要考虑/考虑了这类用户? 以及安全性(可被拷贝偷走放在自己机器上以假冒其他用户用). 可对cookie加密或摘要来保证数据的机密性和完整性
服务端存储有内存和外部存储. 一般把跨越多次请求维护特定客户端的状态数据的场所叫做Session. Session并不是HTTP本身的概念, 而是各开发包实现者提供的概念和实现, 通过唯一的session id(通常通过cookie保存)在不同的请求间关联数据.
Session放在内存中的问题是无法适应负载平衡和可靠性的需求, 优点是性能; 外部存储恰好相反; 各种不同的开发包对Session的实现一般都支持存储可配置, 对应用"透明"(实际上无法完全透明, 比如如果需要持久化Session到外部存储, 则需要内存对象支持某种形式的可序列化, 而如果只是放在内存里则无此需求)
URL本身可携带数据, 问题是应用程序开发者需要仔细维护URL的产生和解析. 开发包提供了支持, 可定义URL到对应处理程序的映射, 解码URL中携带的数据并作为参数传递给处理程序.
URL和cookie都适合存放小片数据. 那么什么类型的数据适合放进URL, 什么数据适合放进cookie呢? 从URL的语义上来说, 编码进URL的数据应该是所请求资源的某种属性, 而cookie一般存放用户的某种标识数据. 也就是URL应该是业务相关数据, cookie应该是应用相关数据. 当浏览器禁用cookie时, 开发包支持把session id之类的数据编码进URL.
Session存放需要频繁用到的数据, 用户相关而业务无关的. 关键的业务数据要及时持久化到外部存储. 业务相关数据一般按需存取...
状态清除
状态清除是由状态保存引入的问题, 确切的说是对保存状态的资源的回收和重利用. 另外是减少安全性方面的隐患.
客户端cookie有过期时间, 也可在用户登出的时候主动设置其过期来删除
服务端Session可在用户登出的时候主动删除, 开发包也会检测同样的Session ID访问间隔, 如果超过某个设定的时间没有包含某个Session ID的cookie被送到服务端, 则自动清除对应Session数据. 这时候表现为如果用户长时间不操作, 下次操作时将被开发包重定向到登录页面.
业务数据的传递
数据的输入和处理发生在不同的主机上, 是Web应用的天然特质. HTML的Form和隐藏字段, HTTP Post专为此设计. 另外URL本身也可以承载一些业务数据(HTTP Get)
跨页面的业务数据传递, 一般通过持久化到外部存储的方式来共享. 应用数据则可以通过Session, 一些细分的概念如临时数据, 只用一次的数据 等, 开发包都有支持, 一般也是背后通过Session实现
HTTP的无连接带来的第二个问题是服务端无法主动把信息推给客户端, 另一个原因是客户端通常没有public的IP. 客户端只能使用按需请求或轮询模型. 此题无解. 如果是企业内部系统集成, 考虑使用消息队列等方式.
并发是要处理的第三个本质问题. 对并发(背后是状态一致性与完整性)的解决会带来事务, 事务会带来性能问题, 对性能的解决会带来Cache等方案, Cache则会引入状态一致性与完整性问题...回到起点.
最终是状态的一致性与完整性和性能之间的某种折衷.
Cache的一种实现是将动态页面转化为一段时间内的静态页面, 交给Web服务器直接处理, 减轻应用服务器的负担.
应用服务器内的Cache一般是key value pair. key一般是url, 查询语句, 对象ID等.
Cache的实现要考虑的问题主要是Cache的生命周期, 是请求内, 会话内, 还是应用程序范围. 以及同步问题, 何时刷新, 如何刷新.
HTTP Request范围内的Cache, 和通常的事务策略配合良好. 事务通常不会超出一次请求, 隔离级别是可读到其它事务提交后的内容, 不会读到其它事务提交前的改动(?). 这类Cache边界清晰, 几乎不会有失效刷新的需求, 基本不需要考虑多线程的问题(每个请求一般独占一个处理线程). 缺点是对性能提高有限, 如果一次请求范围内对同一资源的访问很少超过一次的话.
Session范围内的Cache...什么类型的数据适合放到Session级别的Cache呢? Session通常包含多个事务, 如果放业务数据到Cache, 毫无疑问要经常刷新, 失去Cache的意义. 刷新时有多线程的问题, 尤其对AJAX应用. 如果是不经常变化的应用数据, 通常放到应用程序级别的Cache. Session内的数据基本可看作已经是某种缓存...
Application范围内的数据, 一般存放不经常变化的配置数据, 数据字典等. 刷新可以是定时轮询, 及各种基于事件的手段: HTTP Get/Post, 消息队列等
这里说的验证是Validation, 不是Authentication. Authentication/Authorization并非Web特定问题, 其它类型的应用面临同样的问题, 有相似的解决方案
Validation则是Web相关, 盖因输入与处理发生在不同的主机上, 验证逻辑发生的位置和时机都是需要考虑的因素
一条底线是在数据被正式接受之前, 比如存到数据库之前, 用来进行业务计算之前, 进行验证, 以确保数据的合法性. 这在大多数情况下都是必要的. 然而从用户体验的角度讲, 时间有点晚, 并且验证产生的错误信息可能更偏向于实现, 而不是用户能理解的业务含义, 并不必要的向用户暴露实现细节. 所以需要其它的验证手段作为补充
一种方式是在客户端用JavaScript进行验证, 不必让数据到服务器走一个来回, 带来的问题是部分验证逻辑在客户端和服务端用不同的语言写两遍.
一个改进是利用AJAX即时验证, 既改善了用户体验, 又避免了用JavaScript重写验证逻辑
即时的验证一般关注于基础合法性的验证, 比如格式, 必填字段等. 数据的业务合法性则一般在提交后由服务端进行全面验证
开发包对Web问题的各种解决方案进行了封装, 分离关注点, 使开发人员更关注业务逻辑, 开发包处理了所有与Web相关的应用逻辑
为使这一切对开发者透明, 结合Web应用的Request/Response模型, 开发包一般采用管道和过滤器模式来动态的,可配置的将各种服务插入到数据的处理流程中.
比如Session的管理, 包括通过cookie识别特定客户端和清除过期数据等逻辑, 都透明的发生在请求经过的路径上. Session数据本身的存储位置, 也都是可配置的. 其它的例子包括页面缓存和用户验证等.
封装HTTP协议字节流到特定开发平台的对象, 通常是能以key-value pair的形式访问的接口. 更有细粒度的对象如Cookie等概念的封装
封装URL到对应处理程序的映射, 包括组装URL, 将URL参数解析成具有业务含义的数据. 开发者可定制映射规则
封装Form/Request数据到业务对象的转换, 通常称之为Binder
通过MVC分离视图逻辑和业务逻辑, 并且通过模板视图技术支持UI开发者和业务开发者的分工协作
提供了过滤器接口, 可以自定义验证, 授权, 错误处理等逻辑