覆盖字符集的urlFormEncoded解析器 - Play Framework 2.3.x.

时间:2015-03-25 07:18:10

标签: scala encoding playframework

如何在Play 2.3.x(Scala)中覆盖urlFormEncoded解析器以使用另一个字符集?

我正在使用Scala中的Play framework 2.3.x编写适用于日语环境的BBS软件。

问题在于charset:客户端POST请求使用url编码(又名。"百分比编码")表单参数编码在Shift-JIS - 日本着名的字符集 - 但Play将其解码为UTF-8。这是可以预测的,因为它在https://github.com/playframework/playframework/blob/2.3.x/framework/src/play/src/main/scala/play/api/mvc/ContentTypes.scala#L515中是硬编码的。

我无法修改BBS客户端'编码,因为它是事实上的标准。

因此,我必须在Play中重写或覆盖url编码解码器以使用Shift_JIS

可能有一些解决方案:

  1. 从修改后的源代码手动构建Play并使用它。
  2. 拦截请求并将正文解码为Shift_JIS,然后将正文重新编码为UTF-8
  3. 创建扩展play.api.mvc.BodyParsers.parse并使用它的自定义解析器。
  4. 我认为最有希望的选择是3,但我无法创建扩展object的{​​{1}}。

    那么,我怎样才能覆盖默认解析器并使用它(或者还有其他更好的解决方案)?

    感谢。

2 个答案:

答案 0 :(得分:0)

默认解析器使用RequestHeader的charset(如果可用)。 charsetContent-Type标题确定。所以我假设客户端没有设置Content-Type标题。

一种方法是通过包装RequestHeader来更改charset

定义包装器。

class WrappedRequestHeader(rh: RequestHeader) extends RequestHeader {
  override def id = rh.id
  override def secure = rh.secure
  override def uri = rh.uri
  override def remoteAddress = rh.remoteAddress
  override def queryString = rh.queryString
  override def method = rh.method
  override def headers = rh.headers
  override def path = rh.path
  override def version = rh.version
  override def tags = rh.tags
}

定义一个请求标头,如果原始标头没有它,则返回一个常量charset

class DefaultCharsetRequestHeader(rh: RequestHeader, defaultCharset: String) extends WrappedRequestHeader(rh) {
  override lazy val charset = rh.charset orElse Some(defaultCharset) // Or always use Some(defaultCharset) if you want
}

然后,您可以从任何默认解析器创建BodyParser。

  def createParser[A](parser: BodyParser[A], defaultCharset: String): BodyParser[A] = BodyParser[A] { rh =>
    parser(new DefaultCharsetRequestHeader(rh, defaultCharset))
  }

如果您想广泛应用它,也可以创建过滤器。

class DefaultCharsetFilter(defaultCharset: String) extends Filter {
  override def apply(f: (RequestHeader) => Future[Result])(rh: RequestHeader): Future[Result] =
    f(new DefaultCharsetRequestHeader(rh, defaultCharset))
}

我还没有试过上面的代码,但我希望它有效。

答案 1 :(得分:0)

我使用apache httpcomponents解决了此问题的续订请求。

我们可以将URL编码数据检索为原始文本,将parse.torelantText指定为主体解析器。然后手动将URL编码数据解析为Map[String, Seq[String]]并重新创建Request

我在下面显示代码。

build.sbt中定义依赖关系:

libraryDependencies += "org.apache.httpcomponents" % "httpclient" % "4.4"

定义要将Request[String]转换为Request[Map[String, Seq[String]]]的对象:

import play.api.mvc.{ Headers, Request }

object PercentEncoding {
  import java.net.URI
  import scala.collection.JavaConversions._
  import org.apache.http.client.utils.URLEncodedUtils
  import org.apache.http.NameValuePair
  def extractSJISRequest(request: Request[String]) = {
    val body = request.body
    val parsed: Seq[NameValuePair] = URLEncodedUtils.parse(new URI("http://example.com/?" + body), "Shift_JIS")
    val newBody = parsed.map { pair ⇒ (pair.getName, Seq(pair.getValue)) }.toMap
    new Request[Map[String, Seq[String]]] {
      override def body: Map[String, Seq[String]] = newBody

      override def uri: String = request.uri

      override def remoteAddress: String = request.remoteAddress

      override def queryString: Map[String, Seq[String]] = request.queryString

      override def method: String = request.method

      override def headers: Headers = request.headers

      override def path: String = request.path

      override def version: String = request.version

      override def tags: Map[String, String] = request.tags

      override def id: Long = request.id
    }
  }
}

然后使用request转换原始PercentEncoding并定义隐式请求值而不是原始request

def hogehoge = Action(parse.tolerantText) { request ⇒
    implicit val newRequest = PercentEncoding.extractSJISRequest(request)
...
}

它有效。