Ch4 : Let's Play!!

[Play 543]
Play本身是一個MVC架構的Framework(框架),所以我們撰寫相關功能時,Play的目錄架構,本身就在教導怎樣去撰寫Play風格的MVC網站或服務。以下就來開始介紹Play怎樣運作的,跟實際程式細部的功能與用途。我會仿官方流程下來介紹,但是有調整了一下介紹順序,跟補充說明。

這邊建議您的瀏覽器使用Chrome,或者是Firefox,可以協助您開發與微調顯示畫面結果。


Play,Play,and Play it!!


[Play網站怎樣運作的?]
用命令與提示字元,切換目錄到我們最一開始建立的專案myFirstApp
讓專案可執行成為一個網站服務activator run
網址 : http://127.0.0.1:9000/

使用者
使用者在瀏覽器輸入網址後,網站回傳相關頁面或資訊,一般正常情況下,會出現可瀏覽的網頁。

Play 運作流程
使用者輸入網址後,Play會如下面所說明的順序,開始依序執行。先有個大致流程運作感覺就可以了,實際多寫幾次,就會了解Play的運作流程了。
note:這邊只介紹正常可以瀏覽的結果,之後章節會說明,再來補充其他例外地處理。

1.使用者輸入網址 : http://127.0.0.1:9000/

2.Play開始分析使用者的請求,會先根據專案conf/routes,去尋找對應的controller

 # Routes
 # This file defines all application routes (Higher priority routes first)
 # ~~~~

 # 這邊我可以看到,當使用者輸入網址後只有一個/,會主動去找controllers目錄下
 # HomeController.java裡面寫好的index方法,來執行我們要的結果
 # 前面的GET,代表這個網址需要是用Http GET方式才能取得網站的資訊
 GET     /                           controllers.HomeController.index


3.根據Routes所定義的controller,我們網站呼叫的內容,可以在app/controllers裡找到,有一個寫好的HomeController,它已經寫好了index()的方法

 package controllers;

 import play.mvc.*;
 import views.html.*;

 public class HomeController extends Controller {

     /**
      * 這邊我可以看到,index方法,回傳了ok的Result結果
      * 而裡面的是index.render()回傳一個網頁內容與文字說明 
      */
     public Result index() {
         return ok(index.render("Your new application is ready."));
     }

 }


4.我們繼續看到index方法裡面,是回傳index.render("xxx")的結果,而這個index其實是PlayHtml頁面,它會放在app/views目錄下,我們可以看到一個頁面叫做index.scala.html

 @(message: String)

 @main("Welcome to Play") {

     @play20.welcome(message, style = "Java")

 }


5.最後我們的Play成功完成後,會Response(回覆)網頁,給使用者瀏覽。

6.經過以上說明,我們就可以簡單知道,Play網站是怎樣去執行的。下面我們再快速瀏覽一次Play的運作流程。

I.   使用者輸入網址後,routes分析使用者要請求的頁面。
II.  routes找到要執行方法,呼叫對應的controler。
III. controller接受到routes的請求訊息後,controller開始去運算。
IV.  運算出結果後,根據運算結果,是要回覆網頁資訊,還是文字訊息,在把結果回覆給請求的使用者。


有簡單的流程概念後,下面開始介紹怎樣寫一個簡單的Play的網站服務。


[Controller相關說明]
我們寫Play網站服務的時候,最先從Controller開始動手,完成之後,再去conf/routes要使用哪種HTTP方法來觸發我們寫好的Controller

Controller(控制項)
我們在app/controller新增一隻ApplicationController,我們可以注意到Application繼承來至於Play本身的Controller類別,我們模仿(參考)上方的HomeController,來寫以下的程式。

package controllers;

import play.*;
import play.mvc.*;

public class Application extends Controller {

    public Result index() {
       return ok("Hello world"); // 這邊呼叫的話,會回傳Hello world純文字畫面
   }
}


Action(動作)
現在我們把焦點關注在剛剛寫好的index()方法上,當使用者希望做什麼事情的時候,我們都可以先在app/controller下的Controller,寫好我們想要的功能,再回去寫相關的程式。
note : play回傳的ok,在HTTP正常連線回傳的連線代碼是200,回傳的物件類別是play.mvc.Result,它可以接受回傳整個網頁,或一段文字,或者回傳Json資料都是可以做到的。

public Result index() {
   return ok("Hello world");
}


controller執行的方法是可以接受參數的,在Routes撰寫時,需要寫上對應的參數名稱設定。

public Result index(String name) {
     return ok("Hello " + name);
}


Results(結果)
Play程式,可以發現到Play網站呼叫的方法回傳的物件Class(類別)通常會是play.mvc.Result或是play.mvc.Results,而Results已經寫好大部份HTTP常見的回覆方法,大多時候直接拿來使用即可。若需要其他的回覆方法,可以參考官方的play.mvc.Results API說明。
play常見的Result回覆方法

Result ok = ok("Hello world!"); <=正常連線,回傳Hello World字串
Result notFound = notFound();   <=找不到此頁面,通常會給預設頁面
Result pageNotFound = notFound("<h1>Page not found</h1>").as("text/html"); <=找不到頁面,且可以增加Html語法
Result badRequest = badRequest(views.html.form.render(formWithErrors));    <=Play錯誤時回覆錯誤頁面
Result oops = internalServerError("Oops"); <=系統內部錯誤
Result anyStatus = status(488, "Strange response type"); <=也可以自定義網頁狀態


Play本身支援轉址功能,這功能實作在Results裡的redirect方法。若需要轉換頁面時,如參考下面範例即可。

public Result index() {
    return redirect("/user/home");
}


notes:當您要寫這樣的轉址服務時,偶爾會遇到變更網址問題,而且這網址剛好不是我們Play專案下的服務時候,建議集中寫成一個Class來管理URL,當需要redirect(轉址)時就可以import這個Class來幫助我們轉址,這樣之後網址的修正,只需要修正管理網址的Class,就可以快速修正完畢。
類別(Class)範例

public class URLmanager{
 public final String playHome = "/user/home";
}
--
public Result index() {
    return redirect(new URLmanger().playHome);
}

列舉(enum)範例

public enum URLmanger {

playHome("/user/home");

private String value;

 private URLmanger(String value) {
      this.value = value;
 }

 public String getValue() {
      return this.value;
    }
}
--
public Result index() {
    return redirect(URLmanger.playHome.getValue());
}

[Routes相關說明]
當我們寫好controller方法之後,最後一個步驟就是寫conf/routesroutes設定檔,這個設定檔是Play網站服務進來的入口,Play會去看目前的routes有哪些網址可以被呼叫的,再往下執行下去。下面就來介紹Routes該怎樣去撰寫。

Routes格式
我們可以把Routes格式分成三個部分
1.GET的意思是,這個內容要使用是HTTP GET方式取得網站內容
2./clients/:id,代表實際網站啟動後,可以被呼叫的服務。如果要看到這個頁面的話。
在瀏覽器輸入http://127.0.0.1:9000/clients/123,即可。
3.最後是當網址被呼叫時,要去設定要被那個controller呼叫到,這邊要稍微注意到,如果網址後面帶有參數類型,通常對應的controller也會有對應的參數,需要接收瀏覽器的前端請求,再後續處理。
note : 我們可以看到後面的controller有接參數,代表需要對應的資料,一般來說基本常見的物件都可以接受。像 String,int,Long...etc都是可以接收的。

#Http method   URL path              被呼叫的方法,前面是變數名稱,後面是變數要使用的資料型態
GET            /clients/:id          controllers.Clients.show(id: Long)


Play支援的HTTP的Method
Play支援HTTP常見的呼叫方法如(GET, PATCH, POST, PUT, DELETE, HEAD, OPTIONS),都是有支援的,下個章節介紹將會以GETPOST為主來說明。

常見的Routes寫法
以下簡單說明Routes常見可以使用的寫法。

固定網址 : 不需要接收網址參數的網址。
EX URL : http://127.0.0.1:9000/clients/all

GET   /clients/all          controllers.Clients.list()


動態網址 : 網址需要接收參數,才能執行的網址。
EX URL : http://127.0.0.1:9000/clients/0001

GET   /clients/:id          controllers.Clients.show(id: Long)


多參數動態網址 : 如果網址是需要多參數的,可以參考以下範例。差異點是在中間的URL樣板部分,後面可以不需要寫參數名稱。
EX URL : http://127.0.0.1:9000/clients?id=0001&dept=a001

GET   /clients              controllers.Clients.show(id: Long , dept: String)


參數動態網址 : 當你想要設計一個下載圖片的網址時,可以使用正規表示式的*代表任何文字都可以,跟routes寫好變數名稱接收任何文字後,就可以達到我們要的網址效果。
EX URL : http://127.0.0.1:9000/files/images/logo.png

GET   /files/*name          controllers.Application.download(name)


可過濾參數的網址 : 當你不期望一些網站服務被惡意嘗試時,可以使用正規表示式,寫在routes設定檔裡,減少這類型問題。
note : 正規表示式,是一個可以用來檢查標準文字是否符合我們的規則的用途。對於資料驗證部分是很有用的工具。
ex OK URL : http://127.0.0.1:9000/clients?id=0001
ex FAIL URL : http://127.0.0.1:9000/clients?id=abc

#正規表示式,前後需要用大於小於符號包起來,0-9是代表可以接受0~9的數字,後面的+代表意思是只要1個數字以上就可以。
GET   /items/$id<[0-9]+>    controllers.Items.show(id: Long)


需要預設值的網址 : 部分網站,如果發生有需要的參數網址時,卻沒有給參數時,就會需要給預設參數,讓網頁運作是正常的。
ex URL : http://127.0.0.1:9000/ => http://127.0.0.1:9000/home

# Extract the page parameter from the path, or fix the value for /
GET   /                     controllers.Application.show(page = "home")
GET   /:page                controllers.Application.show(page)

# Pagination links, like /clients?page=3
GET   /clients              controllers.Clients.list(page: Int ?= 1)


可選擇性加入參數的網址 : 當我們要去看一些網站內容,有些部分參數,其實可以選擇輸入或不輸入。
EX URL : http://127.0.0.1:9000/home?version=30
EX URL : http://127.0.0.1:9000/home

# The version parameter is optional. E.g. /api/list-all?version=3.0
GET   /api/list-all         controllers.Api.list(version ?= null)


reverse-routing
最後介紹的是routescontroller產出網址的應用。我們可以使用Play內建的reverse-routing技術,協助我們把實際寫好的Play服務,轉換成相對路徑,就可以在contorllerxxx.scala.html頁面設定好對應的Play服務路徑,而不是寫死的服務路徑。

首先我們在app/controller,新增Application.class,寫要執行的方法,routes也寫好要對應要呼叫的方法。當你想要呼叫別的controller且要使用Play reverse-routing的把網頁導向時,需要在routescontroller都有寫好服務之後,才能使用這方法下去轉換。

通常物件呼叫的方式會是controllers.routes.[controller-name].[controller-method],來去獲得要呼叫的方法。

 package controllers;

 import play.*;
 import play.mvc.*;

 public class Application extends Controller {

  public Result hello(String name) {
      return ok("Hello " + name + "!");
   }

  // Redirect to /hello/Bob
  public Result index() {
    return redirect(controllers.routes.Application.hello("Bob"));
  }

}

接下來在conf/routes新增以下的服務

 # Default page 
 GET   /index                controllers.Application.index()

 # Hello action
 GET   /hello/:name          controllers.Application.hello(name)

Request

http:127.0.0.1:9000/index => http:127.0.0.1:9000/hello/Bob

Response

Hello Bob !


最後我們就可以看到實際的結果,會把原本http:127.0.0.1:9000/index的網址轉向到http:127.0.0.1:9000/hello/Bob,來顯示最後的結果Hello Bob !


[Final]
到這邊,基本Play的程式運作流程與如何撰寫新的服務,你就會有大致的印象,下一章簡單介紹Play Request & Response,也會更詳細說明解釋,這章節使用到的技術。以上的介紹,好好的練習下去吧!加油!


[Reference] 1.JavaHome
https://www.playframework.com/documentation/2.5.x/JavaHome

2.JavaActions
https://www.playframework.com/documentation/2.5.x/JavaActions

3.JavaRouting
https://www.playframework.com/documentation/2.5.x/JavaRouting

4.Results API
https://www.playframework.com/documentation/2.5.4/api/java/play/mvc/Results.html

5.Java Regular Expression的學習筆記 [精華]
https://www.javaworld.com.tw/jute/post/view?bid=20&id=130126

results matching ""

    No results matching ""