PlayScala 2.5.x - 關於Content-Type的注意事項

在Play項目中咱們常常須要開發一些自定義Filter完成一些特定任務,在Filter實現中一般須要根據Response的Content-Type作相應的處理。例如實現一個CacheFilter只緩存js/css/img等靜態文件,LoggerFilter只打印html響應的請求,GzipFilter忽略image類型響應(由於image自己就是壓縮類型)。因此正確的獲取Content-Type在開發Filter時顯得尤其重要。在Play2.5.x中,Content-Type的獲取方式發生了一些變化,下面對比Play2.4.x作一些簡單的說明。css

從Play2.5.x開始,Play將逐漸地從Iteratee遷移到Akka Stream,在官方文檔「Play 2.5 Migration Guide」第1段中就說明了這一點:html

對於咱們的平常開發來講,最大的影響就是Result的類型聲明發生了變化,在Play2.4.x中Result的類型聲明爲:json

case class Result(header: ResponseHeader, body: Enumerator[Array[Byte]],
    connection: HttpConnection.Connection = HttpConnection.KeepAlive)

而在Play2.5.x中,body的類型從Enumerator變成了HttpEntity:緩存

case class Result(header: ResponseHeader, body: HttpEntity)

下面咱們經過生成一個簡單的json響應對比一下2.4.x和2.5.x之間的實現差別,生成json代碼以下:app

Ok(Json.obj("success" -> true))

由於傳入的是JsValue類型,因此Play會自動添加以下響應頭:ide

Content-Type:application/json

Play2.4.x的相應實如今Results.Status.apply方法中,代碼以下:ui

class Status(status: Int) extends Result(header = ResponseHeader(status), body = Enumerator.empty, connection = HttpConnection.KeepAlive) {  
  def apply[C](content: C)(implicit writeable: Writeable[C]): Result = {
    Result(
      ResponseHeader(status, writeable.contentType.map(ct => Map(CONTENT_TYPE -> ct)).getOrElse(Map.empty)),
      Enumerator(writeable.transform(content))
    )
  }
...

注意apply方法的第2行,Play2.4.x根據響應內容將Content-Type設置到ResponseHeader中。scala

再來看Play2.5.x,實現也在Results.Status.apply方法中,代碼以下:code

class Status(status: Int) extends Result(header = ResponseHeader(status), body = HttpEntity.NoEntity) {
  def apply[C](content: C)(implicit writeable: Writeable[C]): Result = {
    Result(
      header,
      writeable.toEntity(content)
    )
  }
...

注意apply方法的第2行,Play2.5.x並無在ResponseHeader設置請求頭。繼續追蹤HttpEntity的實現,發現它有一個contentType方法聲明,其值來自隱式的ContentTypeOf[JsValue]參數:orm

/**
   * The content type of the entity, if known.
   */
  def contentType: Option[String]

好吧,真相浮出水面了:Play2.5.x默認將Content-Type響應頭設置在HttpEntity上,而不是像Play2.4.x那樣設置在響應頭上。

因此Play2.5.x中正確獲取Content-Type的方法是使用response.body.contentType,下面是配置GzipFilter的代碼示例:

new GzipFilter(shouldGzip = (request, response) =>
  response.body.contentType.exists(_.startsWith("text/html")))

 

參考:

Play Framework - Filters

相關文章
相關標籤/搜索