2013-02-20 24 views
14

Così in Play 2.0 ho avuto questa:Percorsi con parametro opzionale - Gioca 2.1 Scala

GET  /tasks/add    controllers.Tasks.addTask(parentId: Option[Long] = None) 
GET  /tasks/:parentId/add controllers.Tasks.addTask(parentId: Option[Long]) 

Con un metodo di controllo come questo:

def addTask(parentId: Option[Long]) = Action { 
    Ok(views.html.addTask(taskForm, parentId)) 
} 

E lavoravo. Come ho migrato a 2.1, sembra lamentarsi di queste righe con: No URL path binder found for type Option[Long]. Try to implement an implicit PathBindable for this type. Fondamentalmente, quello che sto cercando di fare è avere il percorso tasks/add e il percorso tasks/123/add link allo stesso metodo che accetta uno Optional[Long]. Qualche idea su come fare questo? Grazie.

Ok, quindi ho una specie di bug non è un bug, è una funzione di risposta su Lighthouse: "Abbiamo rimosso il supporto Option [Long] nei path bindables poiché non ha senso avere un parametro path opzionale. puoi implementare il tuo percorso personale che lo supporta, se lo desideri. " Finora ho 2 soluzioni, passando -1 come parentId, che non mi piace molto. O con 2 metodi diversi, che probabilmente hanno più senso in questo caso. L'implementazione di PathBindable non sembra troppo fattibile in questo momento, quindi probabilmente continuerò ad avere 2 metodi.

risposta

13

Gioca 2.0 supportato Option nei parametri di percorso, Gioca 2.1 non è più Supporta questo, hanno rimosso il PathBindable per l'opzione.

Una possibile soluzione potrebbe essere:

package extensions 
import play.api.mvc._ 
object Binders { 
    implicit def OptionBindable[T : PathBindable] = new PathBindable[Option[T]] { 
    def bind(key: String, value: String): Either[String, Option[T]] = 
     implicitly[PathBindable[T]]. 
     bind(key, value). 
     fold(
      left => Left(left), 
      right => Right(Some(right)) 
     ) 

    def unbind(key: String, value: Option[T]): String = value map (_.toString) getOrElse "" 
    } 
} 

E aggiungere questo al Build.scala utilizzando routesImport += "extensions.Binders._". Esegui play clean ~run e dovrebbe funzionare. Ricaricare i raccoglitori al volo solo a volte funziona.

+0

Grazie, l'ho segnalato. Hai idea di come ottenere il comportamento desiderato senza dover aspettare una correzione? – Laky

+2

Ok, quindi: "Abbiamo rimosso il supporto Option [Long] nei path bindable poiché non ha senso avere un parametro path opzionale.Puoi implementare il tuo percorso bindable che lo supporta, se vuoi." Ma passare un Opzionale invece di qualche valore arbitrario mi sembra molto più bello. Non è questo il motivo per cui abbiamo optionals in primo luogo? Forse in questo caso preferirò creare 2 metodi diversi, in quanto avrebbe più senso del passare -1 come id. – Laky

+4

Sì, hai ragione, è molto meglio. Non capisco la loro decisione, però, perché rimuovere qualcosa che consentisse URL più gradevoli? –

5

Credo che bisogna aggiungere un punto interrogativo:

controllers.Tasks.addTask(parentId: Option[Long] ?= None)

+2

Non che è solo per i parametri di query, non per le parti del percorso. –

+0

@Marius fa un buon punto, ma +1 in ogni caso, perché hai risposto alla domanda data nel titolo: "Percorsi con parametro opzionale" –

5

Da questo routes-with-optional-parameter il suggerimento va come:

GET /     controllers.Application.show(page = "home") 
GET /:page    controllers.Application.show(page) 
+4

Lo so, tuttavia, mi piacerebbe andare in giro a dover passare un valore predefinito. Il parametro dovrebbe contenere l'id o niente. Potrei passare -1 o qualcosa di simile per indicare la parte nulla, ma preferirei usare l'Opzione per quello, poiché mi sembra molto più bello. – Laky

+0

Cosa succede se voglio calcolare il parametro predefinito in fase di esecuzione? Cosa dovrei usare come valore letterale per il parametro predefinito? –

+0

Non riesco a far funzionare questa tecnica. Ottengo http 500. Sto usando POST. POST/copy /: param1 /: param2 @ controller.MyController.copy (param1, param2) POST/copy /: param2 @ controller.MyController.copy (param1 = "", param2). La richiesta di posta inviata è "/ copy/somestring" – Skychan

0

ho avuto la stessa cosa e di più se si specifica il passo come GET/foo:id e controllers.Application.foo(id : Option[Long] ?= None) si ottiene un errore It is not allowed to specify a fixed or default value for parameter: 'id' extracted from the path dall'altra parte si può fare come segue GET/foo controllers.Application.foo(id : Option[Long] ?= None) e funzionerà in attesa che la sua richiesta sembra come .../foo?id=1

2

La semplice soluzione al tuo problema, senza dover passare un valore predefinito, consiste nell'aggiungere un semplice metodo proxy che avvolge il parametro in un'opzione.

Rotte:

GET  /tasks/add    controllers.Tasks.addTask() 
GET  /tasks/:parentId/add controllers.Tasks.addTaskProxy(parentId: Long) 

controllore:

def addTask(parentId: Option[Long] = None) = Action { 
    Ok(views.html.addTask(taskForm, parentId)) 
} 

def addTaskProxy(parentId: Long) = addTask(Some(parentId)) 
+0

Una soluzione, ancora cattiva penso – Sorona