4

关于这个问题,我很好奇如何进行请求后 REST 处理(粗略):

def postProcessor[T](content: T) = {
  request match {
    case Accepts.Json() => asJson(content)
    case Accepts.Xml()  => asXml(content)
    case _ => content
  }
}

在全局配置中覆盖 onRouteRequest似乎无法提供对响应正文的访问,因此看起来Action 组合是拦截响应并执行后处理任务的方法。

问题:这是一个好主意,还是直接在已知类型转换的控制器(或其他类)方法中进行内容类型转换更好?

目前我到处都在做这种事情:

toJson( i18n("account not found") )
toJson( Map('orderNum-> orderNum) )

虽然我希望根据接受标头后请求进行 toJson/toXml 转换。

4

3 回答 3

4

您希望能够根据请求的标头值计算Result包含类型对象的表示的 a 。您可以使用以下类型 trait对该功能进行编码:AAccept

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"))
  }
}

whereFoo是一个简单的case类定义如下:

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))
}
于 2012-06-17T21:28:15.187 回答
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
    )
于 2012-06-16T21:11:21.103 回答
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 上进行了测试

于 2012-12-12T22:20:01.110 回答