自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Play Framework介紹:控制器層

開(kāi)發(fā) 后端
Controller層就是專門(mén)做這件事的:在模型層與傳輸層之間搭起一座橋梁。它使用與模型層同一種語(yǔ)言,以便訪問(wèn)和修改模型對(duì)象,但同時(shí)它又跟HTTP接口一樣,是面向請(qǐng)求(Request)和響應(yīng)(Response)的。

業(yè)務(wù)邏輯代碼通常位于模型(model)層??蛻舳耍ū热鐬g覽器)無(wú)法直接調(diào)用其中的代碼,所以模型對(duì)象提供的功能,必須作為資源以URI方式暴露給外部。

客戶端使用HTTP協(xié)議來(lái)操作這些資源,從而調(diào)用了內(nèi)部的業(yè)務(wù)邏輯。但是,這種從資源到模型之間的映射是單向的:我們可以根據(jù)需要提供不同粒度的資源,可以虛擬出一些資源,還可以給某些資源起別名...

Controller層就是專門(mén)做這件事的:在模型層與傳輸層之間搭起一座橋梁。它使用與模型層同一種語(yǔ)言,以便訪問(wèn)和修改模型對(duì)象,但同時(shí)它又跟HTTP接口一樣,是面向請(qǐng)求(Request)和響應(yīng)(Response)的。

Controller層減少了HTTP與模型層之間的“阻抗不匹配”。

注意

不同的模型方案使用了不同的策略。有一些可以讓我們直接訪問(wèn)模型對(duì)象,比如EJB或者Corba協(xié)議,它們使用RPC(遠(yuǎn)程過(guò)程調(diào)用)。這種交互方式與web很難兼容。

另一些技術(shù)如SOAP,嘗試通過(guò)Web來(lái)訪問(wèn)模型層,但它也是一種PRC風(fēng)格的協(xié)議,只是以HTTP為傳輸協(xié)議。它也不是一種程序協(xié)議。

Web的理念在根本上與面向?qū)ο蟛煌?,所以我們需要一個(gè)層來(lái)協(xié)調(diào)。


Controller綜述

一個(gè)Controller就是一個(gè)位于 controllers 包中的類,其繼承于 play.mvc.Controller :

示例:

  1. package controllers;  
  2.  
  3. import models.Client;  
  4. import play.mvc.Controller;  
  5.  
  6. public class Clients extends Controller {  
  7.  
  8. public static void show(Long id) {  
  9. Client client = Client.findById(id);  
  10. render(client);  
  11. }  
  12.  
  13. public static void delete(Long id) {  
  14. Client client = Client.findById(id);  
  15. client.delete();  
  16. }  
  17.  

Controller中每一個(gè)public static方法都被稱為一個(gè)action。它的簽名形如:

  1. public static void action_name(params...); 

你可以在action的方法簽名中定義各種參數(shù)。Play會(huì)自動(dòng)從相應(yīng)的HTTP參數(shù)中,取出對(duì)應(yīng)的值賦過(guò)去(并進(jìn)行恰當(dāng)?shù)霓D(zhuǎn)換),這一點(diǎn)非常方便。

通常一個(gè)action不需要返回值。當(dāng)我們?cè)赼ction中調(diào)用了一個(gè)能產(chǎn)生“結(jié)果”的方法后,action就退出了。在本例中, render(…) 就是一個(gè)顯示一個(gè)模板的可產(chǎn)生結(jié)果的方法。


獲取HTTP參數(shù)

HTTP請(qǐng)求中可包含數(shù)據(jù),這些數(shù)據(jù)可位于:

  • URI路徑中: 如 /clients/1541, 1541就是一個(gè)動(dòng)態(tài)產(chǎn)生的參數(shù).
  • Query String: /clients?id=1541.
  • request body: 如果提交了一個(gè)HTML表彰,將request body中將包含以 x-www-urlform-encoded 方式轉(zhuǎn)換過(guò)的數(shù)據(jù)。

對(duì)于這些情況,Play都可以取得數(shù)據(jù),并生成一個(gè) Map<String, String[]> 。Key是參數(shù)名,來(lái)源于:

  • 在conf/routes文件中定義的規(guī)則中的動(dòng)態(tài)參數(shù)名
  • Query String中的name=value
  • 通過(guò)表單提交的數(shù)據(jù)的name.

使用參數(shù)map

在所有的Controller類中都可以直接使用 params 這個(gè)對(duì)象。它是在 play.mvc.Controller 中定義的。這個(gè)對(duì)象中,包含了當(dāng)前請(qǐng)求的所有數(shù)據(jù)。

示例:

  1. public static void show() {  
  2. String id = params.get("id");  
  3. String[] names = params.getAll("names");  

你還可以轉(zhuǎn)換參數(shù)值的類型:

  1. public static void show() {  
  2. Long id = params.get("id", Long.class);  

其實(shí),還有更好的辦法來(lái)轉(zhuǎn)換 :)

直接利用action方法的參數(shù)類型定義來(lái)轉(zhuǎn)換

我們可以直接從action方法的參數(shù)定義中,直接拿到對(duì)應(yīng)的參數(shù)值。方法中的參數(shù)名必須跟http傳過(guò)來(lái)的參數(shù)名相同。

比如,對(duì)于如下的URL:

  1. /clients?id=1451 

我們可以定義一個(gè)包含 id 參數(shù)的action:

  1. public static void show(String id) {  
  2. System.out.println(id);   

我們還可以使用其它的類型,play會(huì)自動(dòng)進(jìn)行轉(zhuǎn)換:

  1. public static void show(Long id) {  
  2. System.out.println(id);   

如果同一個(gè)參數(shù)有多個(gè)值,還可以把它聲明為數(shù)組:

  1. public static void show(Long[] id) {  
  2. for(String anId : id) {  
  3. System.out.println(anid);   
  4. }  

甚至集合:

  1. public static void show(List<Long> id) {  
  2. for(String anId : id) {  
  3. System.out.println(anid);   
  4. }  

這一功能非常方便,其它的框架都很少提供(有一些提供了,但要求每個(gè)參數(shù)前都要加一個(gè)annotation,不太方便)。

內(nèi)部原理是,Play會(huì)對(duì)每個(gè)action進(jìn)行掃描,得到其參數(shù)信息(包括類型與參數(shù)名)。能過(guò)反射很容易得到參數(shù)類型,但得不到參數(shù)名,因?yàn)閖avac在編譯java代碼時(shí),會(huì)忽略參數(shù)名信息。而Play內(nèi)置了eclipse的編譯器,并在編譯時(shí)打開(kāi)相關(guān)選項(xiàng)以記錄參數(shù)名信息,所以才能成功取到。

異  常
 

如果action方法中定義的某個(gè)參數(shù),在http請(qǐng)求中找不到對(duì)應(yīng)的數(shù)據(jù),則將會(huì)把它設(shè)為默認(rèn)值(對(duì)象類型設(shè)為null,基礎(chǔ)數(shù)字類型設(shè)為0,boolean類型設(shè)為false)。如果找到了,但是其值無(wú)法轉(zhuǎn)換為指定的Java類型,則會(huì)將這一錯(cuò)誤記錄在validation對(duì)象中,并使用默認(rèn)值代替。


HTTP to Java 高級(jí)綁定

簡(jiǎn)單類型

Java中所有基礎(chǔ)和常用類型,都可以自動(dòng)綁定:

int, long, boolean, char, byte, float, double, Integer, Long, Boolean, Char, String, Byte, Float, Double.

注意如果某個(gè)參數(shù)HTTP請(qǐng)求中沒(méi)有提供,或者自動(dòng)轉(zhuǎn)換失敗,則Object類型會(huì)被設(shè)為null,而基礎(chǔ)類型會(huì)被設(shè)為它們的默認(rèn)值。

Date

如果一個(gè)日期格式如下,則它可以自動(dòng)轉(zhuǎn)換并綁定:

  • yyyy-MM-dd’T’hh:mm:ss’Z' // ISO8601 + timezone
  • yyyy-MM-dd’T’hh:mm:ss" // ISO8601
  • yyyy-MM-dd
  • yyyyMMdd’T’hhmmss
  • yyyyMMddhhmmss
  • dd'/‘MM’/'yyyy
  • dd-MM-yyyy
  • ddMMyyyy
  • MMddyy
  • MM-dd-yy
  • MM'/‘dd’/'yy

使用@As注解,我們可以指定日期格式。

例如:

  1. archives?from=21/12/1980 
  1. public static void articlesSince(@As("dd/MM/yyyy") Date from) {  
  2. List<Article> articles = Article.findBy("date >= ?", from);  
  3. render(articles);  

我們可還以對(duì)不同的語(yǔ)種定義不同的格式,例如:

  1. public static void articlesSince(@As(lang={"fr,de","*"},   
  2. value={"dd-MM-yyyy","MM-dd-yyyy"}) Date from) {  
  3. List<Article> articles = Article.findBy("date >= ?", from);  
  4. render(articles);  

在這個(gè)例子中,我們將法國(guó)和德國(guó)的日期定義為 dd-MM-yyyy ,其它都為 MM-dd-yyyy. 注意語(yǔ)種可用逗號(hào)分隔。注意參數(shù)lang的個(gè)數(shù)必須與value的個(gè)數(shù)相等。

如果沒(méi)有使用@As注解,則Play將使用你的區(qū)域?qū)?yīng)的默認(rèn)日期格式。我們可在“conf/application.conf”中使用“date.format”為key來(lái)配置該格式.

日歷(Calendar)

Calendar的綁定與date幾乎完全一樣,除了play將會(huì)根據(jù)你的區(qū)域設(shè)置選擇相應(yīng)的Calendar對(duì)象。@Bind注解也可在這里使用。

文件(File)

在Play中處理文件上傳非常簡(jiǎn)單。使用經(jīng) multipart/form-data 編碼的請(qǐng)求將文件post到服務(wù)器端,同時(shí)使用 java.io.File 來(lái)獲取文件:

  1. public static void create(String comment, File attachment) {  
  2. String s3Key = S3.post(attachment);  
  3. Document doc = new Document(comment, s3Key);  
  4. doc.save();  
  5. show(doc.id);  

Play將先取得上傳的文件,保存在臨時(shí)目錄下,并使用上傳的文件名。當(dāng)這個(gè)request結(jié)束后,該文件將被刪除,所以我們必須將它拷貝到一個(gè)安全的目錄中,否則就找不到了。

上傳的文件的MIME類型通常應(yīng)該在HTTP request的head中,以 Content-type 方式指定。但當(dāng)我們通過(guò)瀏覽器上傳文件時(shí),一些不常見(jiàn)類型的文件不會(huì)指定。在這種情況下,我們可以使用 play.libs.MimeTypes 類將該文件的后綴名映射到一個(gè)MIME類型上。

  1. String mimeType = MimeTypes.getContentType(attachment.getName());  

play.libs.MimeTypes 類在 $PLAY_HOME/framework/src/play/libs/mime-types.properties 中以文件的后綴為來(lái)尋找對(duì)應(yīng)的MIME類型。

你也可以 通過(guò) 自定義MIME類型 來(lái)增加你自己的類型。

數(shù)組或集合

所有支持的類型,都可以通過(guò)數(shù)組或集合的形式取得:

  1. public static void show(Long[] id) {  
  2. …  

或者:

  1. public static void show(List<Long> id) {  
  2. …  

或者:

  1. public static void show(Set<Long> id) {  
  2. …  

Play還可以處理像Map<String, String>這樣的綁定:

  1. public static void show(Map<String, String> client) {  
  2. …  

一個(gè)如下的query string:

  1. ?client.name=John&client.phone=111-1111&client.phone=222-2222 

將會(huì)把變量client綁定到一個(gè)含有兩個(gè)元素的map上。第一個(gè)元素的key是 name ,值是 John ,第二個(gè)key是 phone 值是 111-1111, 222-2222.

POJO對(duì)象綁定

Play還可以使用簡(jiǎn)單的命名約定將參數(shù)綁定到一個(gè)pojo對(duì)象上。

  1. public static void create(Client client ) {  
  2. client.save();  
  3. show(client);  

可以使用像下面這樣的query string,來(lái)調(diào)用該action以創(chuàng)建一個(gè)client對(duì)象:

  1. ?client.name=Zenexity&client.email=contact@zenexity.fr 

Play創(chuàng)建一個(gè)Client的實(shí)例,然后將HTTP參數(shù)中與Client對(duì)象屬性同名的值賦過(guò)去。無(wú)法處理的參數(shù)將被安全的忽略,類型不匹配的也將安全忽略。

參數(shù)綁定是遞歸的,我們可以通過(guò)query string來(lái)創(chuàng)建一個(gè)完全的對(duì)象圖:

  1. ?client.name=Zenexity  
  2. &client.address.street=64+rue+taitbout  
  3. &client.address.zip=75009 
  4. &client.address.country=France 

如果我們想更新一列對(duì)象,我們可以使用數(shù)組形式來(lái)引用對(duì)象的ID。舉例來(lái)說(shuō),假設(shè)Client模型有一列Customer模型,并聲明為 List<Customer> customers 形式。為了更新這一列Customers,我們應(yīng)該提供一個(gè)如下的query string:

  1. ?client.customers[0].id=123 
  2. &client.customers[1].id=456 
  3. &client.customers[2].id=789 

JPA對(duì)象綁定

我們可以將一個(gè)JPA對(duì)象與HTTP自動(dòng)綁定起來(lái)。

我們可以在HTTP參數(shù)中提供 user.id 字段。當(dāng)Play發(fā)現(xiàn)這個(gè)字段時(shí),它會(huì)先到數(shù)據(jù)庫(kù)中取出相應(yīng)的實(shí)例,然后把HTTP請(qǐng)求中其它的參數(shù)賦過(guò)去。所以我們可以直接save它。

  1. public static void save(User user) {  
  2. user.save(); // ok with 1.0.1  

我們可以使用同樣的方式來(lái)更新完整的對(duì)象圖,但是必須對(duì)每一個(gè)子對(duì)象提供ID:

  1. user.id = 1 
  2. &user.name=morten  
  3. &user.address.id=34 
  4. &user.address.street=MyStreet  

自定義綁定

綁定系統(tǒng)還支持自定義。

@play.data.binding.As

首先要講的是@play.data.binding.As這個(gè)新注解,使用它,我們可以配置一個(gè)綁定??聪旅娴睦?,我們使用它來(lái)指定一個(gè)Date的格式(該格式將被 DateBinder 使用):

  1. public static void update(@As("dd/MM/yyyy") Date updatedAt) {  
  2. …  

@As注解同樣支持國(guó)際化,我們可以這樣使用:

  1. public static void update(  
  2. @As(  
  3. lang={"fr,de","en","*"},  
  4. value={"dd/MM/yyyy","dd-MM-yyyy","MM-dd-yy"}  
  5. )  
  6. Date updatedAt  
  7. ) {  
  8. …  

@As注解可以與所有支持它的binder一起使用,包括你自己的定義的。例如,使用 ListBinder:

  1. public static void update(@As(",") List<String> items) {  
  2. …  

它將會(huì)把一個(gè)以逗號(hào)分隔的字符串綁定到一個(gè) List 上。

@play.data.binding.NoBinding

新的@play.data.binding.NoBinding注解允許我們定義一些“不應(yīng)該被綁定”的字段,以防出現(xiàn)安全問(wèn)題。例如:

  1. public class User extends Model {  
  2. @NoBinding("profile"public boolean isAdmin;  
  3. @As("dd, MM yyyy") Date birthDate;  
  4. public String name;  
  5. }  
  6.  
  7. public static void editProfile(@As("profile") User user) {  
  8. …  

在這種情況下, 在 editProfile action中,就算某個(gè)居心不良的用戶通過(guò)偽造請(qǐng)求提交了一個(gè)包含 user.isAdmin=true 的字段, isAdmin 字段也不會(huì)被綁定.

play.data.binding.TypeBinder

@As 注解同樣允許我們自定義一個(gè)完整的binder. 一個(gè)自定義的binder是 TypeBinder 的子類,我們可以在自己的項(xiàng)目中定義它。例如:

  1. public class MyCustomStringBinder implements TypeBinder<String> {  
  2.  
  3. public Object bind(String name, Annotation[] anns, String value, Class clazz) { return "!!"; } }  

我們可以在任意一個(gè)action中使用它:

  1. public static void anyAction(@As(binder=MyCustomStringBinder.class)   
  2. String name) {  
  3. …  

@play.data.binding.Global

我們還可以自定義一個(gè)全局的binder來(lái)處理某一個(gè)特定的類型。比如,我們給 java.awt.Point 類定義了一個(gè)這樣的binder:

  1. @Global 
  2. public class PointBinder implements TypeBinder<Point> {  
  3.  
  4. public Object bind(String name, Annotation[] anns, String value, Class class) {
  5. String[] values = value.split(“,”);
  6. return new Point( Integer.parseInt(values0), Integer.parseInt(values1) );
  7.  }
  8.  } 

你可以看到這個(gè)全局binder是一個(gè)典型的binder,只是使用了*@play.data.binding.Global*注解。我們?cè)谕獠磕K中定義這種binder,以在不同的項(xiàng)目中復(fù)用。

結(jié)果類型

一個(gè)action方法必須產(chǎn)生一個(gè)HTTP響應(yīng)。最簡(jiǎn)單的方式就是生成一個(gè)Result對(duì)象。一旦某一個(gè)Result對(duì)象生成,該方法將立刻返回(后面的代碼將不會(huì)被執(zhí)行)。

舉例:

  1. public static void show(Long id) {  
  2. Client client = Client.findById(id);  
  3. render(client);  
  4. System.out.println("This message will never be displayed !");  

render(…) 方法產(chǎn)生了一個(gè)Result對(duì)象,后面的代碼都不會(huì)被執(zhí)行。

返回一些文本內(nèi)容

renderText(…) 方法將產(chǎn)生一個(gè)簡(jiǎn)單的Result事件,該事件將直接向HTTP Response中寫(xiě)入一些文本數(shù)據(jù)。

舉例:

  1. public static void countUnreadMessages() {  
  2. Integer unreadMessages = MessagesBox.countUnreadMessages();  
  3. renderText(unreadMessages);  

你還可以使用Java的標(biāo)準(zhǔn)格式化語(yǔ)法來(lái)格式化文本信息:

  1. public static void countUnreadMessages() {  
  2. Integer unreadMessages = MessagesBox.countUnreadMessages();  
  3. renderText("There are %s unread messages", unreadMessages);  

返回二進(jìn)制內(nèi)容

為了處理如 存儲(chǔ)在服務(wù)器上的文件 這樣的二進(jìn)制數(shù)據(jù),我們可使用 renderBinary 方法。例如,如果我們有一個(gè) User 模型,它有一個(gè) play.db.jpa.Blob photo 屬性, 我們可以使用以下方式使用保存的MIME類型向客戶端發(fā)送圖片數(shù)據(jù):

  1. public static void userPhoto(long id) {   
  2. final User user = User.findById(id);   
  3. response.setContentTypeIfNotSet(user.photo.type());  
  4. java.io.InputStream binaryData = user.photo.get();  
  5. renderBinary(binaryData);  
  6. }  

把文件當(dāng)作附件下載

我們可以通過(guò)設(shè)置HTTP頭,來(lái)指導(dǎo)瀏覽器把二進(jìn)制數(shù)據(jù)當(dāng)作“附件”來(lái)保存。我們只需要在 renderBinary 方法中傳入一個(gè)文件名即可。Play會(huì)自動(dòng)在響應(yīng)頭中把文件名設(shè)給 Content-Disposition 。舉例來(lái)說(shuō),前面例子中的 User 模型有一個(gè) photoFileName 屬性:

  1. renderBinary(binaryData, user.photoFileName);  

執(zhí)行一個(gè)模板

如果需要產(chǎn)生的內(nèi)容很復(fù)雜,我們通常會(huì)在模板中來(lái)創(chuàng)建內(nèi)容:

  1. public class Clients extends Controller {  
  2.  
  3. public static void index() { render(); } }  

這里有一個(gè)命名約定,Play會(huì)根據(jù)controller名和action名來(lái)尋找默認(rèn)的模板路徑。對(duì)于上例是:

  1. app/views/Clients/index.html 

把數(shù)據(jù)加入到模板域中

模板通常都需要數(shù)據(jù)。我們可以把這些數(shù)據(jù)放在 renderArgs 對(duì)象中:

  1. public class Clients extends Controller {  
  2.  
  3. public static void show(Long id) {  
  4. Client client = Client.findById(id);  
  5. renderArgs.put(“client”, client); render();  
  6. }  

當(dāng)執(zhí)行模板的時(shí)候,會(huì)自動(dòng)創(chuàng)建 client 變量。

例如,我們可在模板中使用client變量:

  1. <h1>Client ${client.name}</h1> 

將數(shù)據(jù)加入到模板類中的更簡(jiǎn)單的方法

我們可以直接將數(shù)據(jù)傳入到 render(...) 中:

  1. public static void show(Long id) {  
  2. Client client = Client.findById(id);  
  3. render(client);   

在這種情況下,模板中也將有一個(gè)與action中的局部變量名一樣的變量(client)。

我們還可以傳入更多的變量:

  1. public static void show(Long id) {  
  2. Client client = Client.findById(id);  
  3. render(id, client);   

重要!

你只能通過(guò)這種方式來(lái)傳入局部變量

h4. 使用其它模板

如果你不想使用默認(rèn)模板,可以通過(guò)在 renderTemplate(…) 中的第一個(gè)參數(shù)中指定另一個(gè)模板名稱。

舉例:

  1. public static void show(Long id) {  
  2. Client client = Client.findById(id);  
  3. renderTemplate("Clients/showClient.html", id, client);   

重定向到另一個(gè)URL

redirect(…) 方法會(huì)產(chǎn)生一個(gè)重定向事件,接著會(huì)產(chǎn)生一個(gè)HTTP Redirect響應(yīng)。

  1. public static void index() {  
  2. redirect("http://www.zenexity.fr");  

Action鏈

在play中沒(méi)有Servlet API forward 的等價(jià)物。每一個(gè)HTTP request只能調(diào)用一個(gè)action。如果我們需要調(diào)用另一個(gè),必須通過(guò)重定向,讓瀏覽器訪問(wèn)另一個(gè)URL來(lái)訪問(wèn)它。這樣的話,瀏覽器的URL始終與被執(zhí)行的action保持一致,實(shí)現(xiàn) Back/Forward/Refresh 的管理就容易多了。

你可以發(fā)送到任何一個(gè)action的Redirect,只需要直接在Java中調(diào)用該action即可。該調(diào)用將會(huì)自動(dòng)被Play攔截,并生成一個(gè)HTTP重定向。

舉例:

  1. public class Clients extends Controller {  
  2.  
  3. public static void show(Long id) 
  4. Client client = Client.findById(id);
  5. render(client);
  6. }
  7. public static void create(String name) {
  8. Client client = new Client(name);
  9. client.save();
  10. show(client.id);
  11. }
  12. }  

With these routes:

  1. GET /clients/{id} Clients.show  
  2. POST /clients Clients.create  
  • 瀏覽器向 /clients URL發(fā)送一個(gè)POST。
  • Router調(diào)用 Clients controller的 create action.
  • Action方法直接調(diào)用 show 方法
  • 該Java調(diào)用被攔截,Router根據(jù)它產(chǎn)生一個(gè)調(diào)用Clients.show(id)所需要的新URL。
  • HTTP響應(yīng)為 302 Location:/clients/3132.
  • 瀏覽器接著發(fā)送 GET /clients/3132.

自定義編碼集

Play強(qiáng)調(diào)使用UTF-8,但有時(shí)候某些響應(yīng),或者整個(gè)應(yīng)用的響應(yīng),都必須使用一個(gè)不同的編碼集。

給當(dāng)前response定義encoding

要改變當(dāng)前response的編碼集,我們需要在controller中這樣做:

  1. response.encoding = "ISO-8859-1"

如果要使用一個(gè)與服務(wù)器默認(rèn)的不同的編碼集,我們必須在form中包含兩次encoding/charset。一次在 accept-charset 屬性中,一次在一個(gè)hidden類型的字段 _charset_ 中。 accept-charset 告訴瀏覽器使用哪種字符集,而 _charset_ 告訴play使用哪種字符集:

  1. <form action="@{application.index}" method="POST" accept-charset="ISO-8859-1"> 
  2. <input type="hidden" name="_charset_" value="ISO-8859-1"> 
  3. </form> 

自定義整個(gè)程序的編碼集

配置application.web_encoding 來(lái)指定Play使用哪種編碼集。


攔截器(Interceptions)

一個(gè)controller可以定義多個(gè)攔截器方法。攔截器作用于一個(gè)controller及其所有子類的所有action方法上。對(duì)于定義一些所有action共用的操作時(shí),使用攔截器非常有用,比如:檢查用戶是否已經(jīng)登錄(有沒(méi)有訪問(wèn)權(quán)),截入request范圍內(nèi)的數(shù)據(jù),等等。

這些方法必須為static,但不一定是public。你必須給它們?cè)黾雍线m的注解以表明它們是攔截器。

@Before

如果方法上有@Before注解,則它將在該controller中的每一個(gè)action被調(diào)用前被執(zhí)行。

所以可以進(jìn)行一下安全檢查:

  1. public class Admin extends Application {  
  2.  
  3. @Before static void checkAuthentification() {  
  4. if(session.get(“user”) == null) login();  
  5. }  
  6. public static void index() {  
  7. List users = User.findAll();  
  8. render(users);  
  9. }  
  10. …  

如果你想給某些方法開(kāi)綠燈,可按下面的方法排除一些action:

  1. public class Admin extends Application {  
  2.  
  3. @Before(unless=“login”)  
  4. static void checkAuthentification() {  
  5. if(session.get(“user”) == null) login();  
  6. }  
  7. public static void index() {  
  8. List users = User.findAll();  
  9. render(users);  
  10. }  
  11. …  
  12. }  

或者僅對(duì)于某些方法調(diào)用該攔截器,可使用“only”:

  1. public class Admin extends Application {  
  2.  
  3. @Before(only={“login”,“logout”})  
  4. static void doSomething()  
  5. {  
  6. …  
  7. }  
  8. …  

@After, @Before 和 @Finally這三個(gè)注解,都提供了 unlessonly 參數(shù)。

@After

使用@After注解的方法,將在該Controller中的每一個(gè)action之后被調(diào)用。

  1. public class Admin extends Application {  
  2.  
  3. @After static void log(){  
  4. Logger.info(“Action executed ...”);  
  5. }  
  6. public static void index() {  
  7. List users = User.findAll();  
  8. render(users);  
  9. }  
  10. … }  

@Catch

使用了@Catch注解的方法,將會(huì)在某個(gè)拋出了它所指定的異常時(shí),被調(diào)用。異常類型將被傳入到@Catch方法的參數(shù)中。

  1. public class Admin extends Application {  
  2.  
  3. @Catch(IllegalStateException.class)  
  4. public static void logIllegalState(Throwable throwable) {
  5. Logger.error(“Illegal state %s…”, throwable);  
  6. }  
  7. public static void index() { List users = User.findAll();  
  8. if (users.size() == 0) {  
  9. throw new IllegalStateException(“Invalid database - 0 users”);  
  10. }  
  11. render(users);  
  12. }  

與Java的異常處理一樣,我們可以使用一個(gè)超類來(lái)捕獲更多的異常類型。如果我們有多個(gè)catch方法,可以通過(guò)指定其 priority 來(lái)定義它們的執(zhí)行順序(priority為1的最先執(zhí)行)。

  1. public class Admin extends Application {  
  2.  
  3. @Catch(value = Throwable.class, priority = 1)  
  4. public static void logThrowable(Throwable throwable) {  
  5. // Custom error logging… Logger.error(“EXCEPTION %s”, throwable);  
  6. }  
  7. @Catch(value = IllegalStateException.class, priority = 2)  
  8. public static void logIllegalState(Throwable throwable) {  
  9. Logger.error(“Illegal state %s…”, throwable);  
  10. }  
  11. public static void index() {  
  12. List users = User.findAll();  
  13. if(users.size() == 0) {  
  14. throw new IllegalStateException(“Invalid database - 0 users”);  
  15. }  
  16. render(users); }  

@Finally

使用@Finally注解的方法,總是在該Controller中每一個(gè)action執(zhí)行完之后再執(zhí)行。不論action執(zhí)行成功或者失敗,它都將會(huì)被執(zhí)行。跟Java中finally的意思相同。

  1. public class Admin extends Application {  
  2.  
  3. @Finally static void log() {  
  4. Logger.info(“Response contains : ” + response.out);  
  5. }  
  6. public static void index() {  
  7. List users = User.findAll(); render(users);  
  8. }  
  9. …  

如果@Finally方法有一個(gè)Throwable類型的參數(shù),則異常對(duì)象會(huì)被傳入(如果有的話):

  1. public class Admin extends Application {  
  2.  
  3. @Finally static void log(Throwable e) {  
  4. if( e == null ){  
  5. Logger.info(“action call was successful”);  
  6. }  
  7. else 
  8. {  
  9. Logger.info(“action call failed”, e);  
  10. }  
  11. }  
  12. public static void index() {  
  13. List users = User.findAll(); render(users);  
  14. }  
  15. …  

Controller繼承

如果一個(gè)Controller類是另一個(gè)的子類,則父類中定義的攔截器同樣對(duì)子類有效。

通過(guò)@With注解,加入更多的攔截器

因?yàn)镴ava中沒(méi)有多重繼承,所以通過(guò)Controller繼承來(lái)共用攔截器很難。但是通過(guò)@With注解,我們可以把一些攔截器定義在一個(gè)完全不同的類中,然后在當(dāng)前Controller中使用它們。

舉例:

  1. public class Secure extends Controller {  
  2.  
  3. @Before static void checkAuthenticated() {  
  4. if(!session.containsKey(“user”)) {  
  5. unAuthorized();  
  6. }  
  7. }  

把它加到另一個(gè)Controller中:

  1. @With(Secure.class)  
  2. public class Admin extends Application {  
  3.  
  4. … } 

Session和Flash scopes

如果你打算在多個(gè)HTTP請(qǐng)求之間共用數(shù)據(jù),可以把它們保存在Session或Flash域中。保存在Session中的數(shù)據(jù),對(duì)于整個(gè)user session都可用,而保存在flash域中的數(shù)據(jù),則僅僅在下一個(gè)請(qǐng)求可用。

有一點(diǎn)非常重要,需要理解的是,在play中,Session和Flash數(shù)據(jù)并沒(méi)有保存在服務(wù)器端,而是通過(guò)Cookie被加入到每一個(gè)HTTP請(qǐng)求中。所以能保存的數(shù)據(jù)量非常小(不超過(guò)4KB),并且只能保存字符串。

當(dāng)然,cookies都使用了一個(gè)密鑰進(jìn)行了加密,所以客戶端無(wú)法修改cookie數(shù)據(jù)(否則該數(shù)據(jù)將無(wú)效)。Play的session不是用來(lái)當(dāng)作數(shù)據(jù)緩存。如果我們需要緩存與session相關(guān)的某些數(shù)據(jù),可以使用Play內(nèi)置的緩存機(jī)制,并使用 session.getId() 作為key來(lái)保存。

舉例:

  1. public static void index() {  
  2. List messages = Cache.get(session.getId() + "-messages", List.class);  
  3. if(messages == null) {  
  4. // Cache miss  
  5. messages = Message.findByUser(session.get("user"));  
  6. Cache.set(session.getId() + "-messages", messages, "30mn");  
  7. }  
  8. render(messages);  

當(dāng)我們關(guān)閉瀏覽器時(shí),session數(shù)據(jù)將過(guò)期,除非我們通過(guò) application.session.maxAge 進(jìn)行了配置。

緩存使用了與傳統(tǒng)Servlet HTTP session不同的定義,所以我們不能假設(shè)這些數(shù)據(jù)只是在cache中。這將強(qiáng)迫我們處理cache中沒(méi)有數(shù)據(jù)的情況,并強(qiáng)迫我們的程序是完全無(wú)狀態(tài)的。

原文鏈接:http://play-framework.herokuapp.com/zh/controllers

【編輯推薦】

  1. Play Framework介紹:HTTP路由
  2. Play Framework框架安裝指南
  3. Play Framework框架概述
  4. Play Framework hotswap及源碼分析
  5. Play Framework總結(jié)性介紹
責(zé)任編輯:林師授 來(lái)源: Play Framework中文小站
相關(guān)推薦

2012-02-20 14:26:48

JavaPlay Framew

2012-02-24 09:53:24

JavaPlay Framew

2012-02-20 14:20:44

JavaPlay Framew

2023-08-13 18:31:45

SDN控制器

2012-03-14 12:29:55

JavaPlay Framwo

2012-02-22 16:06:42

2012-02-20 14:41:30

JavaPlay Framew

2009-12-01 18:29:56

PHP緩存控制器

2012-02-22 17:23:51

JavaPlay Framew

2016-07-25 15:29:08

SDN控制器H3C

2012-02-23 13:13:00

JavaPlay Framew

2011-07-14 10:28:54

額外域控制器

2012-03-14 09:29:00

Play framewJava

2012-02-23 13:48:16

JavaPlay Framew

2015-08-07 15:28:46

選取城市控制器源碼

2011-07-12 09:29:10

主域控制器備份域控制器

2015-02-02 09:37:42

SDN控制器

2012-02-23 12:53:40

JavaPlay Framew

2024-09-27 16:28:07

2009-01-12 11:16:58

控制器控制器行為行為結(jié)果
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)