play框架使用起来(18)
1、集成OpenID
OpenID是身份識別系統,具有開放,非集中等特點。我們只需要記錄OpenID授權用戶的使用信息,不必保持用戶的特定狀態,就可以在程序中很容易地識別新用戶。
OpenID是去中心化的網上身份認證系統。對于支持OpenID的網站,用戶不需要記住像用戶名和密碼這樣的傳統驗證標記。取而代之的是,他們只需要預先在一個作為OpenID身份提供者(identity provider, IdP)的網站上注冊。OpenID是去中心化的,任何網站都可以使用OpenID來作為用戶登錄的一種方式,任何網站也都可以作為OpenID身份提供者。OpenID既解決了問題而又不需要依賴于中心性的網站來確認數字身份。
??????下面實例將演示在Play應用中如何使用OpenID認證,以下是OpenID認證過程:
- 針對每個用戶請求,先檢查用戶是否已經連接OpenID。
- 如果用戶沒有連接OpenID,跳轉至用戶可以提交OpenID的頁面。
- 將用戶提交的OpenID重定向到OpenID提供商。
- 返回后,得到驗證通過的OpenID用戶信息,并將它保存在HTTP Session中。
??????Play中的OpenID功能由play.libs.OpenID輔助類提供,基于OpenID4Java實現。我們在控制器中定義authenticate()方法,處理用戶提交的OpenID。該方法先進行用戶OpenID的檢查,如果已經連接,則將用戶信息保存在HTTP Session中,并顯示歡迎頁面。如果用戶是第一次提交OpenID,則將提交的OpenID重定向到OpenID提供商:
static void checkAuthenticated() {
? ? if(!session.contains("user")) {
? ? ? ? login();
? ? }
}
?
public static void index() {
? ? render("Hello %s!", session.get("user"));
}
? ? ?
public static void login() {
? ? render();
}
? ?
public static void authenticate(String user) {
? ? if(OpenID.isAuthenticationResponse()) {
? ? ? ? UserInfo verifiedUser = OpenID.getVerifiedID();
? ? ? ? if(verifiedUser == null) {
? ? ? ? ? ? flash.error("Oops. Authentication has failed");
? ? ? ? ? ? login();
? ? ? ? }
? ? ? ? session.put("user", verifiedUser.id);
? ? ? ? index();
? ? } else {
? ? ? ? if(!OpenID.id(user).verify()) { // will redirect the user
? ? ? ? ? ? flash.error("Cannot verify your OpenID");
? ? ? ? ? ? login();
? ? ? ? }
? ? }
}
??????創建Login.html模板,添加用戶提交OpenID的表單:
#{if flash.error}<h1>${flash.error}</h1>
#{/if}
?
<form action="@{Application.authenticate()}" method="POST">
? ? <label for="user">What’s your OpenID?</label>
? ? <input type="text" name="user" id="user" />
? ? <input type="submit" value="login..." />
</form>
??????最后定義路由:
GET ? / ? ? ? ? ? ? ? ? ? ? Application.indexGET ? /login ? ? ? ? ? ? ? ?Application.login
* ? ? /authenticate ? ? ? ? Application.authenticate
2、Ajax請求
2.1 集成jQuery#
??????Play默認集成了jQuery庫,并將其存放于應用的public/javascripts目錄。框架對jQuery進行了封裝,提供#{jsAction /}標簽簡化了請求異步調用控制器中的Action方法,因此我們可以通過jQuery非常方便地處理Ajax請求。??????本節將以簡單的Ajax應用為例,介紹在Play框架中如何使用jQuery提供的Ajax支持。
2.2 使用#{jsAction /}#
??????Play提供的#{jsAction /}標簽會返回一個JavaScript函數,該JavaScript函數由基于Action方法的URL連接和自由變量組成。它并不會自動執行Ajax請求,而需要我們先手動對返回的URL進行配置。
??????下面介紹Play應用的Ajax實例。首先在控制器Hotels中定義Action方法list,用于處理瀏覽器異步提交的請求。list方法定義完成后為其配制路由規則:
GET ? ? /hotels/list ? ? ? ?Hotels.list??????我們在客戶端中可以通過以下方式導入路由:
<script type="text/javascript">? ?var listAction = #{jsAction @list(':search', ':size', ':page') /}
? ?$('#result').load(
? ? ? ?listAction({search: 'x', size: '10', page: '1'}),
? ? ? ?function() {
? ? ? ? ? ?$('#content').css('visibility', 'visible')
? ? ? ?}
? ?)
</script>
??????在這個例子中,我們向Hotels控制器中的list方法發送請求,請求中包含search,size和page三個參數。之后將請求保存在listAction變量中,使用load函數通過jQuery處理該請求(這里處理的是HTTP GET請求)。以下就是所發送請求的具體URL:
GET /hotels/list?search=x&size=10&page=1??????以這種方式發送請求會返回HTML數據。當然,我們也可以在控制器中使用適當的渲染方法,返回其他格式的數據,比如renderJSON, renderXML或者直接使用XML的模版等。在客戶端接收到JSON或者XML數據后,可以通過jQuery進行格式轉換。如果讀者還想了解更多細節問題,可以查閱jQuery相關內容。
??????如果讀者需要使用POST請求,只需要將jQuery方法進行轉換即可:
$.post(listAction(), function(data) {? $('#result').html(data);
});
2.3 使用#{jsRoute /}#
??????Play提供的#{jsRoute /}標簽,可以幫助開發者更好地管理路由。#{jsRoute /}標簽的使用方法很簡單,并且與#{jsAction /}類似,但是不同的地方為#{jsRoute /}標簽返回的是一個對象。該對象包含基于服務端Action的URL,以及相應的HTTP方法(GET、POST等),具體范例如下所示。<script type="text/javascript">
? ? var updateUserRoute = #{jsRoute @Users.update(':id') /}
? ? $.ajax({
? ? ? url: updateUserRoute.url({id: userId}),
? ? ? type: updateUserRoute.method,
? ? ? data: 'user.name=Guillaume'
? ? });
</script>
??????使用#{jsRoute /}標簽所帶來的好處是顯而易見的,開發者只需要修改routes路由文件,就可以統一地改變HTTP方法,而不再需要一個一個查看和修改模板文件了。
3、管理數據庫升級
開發人員使用關系數據庫時,通常需要跟蹤和管理數據庫結構的升級和變化。當出現以下幾種情況時,我們需要使用更成熟的方式跟蹤和管理數據庫結構的變化:
- 在團隊開發中,每個成員都需要知道數據庫結構的任何變化。
- 當成品部署到服務器上后,需要采用安全穩定的方式去升級數據庫結構。
- 當開發人員在不同的機器上工作時,需要保持數據庫同步。
如果采用JPA進行操作,Hibernate會自動處理數據庫結構的升級。如果讀者經常需要手動管理數據庫結構,并進行一些精細的調整,版本控制就變得必不可少了。
3.1 升級腳本#
??????Play通過編寫升級腳本來跟蹤數據庫結構的變化。這些腳本采用標準的SQL語句作為語法規則,存放在應用程序的db/evolutions目錄下。腳本使用統一的命名規則:編寫的第一個腳本命名為1.sql,第二個腳本為2.sql,并以此類推。每個腳本都包含兩個部分:
- Ups部分用于描述需要改變的地方。
- Downs部分用于描述如何還原。
??????以下是數據庫升級腳本的例子,起到引導作用:
?
# --- !Ups
?
CREATE TABLE User (
? ? id bigint(20) NOT NULL AUTO_INCREMENT,
? ? email varchar(255) NOT NULL,
? ? password varchar(255) NOT NULL,
? ? fullname varchar(255) NOT NULL,
? ? isAdmin boolean NOT NULL,
? ? PRIMARY KEY (id)
);
?
# --- !Downs
?
DROP TABLE User;
在編寫數據庫升級腳本時,須使用注釋明確地將Ups和Downs部分區分開。
??????如果讀者已經在application.conf文件中配置了數據庫,并且編寫了數據庫升級腳本(放置在db/evolutions目錄下),應用在啟動時就會自動激活數據庫的升級模式。我們也可以強制將其關閉,只需在application.conf文件中進行如下配置:
(圖1 數據庫結構不同步)
??????在頁面上點擊Apply evolutions按鈕,就可以直接執行SQL升級腳本。
如果應用使用內存數據庫(db=mem),Play會預先對數據庫進行檢測。如果數據庫為空就會自動執行所有的升級腳本。
3.2 同步#
??????協作開發中,保持應用同步非常重要。設想一下,如果有兩個開發者合力開發項目,開發者A因為自己負責的功能模塊需要,創建了新的數據表并編寫了升級腳本2.sql:
# Add Post?
# --- !Ups
CREATE TABLE Post (
? ? id bigint(20) NOT NULL AUTO_INCREMENT,
? ? title varchar(255) NOT NULL,
? ? content text NOT NULL,
? ? postedAt date NOT NULL,
? ? author_id bigint(20) NOT NULL,
? ? FOREIGN KEY (author_id) REFERENCES User(id),
? ? PRIMARY KEY (id)
);
?
# --- !Downs
DROP TABLE Post;
??????Play會將該升級腳本應用于開發者A的數據庫。與此同時,開發者B因功能需求修改了User表,也編寫了升級腳本,同樣命名為2.sql:
# Update User?
# --- !Ups
ALTER TABLE User ADD age INT;
?
# --- !Downs
ALTER TABLE User DROP age;
??????開發者B先完成了負責的功能模塊,并提交了代碼(比如開發者采用Git進行管理)。開發者A需要先整合兩個人前半段的工作結果,才能開展后續工作,但是在執行git pull的時候,合并會出現如下沖突:
Auto-merging db/evolutions/2.sqlCONFLICT (add/add): Merge conflict in db/evolutions/2.sql
Automatic merge failed; fix conflicts and then commit the result.
??????這是因為開發者A和開發者B都創建了2.sql升級腳本,必須進行整合:
<<<<<<< HEAD# Add Post
?
# --- !Ups
CREATE TABLE Post (
? ? id bigint(20) NOT NULL AUTO_INCREMENT,
? ? title varchar(255) NOT NULL,
? ? content text NOT NULL,
? ? postedAt date NOT NULL,
? ? author_id bigint(20) NOT NULL,
? ? FOREIGN KEY (author_id) REFERENCES User(id),
? ? PRIMARY KEY (id)
);
?
# --- !Downs
DROP TABLE Post;
=======
# Update User
?
# --- !Ups
ALTER TABLE User ADD age INT;
?
# --- !Downs
ALTER TABLE User DROP age;
>>>>>>> devB
??????整合工作非常簡單,只需要將沖突的部分合并即可:
# Add Post and update User?
# --- !Ups
ALTER TABLE User ADD age INT;
?
CREATE TABLE Post (
? ? id bigint(20) NOT NULL AUTO_INCREMENT,
? ? title varchar(255) NOT NULL,
? ? content text NOT NULL,
? ? postedAt date NOT NULL,
? ? author_id bigint(20) NOT NULL,
? ? FOREIGN KEY (author_id) REFERENCES User(id),
? ? PRIMARY KEY (id)
);
?
# --- !Downs
ALTER TABLE User DROP age;
?
DROP TABLE Post;
??????將整合后的腳本命名為2.sql,作為升級腳本的最新修訂版(注意:與前期開發者A應用到數據庫的2.sql不同)。Play會發現這個情況,并詢問開發者A是否同步數據庫,將最新的修訂版2.sql替換原來的版本,如圖2所示。
(圖2 同步升級腳本)
3.3 非同步狀態#
??????如果腳本(比如3.sql)中存在錯誤,數據庫升級工作將無法完成。當出現這種情況時,Play會將數據庫的結構標記為非同步狀態,并要求我們手動解決這個問題,之后才能繼續別的操作。下例的Ups部分存在錯誤:
# Add another column to User?
# --- !Ups
ALTER TABLE Userxxx ADD company varchar(255);
?
# --- !Downs
ALTER TABLE User DROP company;
??????該腳本在執行時會出現錯誤,Play將數據庫的結構標記為非同步狀態,如圖3所示:
(圖3非同步狀態)
??????錯誤必須得到修正后,才能進行后續操作。使用SQL命令手動更改數據庫結構:
ALTER TABLE User ADD company varchar(255);??????然后點擊Mark it resolved按鈕,通知框架問題已經解決。由于原先的升級腳本存在錯誤,必須將其修正。修改3.sql腳本文件:
# Add another column to User?
# --- !Ups
ALTER TABLE User ADD company varchar(255);
?
# --- !Downs
ALTER TABLE User DROP company;
??????Play會發現該升級腳本是最新的,因此覆蓋原有的3.sql執行,如圖4所示:
(圖4 成功執行數據庫結構同步)
??????圖4 所顯示的是腳本正確執行的結果,之后可以繼續別的工作。
介紹一個開發小技巧:開發人員可以在每次開發前手動刪除原有的數據庫,當應用啟動時框架就會自動加載所有的升級腳本。這樣做的好處是可以使開發人員的數據庫一直保持最新版本。
3.4 升級命令#
??????當應用處于DEV模式時,數據庫升級是交互式地進行的。但是在PROD模式下,必須先使用play evolutions命令將數據庫結構升級為最新版本,才可以開啟應用。當框架發現應用程序的數據庫不是最新版本時,控制臺會出現如下錯誤信息:
~ ? ? ? ?_ ? ? ? ? ? ?_~ ?_ __ | | __ _ _ ?_| |
~ | '_ \| |/ _' | || |_|
~ | ?__/|_|\____|\__ (_)
~ |_| ? ? ? ? ? ?|__/ ?
~
~ play! master-localbuild, http://www.playframework.org
~ framework ID is prod
~
~ Ctrl+C to stop
~
13:33:22 INFO ?~ Starting ~/test
13:33:22 INFO ?~ Precompiling ...
13:33:24 INFO ?~ Connected to jdbc:mysql://localhost
13:33:24 WARN ?~
13:33:24 WARN ?~ Your database is not up to date.
13:33:24 WARN ?~ Use `play evolutions` command to manage database evolutions.
13:33:24 ERROR ~
?
@662c6n234
Can't start in PROD mode with errors
?
Your database needs evolution!
An SQL script will be run on your database.
?
play.db.Evolutions$InvalidDatabaseRevision
? ? ? ? at play.db.Evolutions.checkEvolutionsState(Evolutions.java:323)
? ? ? ? at play.db.Evolutions.onApplicationStart(Evolutions.java:197)
? ? ? ? at play.Play.start(Play.java:452)
? ? ? ? at play.Play.init(Play.java:298)
? ? ? ? at play.server.Server.main(Server.java:141)
Exception in thread "main" play.db.Evolutions$InvalidDatabaseRevision
? ? ? ? at play.db.Evolutions.checkEvolutionsState(Evolutions.java:323)
? ? ? ? at play.db.Evolutions.onApplicationStart(Evolutions.java:197)
? ? ? ? at play.Play.start(Play.java:452)
? ? ? ? at play.Play.init(Play.java:298)
? ? ? ? at play.server.Server.main(Server.java:141)
??????該錯誤信息提示我們運行play evolutions命令同步數據庫:
$ play evolutions~ ? ? ? ?_ ? ? ? ? ? ?_
~ ?_ __ | | __ _ _ ?_| |
~ | '_ \| |/ _' | || |_|
~ | ?__/|_|\____|\__ (_)
~ |_| ? ? ? ? ? ?|__/ ?
~
~ play! master-localbuild, http://www.playframework.org
~ framework ID is gbo
~
~ Connected to jdbc:mysql://localhost
~ Application revision is 3 [15ed3f5] and Database revision is 0 [da39a3e]
~
~ Your database needs evolutions!
?
# ----------------------------------------------------------------------------
?
# --- Rev:1,Ups - 6b21167
?
CREATE TABLE User (
? ? id bigint(20) NOT NULL AUTO_INCREMENT,
? ? email varchar(255) NOT NULL,
? ? password varchar(255) NOT NULL,
? ? fullname varchar(255) NOT NULL,
? ? isAdmin boolean NOT NULL,
? ? PRIMARY KEY (id)
);
?
# --- Rev:2,Ups - 9cf7e12
?
ALTER TABLE User ADD age INT;
CREATE TABLE Post (
? ? id bigint(20) NOT NULL AUTO_INCREMENT,
? ? title varchar(255) NOT NULL,
? ? content text NOT NULL,
? ? postedAt date NOT NULL,
? ? author_id bigint(20) NOT NULL,
? ? FOREIGN KEY (author_id) REFERENCES User(id),
? ? PRIMARY KEY (id)
);
?
# --- Rev:3,Ups - 15ed3f5
?
ALTER TABLE User ADD company varchar(255);
?
# ----------------------------------------------------------------------------
?
~ Run `play evolutions:apply` to automatically apply this script to the db
~ or apply it yourself and mark it done using `play evolutions:markApplied`
~
??????使用play evolutions:apply命令可以通知Play自動執行數據庫升級腳本:
play evolutions:apply??????如果在成品數據庫上手動執行腳本,可以使用play evolutions:markApplied命令通知Play該數據庫已經是最新版本了:
play evolutions:markApplied??????當應用處于DEV模式,如果框架自動執行升級腳本失敗,就需要手動解決,并將數據庫結構標記為已修正的:
play evolutions:resolve
總結
以上是生活随笔為你收集整理的play框架使用起来(18)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 需求分析挑战之旅(疯狂的订餐系统)(6)
- 下一篇: play home android,pl