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做一些简单的说明。

从Play2.5.x开始,Play将逐渐地从Iteratee迁移到Akka Stream,在官方文档“Play 2.5 Migration Guide”第1段中就说明了这一点:

对于我们的日常开发来说,最大的影响就是Result的类型声明发生了变化,在Play2.4.x中Result的类型声明为:

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代码如下:

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

因为传入的是JsValue类型,所以Play会自动添加如下响应头:

Content-Type:application/json

Play2.4.x的相应实现在Results.Status.apply方法中,代码如下:

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中。

再来看Play2.5.x,实现也在Results.Status.apply方法中,代码如下:

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]参数:

  /**
   * 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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏JackieZheng

学习SpringMVC——如何获取请求参数

  @RequestParam,你一定见过;@PathVariable,你肯定也知道;@QueryParam,你怎么会不晓得?!还有你熟悉的他(@CookieV...

30350
来自专栏企鹅号快讯

Spring Boot 2.0-WebFlux framework

1、介绍 1.1 什么是响应式编程(Reactive Programming)? 简单来说,响应式编程是针对异步和事件驱动的非阻塞应用程序,并且需要少量线程来垂...

47450
来自专栏编舟记

R3 Corda 和 springboot 集成

因为Corda内置的Corda Webserver已经被标记成弃用了,一般不再提供支持;再者,springboot的生态明显占优。

26220
来自专栏公众号_薛勤的博客

Netty入门(二)之PC聊天室

参看Netty入门(一):Netty入门(一)之webSocket聊天室 Netty4.X下载地址:http://netty.io/downloads.ht...

17840
来自专栏coolblog.xyz技术专栏

Spring MVC 原理探秘 - 一个请求的旅行过程

22440
来自专栏美团技术团队

Spring MVC注解故障追踪记

Spring MVC是美团点评很多团队使用的Web框架。在基于Spring MVC的项目里,注解的使用几乎遍布在项目中的各个模块,有Java提供的注解,如:@O...

42470
来自专栏小灰灰

Spring之RequestBody的使用姿势小结

对上面几种方式有兴趣的可以看一下这篇博文: SpringMVC之请求参数的获取方式

37310
来自专栏技术墨客

Spring核心——数据校验

在Java数据校验详解中详细介绍了Java数据校验相关的功能(简称Bean Validation,涵盖JSR-303、JSR-349、JSR-380),本文将在...

13410
来自专栏Java帮帮-微信公众号-技术文章全总结

【学习笔记】springboot教程(1)第一个demo

【学习笔记】springboot教程(1) 第一个demo 摘要: 先了解下springboot到底是个什么东西,能用来干什么?有什么好处?也就是为什么要学习他...

36340
来自专栏技术墨客

Spring核心——数据校验

在Java数据校验详解中详细介绍了Java数据校验相关的功能(简称Bean Validation,涵盖JSR-303、JSR-349、JSR-380),本文将在...

67020

扫码关注云+社区

领取腾讯云代金券