2016-03-30 6 views
2

С маршрутом, как:В Play, как включить настраиваемые параметры запроса в обратном URL? (Call)

GET /users/:id controllers.Users.view(id) 

В контроллере я могу читать пользовательские backTo строки запроса параметров, как это:

def view (id: String) = Action {r => 
    val backTo = r.getQueryString("backTo") // read custom param 
    ... 
} 

Допуская backTo необязательный параметр строки запроса, что я не хотите включать в определение маршрутов (возможно, все действия могут его прочитать).

Как создать URL-адрес с использованием обратного маршрутизации, который включает параметр backTo?

Я бы ожидать что-то вроде:

routes.Users.view(id).withQueryString("backTo" -> Seq("previous")) 

Но что не существует.

ответ

2

Я в конечном итоге просто жалкий способ withQueryString на Call что просто разбирает URL и перестраивает URL с дополнительными параметрами:

implicit class CallOps (c: Call) { 
    import org.jboss.netty.handler.codec.http.{QueryStringDecoder, QueryStringEncoder} 
    import scala.collection.JavaConverters._ 

    def withQueryString(query: (String,Seq[String])*): Call = { 
    val decoded = new QueryStringDecoder(c.url) 
    val newUrl = new QueryStringEncoder(decoded.getPath) 
    val params = decoded.getParameters.asScala.mapValues(_.asScala.toSeq).toSeq 
    for { 
     (key, values) <- params ++ query 
     value <- values 
    } newUrl.addParam(key, value) 
    Call(c.method, newUrl.toString, c.fragment) 
    } 
} 

И теперь я могу использовать его как это:

routes.Users.view(id).withQueryString("backTo" -> Seq("previous")).url 

Мне жаль, что мне не нужно повторно разбирать URL-адрес, но к моменту, когда у меня есть Call, URL-адрес уже создан.

1

Я думаю, вам нужно было бы спуститься до уровня String, так как такого метода нет (пока).

То, что дает вам обратный маршрутизатор, на самом деле является объектом play.api.mvc.Call. Вы можете посмотреть на источник здесь можно:

https://github.com/playframework/playframework/blob/2.5.0/framework/src/play/src/main/scala/play/api/mvc/Http.scala#L359

Вы увидите, что вы можете получить absoluteURL() - который возвращает вам String - отсюда вы бы манипулировать им, добавив параметр запроса:

val url = routes.Users.view(id).absoluteURL() + "?backTo=home"

+0

К сожалению, это требует от меня повторного анализа URL-адреса, поскольку я не могу (не должен?) Делать здесь предположение, что параметр «id» является параметром пути (деталь реализации) ... если маршрут изменилось, может закончиться неверный URL-адрес, например: «/ users? id = 24? backTo = home» –

+0

@AlvaroCarrasco, если вы сохраните правильный порядок маршрутов ('GET/users ...' first 'GET/users /: id ... 'later) двойной знак вопроса не произойдет, так или иначе, используя действительный синтаксис маршрутизации IMHO по-прежнему намного безопаснее – biesior

+0

@biesior Моя точка зрения заключается в том, что обратная маршрутизация должна изолировать меня от необходимости беспокоиться о фактической реализации маршрут. Изменение одного маршрута с использованием параметра «path param» для использования параметра «query param» не должно начинать производить недействительные URL-адреса –

1

Что плохого об использовании Parameters with default values (без разрешения его вручную самостоятельно)? Образец:

def view(id: String, backTo: String, forwardTo: String, whatever: String) = Action { 
    Ok(
    "ID: " + id 
     + ", backTo: " + backTo 
     + ", forwardTo: " + forwardTo 
     + ", whatever: " + whatever 
) 
} 

маршрут:

GET /users/:id controllers.Users.view(id, backTo ?= null, forwardTo ?= null, whatever ?= null) 

это позволяет делать обратные маршруты с только ID, как требуется пары:

// /users/john-doe 
routes.Users.view("john-doe") 

упорядоченного Params:

// /users/john-doe?backTo=prev&forwardTo=next&whatever=bar 
routes.Users.view("john-doe", "prev", "next", "bar") 

или только с именованными параметрами:

// /users/john-doe?whatever=baz 
routes.Users.view("john-doe", whatever = "baz") 

Что более важно, это типобезопасное, как это абсолютно под синтаксисом маршрутизации пьесы, без каких-либо ручных манипуляций. Также вам не нужно заботиться о том, следует ли начинать ваши параметры с ? или & char: "?backTo=home" против "&backTo=home" ...

+1

Обратите внимание, что я не хочу включать его в файл маршрутов, так как он может быть доступен на всех страницах (слишком много повторений). Я бы ожидал, что метод '.withQueryString (...)' в 'Call', как и метод' .withFragment (...) '. Предположительно, если есть способ прочитать значение ('r.getQueryString (...)'), должен быть способ поставить его там, нет? –

+0

_ слишком много повторений, не могли бы вы объяснить? это не синтаксис 'routes.Users.view (" john-doe ", whatever =" baz ")' право на вас страхи? – biesior

+0

Если у меня есть 40 маршрутов, все они потенциально берут необязательный параметр 'backTo', тогда я должен поместить этот параметр в каждый из них (в определении маршрутов, где и есть повторение). –