Я перехватываю все запросы к моему игровому приложению, переопределяя метод onRouteRequest GlobalSettings. Теперь мне нужно отправить некоторые данные в отправленное действие отсюда, чтобы я не выполнял все эти вычисления во всех действиях. Как установить атрибут объекта запроса (play.api.mvc.RequestHeader), который я передаю методу super onRouteRequest?
Как передать переменную в действие из перехваченного запроса в PlayFramework?
- Установка атрибута недоступна, потому что в функциональном плане мы находимся в неизменяемой среде. Так что, например, при добавлении материала в сеанс вы создаете новый с помощью
withSession
. И в контексте onRouteRequest вы не можете создать новый запрос, потому что вы не сможете передать это новое вашему базовому действию. 10.06.2012 - +100, та же лодка, есть фрагмент данных для вставки в запрос, который применяется ко ВСЕМ маршрутам/типам действий. Я хотел бы выполнить расчет данных в одном месте, onRouteRequest, а затем в любом месте приложения, где неявный запрос находится в области действия, иметь доступ к данным (вместо повторного расчета в разных местах или добавление шаблона для каждого действия для его обработки). 28.10.2012
- @andypetrella scala не является чисто функциональной. Мы можем вводить данные в композицию «Запрос через действие» и WrappedRequest, эффективно изменяя пост-маршрут запроса. Мне бы очень хотелось установить заполнитель Map[String,String] в onRouteRequest. Вы можете скопировать запрос и, например, присвоить значение тегам Map в RequestHeader. Играйте, конечно, сдувая ваши драгоценные данные, используя теги Map для маршрутизации результатов (метод контроллера, тип GET и т.д.) 28.10.2012
- @virtualeyes, ты прав, Scala — это не только f°. Однако ядро Play использует максимум функциональные парадигмы, чтобы упростить параллелизм и так далее. Вот почему такие вещи неизменяемы в Play. Тем не менее, я не понимаю вашу точку зрения? Что бы Вы хотели? Модификация на месте? 29.10.2012
- WrappedRequest — это единственный способ, на самом деле, имитировать ввод данных в запрос. Это работает, и работает довольно хорошо, просто пришлось реорганизовать мое действие Authenticate, чтобы расширить новую черту действия Task, которая сопоставляет класс case с желаемыми параметрами с WrappedRequest. Затем в любом месте приложения вместо ссылки на play.api.mvc.Request вы ссылаетесь на com.company.Task, который содержит идентификатор сеанса пользователя и данные, извлеченные/преобразованные из URI. 29.10.2012
Ответы:
Для ваших нужд я не думаю, что использование onRouteRequest
будет работать (по крайней мере, элегантно).
Но давайте попробуем использовать специальную структуру для перехвата.
Вот как вы можете перехватить запрос, вычислить некоторые общие данные и передать их в действие.
Во-первых, вот объект Interceptor
, у которого есть метод intercept
и удобный метод username
:
object Interceptor {
def intercept[A, B](f: RequestHeader => Option[B], e: RequestHeader => Result)(action: B => Action[A]): Action[(Action[A], A)] = {
val bodyParser = BodyParser {
request =>
f(request) map {
b =>
val innerAction = action(b)
innerAction.parser(request).mapDone {
body => body.right.map(innerBody => (innerAction, innerBody))
}
} getOrElse {
Done(Left(e(request)), Input.Empty)
}
}
Action(bodyParser) {
request =>
val (innerAction, innerBody) = request.body
innerAction(request.map(_ => innerBody))
}
}
def username[A](check: RequestHeader => Option[String]): ((String) => Action[A]) => Action[(Action[A], A)] = intercept(check, r => Results.Unauthorized("not logged in"))
}
Как видите, рабочая функция intercept
дает вам возможность вычислить некоторые вещи на основе содержимого запроса. Какой результат вычисления типа B
может быть неудачным (Option
), в этом случае обработчик должен сказать, что делать.
Определив, что вычислять, вы можете определить action
с помощью функции, которая принимает B
и возвращает Action[A]
.
Метод username
— это просто простой предопределенный перехватчик, который позволяет нам определить, как получить имя пользователя, вошедшего в систему, просто для иллюстрации.
Теперь вот как мы можем использовать их оба в вашем Controller
//index is defined for both GET and POST in routes, but fails on POST
// thanks to the interceptor that checks at first the used method
// the case mustn't be handled in the Action definition
def index = Interceptor.intercept(
/*check the method*/
request => if (request.method == "GET") Some(request.method) else None,
/*not a GET => bad request*/
request => BadRequest(request.method + " not allowed")
) { /*the computation result*/method => Action {
Ok("The method : " + method)
}
}
//this controller retrieve the username in the session and renders it in a OK response
def secured = Interceptor.username(r => r.session.get("username")) { username => Action {
Ok("You're logged in as " + username)
}
}
//this enables you to logged in => store in session
def login(u:String) = Action { request => {
Ok("Logged in as " + u) withSession(("username" -> u))
}
}
Теперь, если у вас есть общие вычисления, вы можете создать свой предварительно сконфигурированный перехватчик (здесь я использую класс case, но достаточно просто определить функцию, которая частично применяет interceptor
)
case class Intercept[B] (f: RequestHeader => Option[B], e: RequestHeader => Result) {
def apply[A](action: B => Action[A]) = Interceptor.intercept[A,B](f, e)(action)
}
val getInterceptor = Intercept[String](
request => if (request.method == "GET") Some(request.method) else None,
request => BadRequest(request.method + " not allowed")
)
def index2 = getInterceptor { method => Action {
Ok("Da method : " + method)
}
}
EDIT в отношении комментария:
Согласно вашему комментарию, вот как вы могли бы использовать перехватчик (обратите внимание, что я смоделировал поиск и проверку хоста)
Используя hosted
и anotherHosted
, вы сможете протестировать этот рабочий процесс:
- /hosted/false?host=myhost => 404, потому что сначала myhost не кэшируется, и я предоставил false проверенному макету
- /hosted/true?host=myhost => не в кеше, но добавит, а потом нет 404
- /hosted/anotherHosted/false?host=myhost => в кеше, потому что он размещен => нет 404
- /hosted/anotherHosted/false?host=notMyhost => 404
Вот код
def getHost(request:RequestHeader) = request.queryString.get("host").get.head
def checkHost(host:String, b: Boolean) = b
val checkHosted = (b: Boolean) => Intercept[String](
request => {
val host = getHost(request)
Cache.getAs[String](host) match {
case x@Some(_) => x
case None => if (checkHost(host, b)) {
Cache.set(host, host)
Some(host)
} else None
}
},
request => NotFound(getHost(request) + "not hosted")
)
def hosted(b:String) = checkHosted(b.toBoolean) {
host => Action {
Ok("this host is ok : " + host)
}
}
def anotherHosted(b:String) = checkHosted(b.toBoolean) {
host => Action {
Ok("this host is ok : " + host)
}
}