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其實是Play的Html頁面,它會放在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新增一隻Application的Controller,我們可以注意到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/routes的routes設定檔,這個設定檔是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),都是有支援的,下個章節介紹將會以GET與POST為主來說明。
常見的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
最後介紹的是routes與controller產出網址的應用。我們可以使用Play內建的reverse-routing技術,協助我們把實際寫好的Play服務,轉換成相對路徑,就可以在contorller或xxx.scala.html頁面設定好對應的Play服務路徑,而不是寫死的服務路徑。
首先我們在app/controller,新增Application.class,寫要執行的方法,routes也寫好要對應要呼叫的方法。當你想要呼叫別的controller且要使用Play reverse-routing的把網頁導向時,需要在routes與controller都有寫好服務之後,才能使用這方法下去轉換。
通常物件呼叫的方式會是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