6

Ecco un authorisation example from Play Documentation (versione 2.0.4, ho cercato di trovare una nuova versione di questo documento, ma non poteva):In Play 2.4 con DI, come utilizzare una classe di servizio nel tratto "Protetto"?

trait Secured { 

    def username(request: RequestHeader) = request.session.get(Security.username) 

    def onUnauthorized(request: RequestHeader) = Results.Redirect(routes.Auth.login) 

    def withAuth(f: => String => Request[AnyContent] => Result) = { 
    Security.Authenticated(username, onUnauthorized) { user => 
     Action(request => f(user)(request)) 
    } 
    } 

    def withUser(f: User => Request[AnyContent] => Result) = withAuth { username => implicit request => 
    UserDAO.findOneByUsername(username).map { user => 
     f(user)(request) 
    }.getOrElse(onUnauthorized(request)) 
    } 
} 

Nel complesso questo è piuttosto semplice, e mi piacerebbe andare con qualcosa di simile Questo.

Ora, in Play 2.4 il metodo raccomandato è non usare single più (come UserDAO sopra), ma le classi e DI runtime (si veda migration guide o DI docs).

Per esempio, il mio servizio e le classi di repository sono definiti in questo modo:

class AuthService @Inject()(accountRepo: AccountRepository) { } 

class AccountRepository { } 

del gioco con il 2.4 e DI in uso, qual è il/"corretta"/modo più semplice consigliato di entrare in possesso di un servizio o DAO (come AuthService nel mio caso o UserDAO nell'esempio di documento) in un tratto come Secured?

O si suppone oggi che si debba implementare l'autorizzazione per i controller in un modo completamente diverso dall'utilizzo di tale tratto?


posso farlo funzionare in questo senso:

trait Secured { 
    val authService = GuiceUtils.inject[AuthService]  
    // ... 
} 

Utilizzando un aiuto come questo:

object GuiceUtils { 
    lazy val injector = new GuiceApplicationBuilder().injector()  
    def inject[T: ClassTag]: T = injector.instanceOf[T] 
} 

Ma secondo una risposta in un related question:

In Play è possibile utilizzare direttamente l'iniettore purché il tratto dell'applicazione sia compreso. Ma questo non è considerato una buona pratica nel codice di produzione .

Se questo è vero, ciò che è considerati buone pratiche in questo caso d'uso?

risposta

16

Penso che l'approccio più semplice sarebbe quello di dichiarare authService nel tratto ma tenerlo astratto, quindi fare in modo che il controller che lo estende gestisca l'iniezione (credo che sia così che funziona l'iniezione MessagesApi/I18nSupport). Quindi potresti fare:

trait Secured { 
    val authService: AuthService 
    ... 
} 

controller Application @Inject()(override val authService: AuthService) extends Controller with Secured { 
    ... 
} 
+1

Grazie, davvero semplice! (Doveva aggiungere 'override val' per renderlo compilato.) – Jonik

+0

ma in questo modo il controller ha una conoscenza dell'implementazione sottostante - authService - che può essere utilizzata solo nel tratto 'Secured'. C'è un modo per iniettarlo solo per tratto? Come nel modello di torta? – freakman