【问题标题】:Creating a custom template format in Play 2.5在 Play 2.5 中创建自定义模板格式
【发布时间】:2017-07-03 22:21:25
【问题描述】:

我正在尝试按照 Play documentation 制作自定义模板格式(在 Play 2.5_ 和 _Scala 2.11.11),但我在这里这意味着它不适合我。

我希望新模板的文件扩展名为“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 可以为任何类型 A 的值编写 HTTP 响应正文 它存在一个隐含的 play.api.http.Writeable[A] 值。所以你们所有人 需要为您的模板结果类型定义这样的值。为了 例如,这里是如何为 HTTP 定义这样的值:

   implicit def writableHttp(implicit codec: Codec): Writeable[Http] =
  Writeable[Http](result => codec.encode(result.body), Some(ContentTypes.HTTP))

当我尝试创建一个新的 view 文件时,例如称为test.scala.stream 它不知道它应该是什么类型,所以看起来肯定有问题。这个需要帮助!

【问题讨论】:

    标签: scala model-view-controller types playframework customization


    【解决方案1】:

    所以我已经破解了这个,并且本着社区精神。

    1) 创建一个文件(我在寻找流式传输 HTML 时将其命名为“HtmlStream”):

       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
    
      }
    

    希望这对其他人有帮助!

    【讨论】:

      【解决方案2】:

      这是我根据播放规范为 cvs 和作品编写的自定义格式,包括您关于 ContentTypes.HTTP 的问题:

      一些建议:

      1-如果您希望播放框架可以编写 HTTP 响应正文,您需要定义正文内容。查看下面的代码implicit def contentTypeCsv,这是您必须在特定正文内容中执行的操作。

      2-其他重要建议,应在您的自定义模板所在的 built.sbt 中定义具有 CUSTOM FORMAT 的旋转模板,无论项目是单个项目还是多个项目。

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

      【讨论】:

        猜你喜欢
        • 2016-08-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-04-27
        • 2014-02-18
        • 2018-06-17
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多