在 Play 2.5 中创建自定义模板格式
Creating a custom template format in Play 2.5
我正在尝试制作自定义模板格式(在 Play 2.5_ & _Scala 2.11.11 中)遵循 Play documentation 但我在这里所以这意味着它不适合我。
我希望新模板的文件扩展名为“stream”(this video 已经有几年了)所以我创建了这个文件如文档所示:
package ui
import akka.NotUsed
import akka.stream.scaladsl.{Source}
import play.twirl.api._
import scala.collection.immutable
case class HtmlStream(source: Source[Html, NotUsed]) extends Appendable[HtmlStream] {
def +=(other: HtmlStream): HtmlStream = andThen(other)
def andThen(other: HtmlStream): HtmlStream = HtmlStream(source.merge(other.source))
}
object HtmlStream {
def apply(text: String): HtmlStream = apply(Html(text))
def apply(html: Html): HtmlStream = HtmlStream(Source.single(html))
}
object HtmlStreamFormat extends Format[HtmlStream] {
def raw(text: String): HtmlStream = HtmlStream(text)
def escape(text: String): HtmlStream = raw(HtmlFormat.escape(text).body)
override def empty: HtmlStream = ???
override def fill(elements: immutable.Seq[HtmlStream]): HtmlStream = ???
}
并将其添加到 build.sbt 文件中:
TwirlKeys.templateFormats += ("stream" -> "ui.HtmlStreamFormat.instance")
我看不到在哪里或如何包含以下隐式(在前面提到的 Play documentation - 在底部);这可能是问题所在:
Play can write an HTTP response body for any value of type A for which
it exists an implicit play.api.http.Writeable[A] value. So all you
need is to define such a value for your template result type. For
instance, here is how to define such a value for HTTP:
implicit def writableHttp(implicit codec: Codec): Writeable[Http] =
Writeable[Http](result => codec.encode(result.body), Some(ContentTypes.HTTP))
当我尝试创建一个新的 view
文件时,例如称为 test.scala.stream
它无法识别它应该是什么类型所以看起来肯定是错误的。这方面需要帮助!
所以我已经破解了这个,本着社区的精神,它就在这里。
1) 创建一个文件(我将我的文件命名为“HtmlStream”,因为我正在寻找流 HTML):
package ui
import akka.NotUsed
import akka.stream.scaladsl.{Source}
import play.twirl.api._
import scala.collection.immutable
import scala.concurrent.ExecutionContext
case class HtmlStream(source: Source[Html, NotUsed]) extends Appendable[HtmlStream] {
def +=(other: HtmlStream): HtmlStream = andThen(other)
def andThen(other: HtmlStream): HtmlStream = HtmlStream(source.merge(other.source))
}
object HtmlStream {
def apply(text: String): HtmlStream = apply(Html(text))
def apply(html: Html): HtmlStream = HtmlStream(Source.single(html))
}
object HtmlStreamFormat extends Format[HtmlStream] {
def raw(text: String): HtmlStream = HtmlStream(text)
def escape(text: String): HtmlStream = raw(HtmlFormat.escape(text).body)
override def empty: HtmlStream = raw("")
override def fill(elements: immutable.Seq[HtmlStream]): HtmlStream = elements.reduce((agg, curr) => agg.andThen(curr))
}
object HtmlStreamImplicits {
implicit def toSource(stream: HtmlStream)(implicit ec: ExecutionContext): Source[Html, NotUsed] = {
stream.source.filter(_.body.nonEmpty)
}
2) 我将这些行添加到 build.sbt 文件中:
TwirlKeys.templateFormats += ("stream" -> "ui.HtmlStreamFormat")
TwirlKeys.templateImports ++= Vector("ui.HtmlStream", "ui.HtmlStream._", "ui.HtmlStreamFormat._", "ui.HtmlStreamImplicits._")
3) 我添加了一个名为 test1.scala.stream 的模板(出现提示时使用 HTML 的 文件关联 ):
@(body: HtmlStream)
<!DOCTYPE html>
<html lang="en">
<head>
<title>title</title>
<link rel="stylesheet" media="screen" href="assets/stylesheets/main.css">
<link rel="shortcut icon" type="image/png" href="assets/images/favicon.png">
<script src="assets/javascripts/hello.js" type="text/javascript"></script>
</head>
<body>
<h1>Streaming the body</h1>
<div>
<div>
@body
</div>
</div>
</body>
</html>
4) 最后,我可以像使用任何其他模板一样使用此模板,方法是将其转换为新的 HtmlStream 类型。
def streamHtml = Action { request =>
val async1: Future[Result] = rr.getAsync(embed = true)(request) // get future
val async1Html: Future[Html] = async1.flatMap(x => Pagelet.readBody(x)) // separate function to convert Future[Result] to Future[Html]
val htmlStream: HtmlStream = HtmlStream(fromFuture(async1Html)) // c onvert to HtmlStream type
Ok.chunked(views.stream.test1(htmlStream)) // chunk the new data type by sending through new template
}
希望这对其他人有帮助!
这是我根据播放规范为 cvs 和作品编写的自定义格式,包括您关于 ContentTypes.HTTP 的问题:
一些建议:
1-如果您希望播放框架可以编写 HTTP 响应主体,您需要定义主体内容。查看下面的代码 implicit def contentTypeCsv 这是您必须在特定正文内容中执行的操作。
2-其他重要建议,自定义格式的 twirl 模板应在自定义模板所在的 built.sbt 中定义,不管项目是单个项目还是多个项目。
class Csv(buffer: immutable.Seq[Csv],text:String,escape:Boolean) extends BufferedContent[Csv](buffer, text) {
val contentType = Csv.contentType
def this(text: String) = this(Nil, Formats.safe(text),false)
def this(buffer: immutable.Seq[Csv]) = this(buffer, "",false)
override protected def buildString(builder: StringBuilder) {
if (elements.nonEmpty) {
elements.foreach { e =>
e.buildString(builder)
}
} else if (escape) {
// Using our own algorithm here because commons lang escaping wasn't designed for protecting against XSS, and there
// don't seem to be any other good generic escaping tools out there.
text.foreach {
case '"' => builder.append("\"\"")
case c => builder += c
}
} else {
builder.append(text)
}
}
}
object Csv {
val contentType = "text/csv"
implicit def contentTypeCsv(implicit codec: Codec): ContentTypeOf[Csv] = ContentTypeOf[Csv](Some(Csv.contentType))
def apply(text: String): Csv = new Csv(text)
def empty: Csv = new Csv("")
}
object CsvFormat extends Format[Csv] {
def raw(text: String): Csv = Csv(text)
def escape(text: String): Csv = {
new Csv(Nil, text, true)
}
def empty: Csv = new Csv("")
def fill(elements: Seq[Csv]): Csv = new Csv(elements)
}
我正在尝试制作自定义模板格式(在 Play 2.5_ & _Scala 2.11.11 中)遵循 Play documentation 但我在这里所以这意味着它不适合我。
我希望新模板的文件扩展名为“stream”(this video 已经有几年了)所以我创建了这个文件如文档所示:
package ui
import akka.NotUsed
import akka.stream.scaladsl.{Source}
import play.twirl.api._
import scala.collection.immutable
case class HtmlStream(source: Source[Html, NotUsed]) extends Appendable[HtmlStream] {
def +=(other: HtmlStream): HtmlStream = andThen(other)
def andThen(other: HtmlStream): HtmlStream = HtmlStream(source.merge(other.source))
}
object HtmlStream {
def apply(text: String): HtmlStream = apply(Html(text))
def apply(html: Html): HtmlStream = HtmlStream(Source.single(html))
}
object HtmlStreamFormat extends Format[HtmlStream] {
def raw(text: String): HtmlStream = HtmlStream(text)
def escape(text: String): HtmlStream = raw(HtmlFormat.escape(text).body)
override def empty: HtmlStream = ???
override def fill(elements: immutable.Seq[HtmlStream]): HtmlStream = ???
}
并将其添加到 build.sbt 文件中:
TwirlKeys.templateFormats += ("stream" -> "ui.HtmlStreamFormat.instance")
我看不到在哪里或如何包含以下隐式(在前面提到的 Play documentation - 在底部);这可能是问题所在:
Play can write an HTTP response body for any value of type A for which it exists an implicit play.api.http.Writeable[A] value. So all you need is to define such a value for your template result type. For instance, here is how to define such a value for HTTP:
implicit def writableHttp(implicit codec: Codec): Writeable[Http] =
Writeable[Http](result => codec.encode(result.body), Some(ContentTypes.HTTP))
当我尝试创建一个新的 view
文件时,例如称为 test.scala.stream
它无法识别它应该是什么类型所以看起来肯定是错误的。这方面需要帮助!
所以我已经破解了这个,本着社区的精神,它就在这里。
1) 创建一个文件(我将我的文件命名为“HtmlStream”,因为我正在寻找流 HTML):
package ui
import akka.NotUsed
import akka.stream.scaladsl.{Source}
import play.twirl.api._
import scala.collection.immutable
import scala.concurrent.ExecutionContext
case class HtmlStream(source: Source[Html, NotUsed]) extends Appendable[HtmlStream] {
def +=(other: HtmlStream): HtmlStream = andThen(other)
def andThen(other: HtmlStream): HtmlStream = HtmlStream(source.merge(other.source))
}
object HtmlStream {
def apply(text: String): HtmlStream = apply(Html(text))
def apply(html: Html): HtmlStream = HtmlStream(Source.single(html))
}
object HtmlStreamFormat extends Format[HtmlStream] {
def raw(text: String): HtmlStream = HtmlStream(text)
def escape(text: String): HtmlStream = raw(HtmlFormat.escape(text).body)
override def empty: HtmlStream = raw("")
override def fill(elements: immutable.Seq[HtmlStream]): HtmlStream = elements.reduce((agg, curr) => agg.andThen(curr))
}
object HtmlStreamImplicits {
implicit def toSource(stream: HtmlStream)(implicit ec: ExecutionContext): Source[Html, NotUsed] = {
stream.source.filter(_.body.nonEmpty)
}
2) 我将这些行添加到 build.sbt 文件中:
TwirlKeys.templateFormats += ("stream" -> "ui.HtmlStreamFormat")
TwirlKeys.templateImports ++= Vector("ui.HtmlStream", "ui.HtmlStream._", "ui.HtmlStreamFormat._", "ui.HtmlStreamImplicits._")
3) 我添加了一个名为 test1.scala.stream 的模板(出现提示时使用 HTML 的 文件关联 ):
@(body: HtmlStream)
<!DOCTYPE html>
<html lang="en">
<head>
<title>title</title>
<link rel="stylesheet" media="screen" href="assets/stylesheets/main.css">
<link rel="shortcut icon" type="image/png" href="assets/images/favicon.png">
<script src="assets/javascripts/hello.js" type="text/javascript"></script>
</head>
<body>
<h1>Streaming the body</h1>
<div>
<div>
@body
</div>
</div>
</body>
</html>
4) 最后,我可以像使用任何其他模板一样使用此模板,方法是将其转换为新的 HtmlStream 类型。
def streamHtml = Action { request =>
val async1: Future[Result] = rr.getAsync(embed = true)(request) // get future
val async1Html: Future[Html] = async1.flatMap(x => Pagelet.readBody(x)) // separate function to convert Future[Result] to Future[Html]
val htmlStream: HtmlStream = HtmlStream(fromFuture(async1Html)) // c onvert to HtmlStream type
Ok.chunked(views.stream.test1(htmlStream)) // chunk the new data type by sending through new template
}
希望这对其他人有帮助!
这是我根据播放规范为 cvs 和作品编写的自定义格式,包括您关于 ContentTypes.HTTP 的问题:
一些建议:
1-如果您希望播放框架可以编写 HTTP 响应主体,您需要定义主体内容。查看下面的代码 implicit def contentTypeCsv 这是您必须在特定正文内容中执行的操作。
2-其他重要建议,自定义格式的 twirl 模板应在自定义模板所在的 built.sbt 中定义,不管项目是单个项目还是多个项目。
class Csv(buffer: immutable.Seq[Csv],text:String,escape:Boolean) extends BufferedContent[Csv](buffer, text) {
val contentType = Csv.contentType
def this(text: String) = this(Nil, Formats.safe(text),false)
def this(buffer: immutable.Seq[Csv]) = this(buffer, "",false)
override protected def buildString(builder: StringBuilder) {
if (elements.nonEmpty) {
elements.foreach { e =>
e.buildString(builder)
}
} else if (escape) {
// Using our own algorithm here because commons lang escaping wasn't designed for protecting against XSS, and there
// don't seem to be any other good generic escaping tools out there.
text.foreach {
case '"' => builder.append("\"\"")
case c => builder += c
}
} else {
builder.append(text)
}
}
}
object Csv {
val contentType = "text/csv"
implicit def contentTypeCsv(implicit codec: Codec): ContentTypeOf[Csv] = ContentTypeOf[Csv](Some(Csv.contentType))
def apply(text: String): Csv = new Csv(text)
def empty: Csv = new Csv("")
}
object CsvFormat extends Format[Csv] {
def raw(text: String): Csv = Csv(text)
def escape(text: String): Csv = {
new Csv(Nil, text, true)
}
def empty: Csv = new Csv("")
def fill(elements: Seq[Csv]): Csv = new Csv(elements)
}