无状态的体系结构
HTTP 协议本身就被设计成无状态的,采用请求 / 响应的模式。不同的请求之间并不存在相互关系。但是这种架构模式在开发某些 Web 应用的时候不是很方便。有些应用要求用户进行认证登录之后才能进行某些操作。同样的 URL,认证和未认证用户看到的内容是不同的。而且用户认证成功之后,他应该在一段时间内保持这种认证状态。否则的话,用户每次都需要输入用户名和密码才能访问受限的内容。对于这种情况,很多 Web 开发框架提供了会话的支持,允许应用保存一些与会话相关的数据。Java Servlet 规范中的 javax.servlet.http. HttpSession 就是一种会话的接口。应用的服务器会负责维护每个会话相关的数据。这些数据可以通过一个会话 ID 来进行标识。这个标识会利用浏览器的 cookie 机制保存在浏览器端,也可以作为请求 URL 的参数来传递。服务器端通过此标识来识别每个会话。在处理相应的请求的时候,就可以根据会话 ID 来获取保存在服务器端上的会话数据。会话机制的问题是会影响应用的可伸缩性。如果一个应用使用多台服务器的话,就需要额外的机制来保证同一用户在不同机器上面的会话是同步的。而无状态的实现则不存在这个问题,对于某一个请求,由不同机器来处理的结果都是相同的。
Play 框架的设计架构就是无状态的。它没有提供服务器端的机制用来维护跨多个请求的数据。如果确实需要保存这样的数据的话,可以考虑下面几种方案:
保存在 Session 或 Flash 作用域中。Play 框架中仍然有会话的机制,但是并没有提供在服务器端保存会话数据的能力。会话数据是保存在浏览器的 cookie 中的,由浏览器在每次请求的时候自动发送。通过这种方式来达到维护会话数据的目的。由于会话数据是保存在 cookie 中,其大小是有限制的,一般不能超过 4K 字节,而且只能保存字符串类型的数据。Flash 作用域和会话一样,也是通过 cookie 来保存的。所不同的是,Flash 作用域中的数据只在下次请求中是有效的。
保存在持久化的数据存储中,如数据库中。如果需要在多个请求中使用同一个领域对象的话,可以把这个对象的 ID 保存在 Session 或 Flash 作用域中,而在控制器动作方法中使用此 ID 来从数据库中查询相应的对象。
保存在暂时性数据存储中,如缓存中。Play 框架内置了缓存的支持,通过调用类 play.cache.Cache 就可以对缓存进行操作。与使用持久化存储类似,缓存中的键的值可以保存在 Session 或 Flash 作用域中。
对于熟悉了 Java Servlet 规范的开发人员来说,需要一些时间来适应 Play 框架的这种无状态的体系结构。不过这种结构对于应用的可伸缩性来说,确实是非常有好处的。
介绍完无状态的体系结构之后,下面介绍一些其它话题。
其它话题
测试
Play 框架对应用的测试也提供了良好的支持。Play 框架一共支持三种类型的测试,分别是单元测试、功能测试和界面测试。单元测试主要用来测试应用的模型层代码。单元测试用例的 Java 类继承自 play.test.UnitTest ,可以使用 JUnit 4 提供的标注和断言。功能测试主要用来测试应用的控制层代码。功能测试用例的 Java 类继承自 play.test.FunctionalTest。在测试用例中,可以通过 GET()、POST()、PUT()、DELETE() 和 makeRequest() 等方法来发出 HTTP 请求,也可以直接调用控制器中的动作方法。除此之外,还可以使用一些与 HTTP 响应相关的断言。如 assertStatus()、assertContentType() 和 assertHeaderEquals() 分别用来验证 HTTP 状态代码、内容类型和 HTTP 头。界面测试使用 Selenium 工具来进行。开发人员可以使用 Selenium 的语法来编写测试用例,也可以使用 Play 框架提供的 #{selenium} 标签。
在进行测试的时候,需要准备一些测试数据。测试数据可以用 YAML 的格式保存在文本文件中,并通过 play.test.Fixtures.laod() 方法来加载这些数据到数据库中。当测试结束之后,可以通过 deleteAll() 方法来删除这些数据。
在测试的时候,需要用 play test命令以测试模式启动应用,再用浏览器访问 http://localhost:9000/@tests 进行测试。