#Actions,contorllers,和Results的概念。
1.Action
Action的概念很熟悉吧,Play应用中的大部分请求都有Action来处理。Action可以看作是一个处理请求数据并产生一个结果放回给客户端的简单Java方法。
Action返回play.mvc.Result,代表Http响应发送到客户端,在例子中,ok()构建了一个200状态的相应,它包含一个text/plain的响应体。
2.Controller
Controller是继承自play.mvc.Contorller的类,它将若干Action方法组合在一起。
下面是两个简单的Action例子。
public static Result index() { return ok("It works!"); } public static Result index(String name) { return ok("Hello" + name); }
3.Results
Results即结果。最简单的莫过于包含一个状态码、一组HTTP头和一个Http体的被发送到web客户端的Http Resul。
这些Results被play.mvc.Result定义,play.mvc.Results 类提供了一些帮助产生标准Http Results的方法,例如我们用到的ok()方法。
下面的代码示例创建了各种Results:
Result ok = ok("Hello world!"); Result notFound = notFound(); Result pageNotFound = notFound("<h1>Page not found</h1>").as("text/html"); Result badRequest = badRequest(views.html.form.render(formWithErrors)); Result oops = internalServerError("Oops"); Result anyStatus = status(488, "Strange response type");
这些方法都可以在play.mvc.Results中找到。
重定向同样是简单的Results,把浏览器重定向到一个新的URL也不过是一种简单的result,不同的是这些result 类型没有响应体。
如:
public static Result index() { return redirect("/user/home"); }
#Http 路由
1.内置的Http路由
路由是将每一个进来的Http请求转换成一个Action调用(action是contoller类里的静态的、公共的方法)的组件。
一个Http请求在MVC框架中被视为一个事件,这一事件包含了两个主要的信息块:
请求路径和Http方法(get,post...)
路由在conf/routes文件中被定义,并且会被编译,也就是说你能直接在浏览器中看到路由错误。
2.路由的配置语法
conf/routes是路由的配置文件,这个文件列出了所有应用需要的用到的路由,每一个
路由包含了一个Http方法和一个指定了action方法调用的通用资源定位符。
示例如下:
# Display a client.
GET /clients/:id controllers.Clients.show(id: Long)
3.Http方法
Http方法指任何被Http所支持的方法,如GET/POST/PUT/DELETE/HEAD等
4.URI
URI定义了路由请求的路径,请求路径的某些部分可以是动态的。
静态路径,例如准确匹配GET /clients/all的请求,就可以这样定义路由:
GET /clients controllers.Clients.list()
动态路径,如果你想定义一个路由,例如通过ID来检索Client,那么你需要增加一个动态部分:
GET /clients/:id controllers.Clients.show(id: Long)
很多时候,动态部分不止一个。动态配置也可以使用通配符和正则表达式。如:
GET /files/*name controllers.Application.download(name)
GET /clients/$id<[0-9]+> controllers.Clients.show(id: Long)
5.调用action生成的方法
路由的最后一部分定义了调用的方法,和需要的参数,参数可以定义各种类型,int,long,String,List等等。
6.路由优先级
多个路由匹配同一个请求的时候就会产生冲突,这时候第一个路由会被使用。
#处理响应
1.改变默认的content-Type
结果的content-Type会根据你设置的Http体的Java值来自动进行指定,如Result textResult = ok("Hello World!");将自动设置content-type头为"text/plian",
而Result jsonResult = ok(jerksonObject);则会被设置为"application/json"。
这一点非常有用,当然也可以通过as方法自行设定。如Result htmlResult = ok("<h1>Hello World!</h1>").as("text/html");
或者直接设置Http response也能达到效果response().setContentType("text/html");return ok("<h1>Hello World!</h1>");
2.设置Http响应头
可以增加或者更改Http响应头,例如:
public static Result index() { response().setContentType("text/html"); response().setHeader(CACHE_CONTROL, "max-age=3600"); response().setHeader(ETAG, "xxx"); return ok("<h1>Hello World!</h1>"); }
这些设置会废弃之前的设置。
3.设置和丢弃cookies
cookies仅仅是Http头的特殊形式,Play提供了一些方法使得对设置cookies变得更简单。
例如:
response().setCookie("theme", "blue");
response().discardCookies("theme");
4.为文本结果指定字符编码
正确处理基于文本的Http响应的字符编码十分重要,Play使用UTF-8作为默认的编码格式。
#Session和Flash 作用域
1.Session的概念在Play中很不同
如果你要保持跨多个Http请求的数据,你可以把他们保存在Session或者Flash作用域中。保存在Session中的数据在整个用户会话中可用,
保存在Flash作用域中的数据仅在下个请求中可用。
Session和Flash数据没有在服务器上储存,而是使用cookies在后续的Http请求之间传递(而是使用cookies被添加到后续的每个Http请求中),理解这一点很重要。
也就是说数据大小受限制了(最大到4KB),并且只能存储字符。
Cookies被一个密钥签名,这样客户端就无法修改cookie数据。Play的Session无意作为一个缓存。如果你需要存储与某个特定用户相关的数据,
那么你可以使用Play内置的缓存机制来存储。通过存储一个用户会话的唯一ID来与缓存中的数据进行关联。
注意:Play没有Session超时失效的技术,如果你的应用程序需要这样的功能,你可以在用汉语Session中存储一个时间戳,并需要你自己来实现Session失效的功能。
读取Session
String user = session("connected");
往Session中存储数据
session("connected", "[email protected]");
删除某个Session
session.remove("connected");
废除所有Session
session().clear();
2.Flash作用域(scope)
Flas作用域的工作方式和Session很相似,但是有两点是不同的:
a 数据仅保持在一个请求中
b 数据未被签名,因此用户可以修改Flash cookie的数据。
注意:Flash作用域仅应该被用于在简单的非Ajax应用中传递成功/失败消息。
因为其数据仅仅传递给下一个请求,但是在复杂的web应用中它无法确保请求的顺序。
总之,Flash作用域受到竞争条件限制。
Flash的使用方法与Session相似。
#(Body parses)Http体分析器
1.何为体解析
一个Http请求会包含一个Http体,这个Http体能过按照content-Type头中的设定被格式化。
注意:你不能直接使用Java实现BodyParser(体解析器)。因为Play的体解析器必须直接处理一个迭代矩阵,
而这必须由Scala来实现。
不过Play提供了默认的BodyParser,它可以满足大部分的情况(解析Json、Xml、Text、上传文件等)。并且你可以直接使用Java来重用这些解析器
来实现自己的解析器。例如你可以基于Text的解析器来实现一个RDF的解析器。
2.BodyParser的Java API
所有的解析器必须返回play.mvc.Http.RequestBody类型的值。这个由BodyParser产生的值最终可以通过request().body()方法获取到。
可以通过@BodyParser.Of的注解来指定一个特定的BodyParser,例如:
@BodyParser.Of(BodyParser.Json.class) pulic static Result index() { RequestBody body = request().body(); ok("Got json: " + body.asJson()); }
3.Http.RequestBody API
通常BodyParser会返回正确的play.mvc.Http.RequestBody类型的值。但一些解析器能提供比Http.RequestBody更特殊的类型。使用as()方法你可以自动的将请求体转换为另一种类型。例如:
@BodyParser.Of(BodyLengthParser.class) pulic static Result index() { BodyLength body = request().body().as(BodyLength.class); ok("Request body length: " + body.getLength()); }
4.默认的体解析器:任意内容
如果你不指定你自觉的体解析器,Play会使用默认的最接近content-Type的解析器。
a text/plain:String,使用asText().
b application:json:JsonNode,使用asJson().
c text/xml: org.w3c.Document,使用asXml();
d application/form-url-encoded:Map<String, String[]>,使用asFormUrlEncoded().
e multipart/form-data:Http.MultipartFormData, 使用asMultipartFormData().
f 其他content type: Http.RawBuffer, 使用asRaw().
例如:
pulic static Result save() { RequestBody body = request().body(); String textBody = body.asText(); if(textBody != null) { ok("Got: " + text); } else { badRequest("Expecting text/plain request body"); } }
5.内容的最大长度
基于文本的体解析器有一个最大长度因为它们要被全部装载到内存中。
Play默认的内容长度为100KB,默认的大小可以在application.conf中定义:parsers.text.maxLength=128K
#Action的构成
1.关于actions的提醒
之前,我们说一个Action就是一个返回play.mvc.Result类型值的Java方法。实际上,Play把内部actions当作函数。
因为Java不支持first class functions,由Java API提供的Action是一个play.mvc.Action的实例:
public abstract class Action { public abstract Result call(Http.Context ctx); }
Play为你建立一个根action来调用正确的action方法,这样就允许更复杂的action组合。