You want to be able to compute a Result
containing a representation of an object of a type A
according to the request’s Accept
header value. You can encode this capability with the following type trait:
trait Repr[-A] {
def render(a: A, request: RequestHeader): Result
}
You could then render any resource from your controller using the following helper trait:
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"))
}
}
Where Foo
is a simple case class defined as follows:
case class Foo(bar: String)
In order to be able to compile the above code, you need to have a value of type Repr[Foo]
in your implicit scope. A first implementation could be written as follows:
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
}
}
}
But for each data type for which you’ll want to write a Repr
instance, the render
method will follow the same pattern:
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
}
}
You probably want to reduce the boilerplate and to avoid users to forget the last “case” statement by abstracting over this pattern. You can for example write the following helper method to build a 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
}
}
Thus you just need to write the following to get a 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))
}