关于this question我很好奇如何做一个la(粗略)后请求REST处理:
def postProcessor[T](content: T) = {
request match {
case Accepts.Json() => asJson(content)
case Accepts.Xml() => asXml(content)
case _ => content
}
}
全局配置中的overriding onRouteRequest似乎不提供对响应正文的访问,因此Action composition似乎是拦截响应并执行后处理任务的方法(s )。
问题:这是一个好主意,还是直接在已知类型转换的控制器(或其他类)方法中进行内容类型转换更好?
目前我到处都是这样做的事情:
toJson( i18n("account not found") )
toJson( Map('orderNum-> orderNum) )
虽然我希望toJson / toXml转换基于接受标头后请求而发生。
答案 0 :(得分:4)
您希望能够根据请求的Result
标头值计算包含A
类型对象表示的Accept
。您可以使用以下 type trait 编码此功能:
trait Repr[-A] {
def render(a: A, request: RequestHeader): Result
}
然后,您可以使用以下帮助程序特征从控制器渲染任何资源:
trait ReprSupport {
def repr[A](a: A)(implicit request: RequestHeader, repr: Repr[A]) =
repr.render(a, request)
}
object MyApp extends Controller with ReprSupport {
def index = Action { implicit request =>
repr(Foo("bar"))
}
}
其中Foo
是一个简单的案例类,定义如下:
case class Foo(bar: String)
为了能够编译上面的代码,您需要在隐式作用域中具有类型Repr[Foo]
的值。第一个实现可以写成如下:
object Foo extends AcceptExtractors {
implicit val fooRepr = new Repr[Foo] {
def render(foo: Foo, request: RequestHeader): Result = request match {
case Accepts.Html() => Ok(views.html.foo(foo)) // Assumes there is a foo.scala.html template taking just one parameter of type Foo
case Accepts.Json() => Ok(Json.obj("bar" -> foo.bar))
case _ => NotAcceptable
}
}
}
但对于您要为其编写Repr
个实例的每种数据类型,render
方法将遵循相同的模式:
implicit val somethingRepr = new Repr[Something] {
def render(value: Something, request: RequestHeader): Result = request match {
// <Some interesting code> (e.g. case Accepts.Html() => Ok(views.html.something(value)))
case _ => NotAcceptable
}
}
您可能希望通过抽象此模式来减少样板并避免用户忘记最后一个“case”语句。例如,您可以编写以下帮助方法来构建Repr[Something]
:
object Repr {
def apply[A](f: PartialFunction[RequestHeader, A => Result]): Repr[A] = new Repr[A] {
def render(a: A, request: RequestHeader): Result =
if (f.isDefinedAt(request)) f(request)(a)
else NotAcceptable
}
}
因此,您只需编写以下内容即可获得Repr[Foo]
:
implicit val fooRepr = Repr[Foo] {
case Accepts.Html() => foo => Ok(views.html.foo(foo))
case Accepts.Json() => foo => Ok(Json.obj("bar" -> foo.bar))
}
答案 1 :(得分:1)
一种选择可能是为此创建一种包装。
它应该类似于以下内容:
//THE WRAPPER that takes the action computation and the formatter
def acceptEnabledAction[A]: (RequestHeader => A, (RequestHeader, A) => Result) => Action[AnyContent] =
(a, f) =>
Action { request =>
f(request, a(request))
}
//a sample formatter
val encoder = (r, r) => r.accept /*or smthg*/ match {
case x if x == "text/json" || x == "application/json" => Ok(toJson(r)) /*dummy to json*/
case x if x == "text/xml" || x == "application/xml" => Ok(toXml(r)) /*dummy to xml*/
case _ => BadRequest("not accepted")
}
//an action using it
def index = acceptEnabledAction[Map[String, Boolean]](
rh => /*the real action content is here*/Map("a" -> true),
encoder
)
答案 2 :(得分:0)
另一个选择是使用mimerender模块(披露:我写了它)。您可以定义映射一次:
val m = mapping(
"text/html" -> { s: String => views.html.index(s) },
"application/xml" -> { s: String => <message>{s}</message> },
"application/json" -> { s: String => toJson(Map("message" -> toJson(s))) },
"text/plain" -> identity[String]_
)
只需在所有控制器中重复使用它:
object Application extends Controller {
def index = Action { implicit request =>
m.status(200)("Hello, world!")
}
}
注意:这是一个非常早期的版本,仅在Play 2.0.4上进行了测试