2015-03-25 53 views
0

我該如何重寫urlFormEncoded解析器以在Play 2.3.x(Scala)中使用另一個字符集?覆蓋字符集的urlFormEncoded解析器 - Play Framework 2.3.x

我正在寫Scala的Play framework 2.3.x在日文環境下工作的BBS軟件。

問題是關於charset:客戶端POSTs請求與編碼在Shift-JIS - 日本着名字符集編碼的url編碼(也就是「百分比編碼」)形式參數 - 但播放解碼爲UTF-8。這是可預測的,因爲它在https://github.com/playframework/playframework/blob/2.3.x/framework/src/play/src/main/scala/play/api/mvc/ContentTypes.scala#L515中被硬編碼。

我不能修改BBS客戶端的編碼,因爲它是事實上的標準。

因此,我必須重寫或覆蓋使用Shift_JIS的網址編碼解碼器。

可能有一些解決方案:

  1. 手工打造,從修改後的源代碼播放和使用它。
  2. 攔截請求並解碼主體爲Shift_JIS,然後將主體重新編碼爲UTF-8
  3. 創建自定義分析器,它擴展play.api.mvc.BodyParsers.parse並使用它。

我認爲最有希望的選擇是3,但是我無法創建object,它延伸play.api.mvc.BodyParsers.parse

那麼,我該如何重寫默認解析器並使用它(或者還有其他更好的解決方案)?

謝謝。

回答

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

我還沒有嘗試上面的代碼,但我希望它的作品。

+0

它似乎很好,但沒有運行; '[錯誤]方法字符集需要一個穩定的,不可變的值 [錯誤]重寫def charset = rh.charset或者else some(defaultCharset)//或者總是使用Some(defaultCharset)如果你想要' – Qwilas 2015-03-25 13:05:04

+0

我更新了答案至少編譯。 – kawty 2015-03-25 15:30:24

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

然後,使用PercentEncoding轉換原始request並定義隱含的請求值,而不是原始request

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

它的工作原理。