1. 程式人生 > >play框架05--控制層

play框架05--控制層

5.1、概述  

Play的控制層位於應用的controllers包中,其中的Java類即為控制器(Controller)。如圖4.1所示,Application.java和MyController.java都屬於控制層。

圖片資源/controller.png

(圖4.1 控制器為controllers包中的Java類)

      控制器需要繼承play.mvc.Controller:

package controllers;
 
import models.Client;
import play.mvc.Controller;
 
public class Clients extends Controller {
 
   
public static void show(Long id) {
       
Client client = Client.findById(id);
        render
(client);
   
}
 
   
public static void delete(Long id) {
       
Client client = Client.findById(id);
        client
.delete();
   
}
 
}

      在控制器中,每個以public static宣告,返回值為void的方法稱為Action。Action的方法宣告如下:

public static void action_name(params…);

      Play會自動將HTTP請求引數轉化為與之相匹配的Action方法引數,這部分內容會在後面的獲取HTTP引數小節進行詳細講解。通常情況下,Action方法無需返回任何值,以呼叫結果方法來終止執行。在上述例子中,render(…)方法就是用來渲染模板的結果方法。

    HTTP請求中往往包含各種引數,這些引數的傳遞形式如下:

  • URI路徑:在路徑/clients/1541中,1541是URI的動態部分。
  • 查詢字串:clients?id=1541。
  • 請求體:如果請求是來自HTML的表單提交(GET或者POST),那麼請求體包含的是表單資料(採用x-www-urlform-encoded作為編碼格式)。


      針對以上幾種情況,Play會自動提取這些HTTP引數並將他們儲存在Map<String,String>型別的變數中,以引數名作為Map的key。這些引數名分別來自於:

  • URI中動態部分的名稱(在routes檔案中定義)。
  • 查詢字串中“名稱/值”對中的名稱部分 。
  • 採用x-www-urlform-encoded編碼的表單資料的引數名。

5.2獲取HTTP引數

5.2.1 使用Map引數#

      HTTP請求中引數物件(params)在任何控制器中都是可訪問的(該實現在play.mvc.Controller超類中定義),它包含了當前所有HTTP請求的引數,並且可以通過get()方法得到,具體如下:

public static void show(){
   
String id=params.get("id");
   
String[] names=params.getAll("name");
}

      這些引數也可以進行型別轉換:

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

      本節將推薦一種更好的解決方案。Play框架提供了自動將Action宣告的引數與HTTP引數自動匹配的功能(只需要保持Action方法的引數名和HTTP引數名一致即可):

/clients?id=1541 

      Action方法可以在宣告中以id作為引數,以此匹配HTTP中變數名為id的引數:

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

      當然,也可以使用其他Java引數型別,而不僅僅是String。在下面的例子中框架會自動將引數轉換為正確的資料型別:

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

      如果引數含有多個值,那麼可以定義陣列引數,具體如下:

public static void show(Long[] id){
   
for(Long anId:id){
       
System.out.println(anId);
   
}
}

      引數甚至可以是List型別:

public static void show(List<Long> id){
   
for(Long anId:id){
       
System.out.println(anId);
   
}
}


注意:

如果Action與HTTP之間的引數無法匹配,Play會將該引數設定為預設值(通常情況下物件型別為null,原始資料型別為0)。如果引數可以匹配但不能正確進行資料轉換,那麼Play會先生成錯誤並新增到驗證器的error物件集合中,然後將引數設定為預設值。


4.2.2 高階HTTP繫結#

簡單型別

      Play可以實現所有Java原生的簡單資料型別的自動轉換,主要包括:int,long,boolean,char,byte,float,double,Integer,Long,Boolean,Char,String,Float,Double。


日期型別

      如果HTTP引數字串符合以下幾種資料格式,框架能夠自動將其轉換為日期型別:

  • 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註解,指定特定格式的日期,例如:

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

      也可以根據不同地區的語言習慣對日期的格式做進一步的優化,具體如下:

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

      在這個例子中,對於法語和德語的日期格式是dd-MM-yyyy,其他語言的日期格式是MM-dd-yyyy。語言值可以通過逗號隔開,且需要與引數的個數相匹配。

      如果沒有使用@As註解來指定,Play會採用框架預設的日期格式。為了使預設的日期格式能夠正常工作,按照以下方式編輯application.conf檔案:

date.format=yyyy-MM-dd

      在application.conf檔案中設定預設的日期格式之後,就可以通過${date.format()}方法對模板中的日期進行格式化操作了。


日曆型別

      日曆型別和日期型別非常相像,當然Play會根據本地化選擇預設的日曆型別。讀者也可以通過@Bind註解來使用自定義的日曆型別。


檔案型別

      在Play中處理檔案上傳是件非常容易的事情,首先通過multipart/form-data編碼的請求將檔案傳送到伺服器,然後使用java.io.File型別提取檔案物件:

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

      新建立檔案的名稱與原始檔案一致,儲存在應用的臨時檔案下(Application_name/tmp)。在實際開發中,需要將其拷貝到安全的目錄,否則在請求結束後會丟失。


陣列和集合型別

      所有Java支援的資料型別都可以通過陣列或者集合的形式來獲取。陣列形式:

public static void show(Long[] id){
       
...
}
      List形式:
public staic void show(List<Long> id){
       
...
}
      集合形式:
public static void show(Set<Long> id){
       
...
}
      Play還可以處理Map<String, String>對映形式:
public static void show(Map<String, String> client) {
   
...
}
      例如下面的查詢字串會轉化為帶有兩個元素的map型別,第一個元素key值為name,value為John;第二個元素key值為phone,value為111-1111, 222-2222。:
?user.name=John&user.phone=111-1111&user.phone=222-2222


POJO物件繫結

      Play使用同名約束規則(即HTTP引數名必須與模型類中的屬性名一致),自動繫結模型類:

public static void create(Client client){
    client
.save();
    show
(client);
}

      以下的查詢字串可以通過上例的Action建立client:

?client.name=Zenexity&client.email=[email protected].fr
      框架通過Action建立Client的例項,並將HTTP引數解析為該例項的屬性。如果出現引數無法解析或者型別不匹配的情況,會自動忽略。

      引數繫結是遞迴執行的,這意味著可以深入到關聯物件:

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

      Play的引數繫結提供陣列的支援,可以將物件id作為對映規則,更新一組模型物件。假設Client模型有一組宣告為List<Customer>的customers屬性,那麼更新該屬性需要使用如下查詢字串:

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


4.2.3 JPA物件繫結#

      通過HTTP引數還可以實現JPA物件的自動繫結。Play會識別HTTP請求中提供的引數user.id,自動與資料庫中User例項的id進行匹配。一旦匹配成功,HTTP請求中的其他User屬性引數可以直接更新到資料庫相應的User記錄中:

public static void save(User user){
    user
.save();
}

      和POJO對映類似,可以使用JPA繫結來更改物件,但需要注意的是必須為每個需要更改的物件提供id:

user.id = 1
&user.<