§Action birleştirme
Bu bölüm genel action işlevselliği tanımlamanın birçok yolunu göstermektedir.
§Özel action yapıcılar
Daha önce action tanımlamanın istek parametresi ile, istek parametresi olmadan, gövde ayrıştırıcı ile vb. olmak üzere pek çok yolu olduğunu görmüştük. Aslında asenkron programlama bölümünde göreceğimiz üzere bundan çok daha fazlası var.
Action oluşturmak için kullanılan tüm bu metotlar ActionBuilder
isimli bir trait içinde tanımlanmışlardır. Action’larımızı tanımlamak için kullandığımız Action
nesnesi yalnızca bu trait’in bir örneğidir.
Önce bir loglama örneği ile başlayalım. Bu action’a yapılan her çağrıyı loglamak istiyoruz.
Bunun ilk yolu bu özelliği ActionBuilder
tarafından üretilen her action için çağrılan invokeBlock
içinde gerçeklemektir:
import play.api.mvc._
object LoggingAction extends ActionBuilder[Request] {
def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]) = {
Logger.info("Calling action")
block(request)
}
}
Şimdi bunu Action
’ı kullandığımız şekilde kullanabiliriz:
def index = LoggingAction {
Ok("Hello World")
}
ActionBuilder
action oluşturmanın tüm yollarını sağladığından bu yöntem örnek olarak özel gövde ayrıştırıcı ile de çalışır:
def submit = LoggingAction(parse.text) { request =>
Ok("Got a body " + request.body.length + " bytes long")
}
§Action’ları birleştirmek
Çoğu uygulamada birden fazla action yapıcıya ihtiyaç duyarız. Bunlardan kimi değişik türde yetkilendirme yaparken, diğerleri başka işlevler sağlayabilir. Hangi durum olursa olsun her bir action yapıcı için loglama kodumuzu tekrar tekrar yazmak istemeyiz. Aksine bu özelliği tekrar kullanılabilir şekilde tanımlamak isteriz.
Tekar kullanılabilir action kodu action’ları sarmalayarak gerçeklenebilir:
import play.api.mvc._
case class Logging[A](action: Action[A]) extends Action[A] {
def apply(request: Request[A]): Future[Result] = {
Logger.info("Calling action")
action(request)
}
lazy val parser = action.parser
}
Ayrıca Action
action yapıcısını da kendi action sınıfımızı tanımlamadan action oluşturmak için kullanabiliriz:
import play.api.mvc._
def logging[A](action: Action[A])= Action.async(action.parser) { request =>
Logger.info("Calling action")
action(request)
}
Action’lar action yapıcılar içine composeAction
metodunu kullanarak dahil edilebilirler:
object LoggingAction extends ActionBuilder[Request] {
def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]) = {
block(request)
}
override def composeAction[A](action: Action[A]) = new Logging(action)
}
Yapıcı artık önceki gibi kullanılabilir:
def index = LoggingAction {
Ok("Hello World")
}
Sarmalayan action’ları action yapıcı kullanmadan da dahil edebiliriz:
def index = Logging {
Action {
Ok("Hello World")
}
}
§Daha karmaşık action’lar
Şu ana dek gelen isteği pek de etkilemeyen action’ları gösterdik. Elbette gelen istek nesnesini okuyabilir ve değiştirebiliriz:
import play.api.mvc._
def xForwardedFor[A](action: Action[A]) = Action.async(action.parser) { request =>
val newRequest = request.headers.get("X-Forwarded-For").map { xff =>
new WrappedRequest[A](request) {
override def remoteAddress = xff
}
} getOrElse request
action(newRequest)
}
Not: Play dahili olarak X-Forwarded-For başlık desteğine sahiptir.
İsteği engelleyebiliriz:
import play.api.mvc._
def onlyHttps[A](action: Action[A]) = Action.async(action.parser) { request =>
request.headers.get("X-Forwarded-Proto").collect {
case "https" => action(request)
} getOrElse {
Future.successful(Forbidden("Only HTTPS requests allowed"))
}
}
Son olarak dönen yanıtı değiştirebiliriz:
import play.api.mvc._
import play.api.libs.concurrent.Execution.Implicits._
def addUaHeader[A](action: Action[A]) = Action.async(action.parser) { request =>
action(request).map(_.withHeaders("X-UA-Compatible" -> "Chrome=1"))
}
§Farklı istek türleri
Action birleştirme HTTP istek ve yanıt seviyesinde ek işlem yapmanıza izin veriyor olsa da çoğu zaman isteğe bağlam ekleyen ya da istek üzerinde doğrulama yapan veri dönüştürme hatları oluşturmak isteyeceksiniz. ActionFunction
istek üzerinde bir fonksiyon olarak düşünülebilir ve hem istek türü hem de bir sonraki katmana geçirilen yanıt türünde parametriktir. Her action fonksiyonu yetkilendirme, veritabanı araması, izin kontrolleri ve action’lar arasında tekrar kullanmak isteyebileceğiniz diğer işlemler gibi modüler işlemleri betimleyebilir.
Farklı türde işlemler için kullanışlı olan ve ActionFunction
’ı implement eden birkaç öntanımlı trait bulunmaktadır:
ActionTransformer
isteği, örneğin ek bilgi ekleyerek değiştirebilir.ActionFilter
seçimli olarak isteklerde araya girebilir, isteği değiştirmeden hata oluşturmak için kullanılabilir.ActionRefiner
yukarıdaki ikisinin genel durumudur.ActionBuilder
girdi olarakRequest
alan fonksiyonların özel durumudur, böylece action oluşturabilir.
invokeBlock
metodunu implement ederek kendi ActionFunction
tanımınızı yapabilirsiniz. Genellikle girdi ve çıktı türlerini Request
örneği (WrappedRequest
kullanarak) olarak tanımlamak uygundur ancak zorunlu değildir.
§Kimlik doğrulama
Action fonksiyonları için en yaygın kullanım durumlarından bir kimlik doğrulamadır. Kullanıcıyı orijinal istekten alıp yeni bir UserRequest
içine ekleyen bir action dönüştürücüyü kolayca yazabiliriz. Bunun da girdi olarak Request
aldığı için aslında bir ActionBuilder
olduğuna dikkat edin.
import play.api.mvc._
class UserRequest[A](val username: Option[String], request: Request[A]) extends WrappedRequest[A](request)
object UserAction extends
ActionBuilder[UserRequest] with ActionTransformer[Request, UserRequest] {
def transform[A](request: Request[A]) = Future.successful {
new UserRequest(request.session.get("username"), request)
}
}
Play aynı zamanda dahili bir kimlik doğrulama action yapıcı sağlar. Nasıl kullanılacağı hakkında daha fazla bilgi burada bulunabilir.
Not: Dahili kimlik doğrulama action yapıcı yalnızca basit kullanım durumları için gereken kod miktarını en aza indirmek için sağlanan bir yardımcıdır ve gerçeklemesi yukarıdaki örneğe çok benzer şekildedir.
Eğer dahili kimlik doğrulama action tarafından sağlanabileceğinden daha karmaşık gereksinimleriniz varsa kendizinkini gerçeklemeniz hem kolaydır hem de tavsiye edilir.
§İsteklere bilgi eklemek
Şimdi Item
türünden nesneler üzerinde işlem yapan bir REST API düşünün. /item/:itemId
yolu altında pek çok yol olabilir ve bunların her biri öğeyi veritabanından sorgulama ihtiyacı duyabilir. Bu durumda sorgulama işini bir action fonksiyonu içine koymak kullanışlı olabilir.
Önecelikle UserRequest
içine bir Item
ekleyen bir istek nesnesi oluşturacağız:
import play.api.mvc._
class ItemRequest[A](val item: Item, request: UserRequest[A]) extends WrappedRequest[A](request) {
def username = request.username
}
Daha sonra öğeyi sorgulayan ve bir hata (Left
) ya da yeni bir ItemRequest
(Right
) içeren bir Either
döndüren bir action arıtıcı tanımlayacağız:
def ItemAction(itemId: String) = new ActionRefiner[UserRequest, ItemRequest] {
def refine[A](input: UserRequest[A]) = Future.successful {
ItemDao.findById(itemId)
.map(new ItemRequest(_, input))
.toRight(NotFound)
}
}
§İstekleri doğrulamak
Son olarak bir action fonksiyonunun isteğin devam edip etmemesine karar vermesini isteyebiliriz. Örneğin UserAction
’dan gelen kullanıcının ItemAction
’dan gelen öğeye erişim yetkisi olup olmadığını kontrol etmek ve yetkisi yoksa hata dönmek istiyor olabiliriz:
object PermissionCheckAction extends ActionFilter[ItemRequest] {
def filter[A](input: ItemRequest[A]) = Future.successful {
if (!input.item.accessibleByUser(input.username))
Some(Forbidden)
else
None
}
}
§Hepsini bir araya getirmek
Şimdi tüm bu action fonksiyonlarını (bir ActionBuilder
başlayarak) bir action oluşturmak için andThen
kullanarak bir araya getirebiliriz:
def tagItem(itemId: String, tag: String) =
(UserAction andThen ItemAction(itemId) andThen PermissionCheckAction) { request =>
request.item.addTag(tag)
Ok("User " + request.username + " tagged " + request.item.id)
}
Play ayrıca global kestirme ihtiyaçları için bir global filtre API sağlar.
Sonraki: İçerik müzakeresi
Dokümantasyonun bu çevirisi Play ekibi tarafından yapılmamaktadır. Eğer bir hata bulduysanız, bu sayfanın kaynak kodu burada bulunmaktadır. Dokümantasyon yönergelerini okuduktan sonra lütfen katkı yapmaktan çekinmeyin.