Parsing play2 scala中的自定义multipartFormData解析器,用于处理数据部件上的多个编码
我正在尝试实现一个定制的multipartFormData数据解析器来处理来自Sendgrid API的回调。回调是一个多部分请求,其中数据部分可以用不同的编码进行编码:例如Parsing play2 scala中的自定义multipartFormData解析器,用于处理数据部件上的多个编码,parsing,scala,playframework,playframework-2.1,sendgrid,Parsing,Scala,Playframework,Playframework 2.1,Sendgrid,我正在尝试实现一个定制的multipartFormData数据解析器来处理来自Sendgrid API的回调。回调是一个多部分请求,其中数据部分可以用不同的编码进行编码:例如utf-8或ISO-8859-1 Sendgrid提供了一个charsets字段,它只是一个json对象,用于解释每个字段的编码方式: {"to":"UTF-8","html":"ISO-8859-1","subject":"UTF-8","from":"UTF-8","text":"ISO-8859-1"} 我目前从Da
utf-8
或ISO-8859-1
Sendgrid提供了一个charsets字段,它只是一个json对象,用于解释每个字段的编码方式:
{"to":"UTF-8","html":"ISO-8859-1","subject":"UTF-8","from":"UTF-8","text":"ISO-8859-1"}
我目前从DataPart中提取字符集,如下所示:
val charsets = extract(request.body.dataParts, "charsets", _.as[Charsets]).getOrElse(Charsets(Some(""), Some(""), Some(""), Some(""), Some("")))
def extract[T](env: Map[String, Seq[String]], key: String, conv: JsValue => T): Option[T] = {
env.get(key).flatMap(_.headOption).map(Json.parse).map(conv)
}
case class Charsets(to: Option[String], html: Option[String], subject: Option[String], from: Option[String], text: Option[String])
object Charsets {
implicit val charsetReads = Json.format[Charsets]
}
但这是行不通的,因为解析器可能设置了错误的编码
原始handleDataPart
硬编码为使用utf-8
def handleDataPart: PartHandler[Part] = {
case headers @ Multipart.PartInfoMatcher(partName) if !Multipart.FileInfoMatcher.unapply(headers).isDefined =>
Traversable.takeUpTo[Array[Byte]](DEFAULT_MAX_TEXT_LENGTH)
.transform(Iteratee.consume[Array[Byte]]().map(bytes => DataPart(partName, new String(bytes, "utf-8"))))
.flatMap { data =>
Cont({
case Input.El(_) => Done(MaxDataPartSizeExceeded(partName), Input.Empty)
case in => Done(data, in)
})
}
}
所以我想做的是开始提取charsets对象,然后在创建Dataparts时使用它,或者不是为每个字段创建字符串,而是创建一个数组[Byte],然后在我的控制器中处理字符串的创建。也许还有别的办法?你将如何解决这个问题?我感觉被卡住了,需要一些指导。从我的好友Tor那里得到了答案!工作起来很有魅力
class Parser @Inject()(mailRequestService: MailRequestService, surveyService: SurveyService) extends Controller {
val UTF8 = "UTF-8"
def parseMail = Action.async(rawFormData) { request =>
val charsets = extract(request.body.dataParts, "charsets", _.as[Charsets]).getOrElse(Charsets(Some(""), Some(""), Some(""), Some(""), Some("")))
val envelope = extract(request.body.dataParts, "envelope", _.as[Envelope]).getOrElse(Envelope(Nil, ""))
val sendgrid = SendgridMail(
extractString(request.body.dataParts, "text", charsets),
extractString(request.body.dataParts, "html", charsets),
extractString(request.body.dataParts, "from", charsets),
extractString(request.body.dataParts, "to", charsets),
charsets,
envelope
)
val simple = mailRequestService.createNewMail(sendgrid).map {
result =>
result.fold(
exception => throw new UnexpectedServiceException("Could not save sendgrid mail: "+sendgrid, exception),
mail => Ok("Success")
)
}
simple
}
def extractString(data: Map[String, Seq[Array[Byte]]], key: String, charsets: Charsets): Option[String] = {
play.Logger.info("data = " + data)
data.get(key).flatMap(_.headOption).map { firstValue =>
(charsets, key) match {
case (charset, "text") if charset.text.isDefined =>
val cset = java.nio.charset.Charset.forName(charset.text.get)
Some(new String(firstValue, cset))
case (charset, "html") if charset.html.isDefined =>
val cset = java.nio.charset.Charset.forName(charset.html.get)
Some(new String(firstValue, cset))
case (charset, "from") if charset.from.isDefined =>
val cset = java.nio.charset.Charset.forName(charset.from.get)
Some(new String(firstValue, cset))
case (charset, "to") if charset.to.isDefined =>
val cset = java.nio.charset.Charset.forName(charset.to.get)
Some(new String(firstValue, cset))
case _ => Some("")
}
}.getOrElse(Some(""))
}
/**
* 1. Retrieve value for key eg. envelope
* 2. Use flatmap to flatten the structure so that we do not get Option[Option[_] ]
* 3. Call map and use JsonParse on the String to get JsValue
* 4. Call map and use provided method eg _.as[Envelope] that results in T, in this case Envelope
* 5. RETURN!
*/
def extract[T](env: Map[String, Seq[Array[Byte]]], key: String, conv: JsValue => T): Option[T] = {
env.get(key).flatMap(_.headOption.map(a => new String(a, "UTF-8"))).map(Json.parse).map(conv)
}
def findSurveyByEmail(email: String): Future[Option[Survey]] = {
surveyService.findSurveyByEmail(email)
}
def handler: parse.Multipart.PartHandler[Part] = {
case headers @ PartInfoMatcher(partName) if !FileInfoMatcher.unapply(headers).isDefined =>
Traversable.takeUpTo[Array[Byte]](1024 * 100)
.transform(Iteratee.consume[Array[Byte]]().map(bytes => FilePart(partName, "", None, bytes)))
.flatMap { data =>
Cont({
case Input.El(_) => Done(data, Input.Empty)
case in => Done(data, in)
})
}
case headers => Done(BadPart(headers), Input.Empty)
}
def rawFormData[A]: BodyParser[RawDataFormData] = BodyParser("multipartFormData") { request =>
Multipart.multipartParser(handler)(request).map { errorOrParts =>
errorOrParts.right.map { parts =>
val data = parts.collect { case FilePart(key, _, _, value: Array[Byte]) => (key, value) }.groupBy(_._1).mapValues(_.map(_._2))
RawDataFormData(data)
}
}
}
}
case class RawDataFormData(dataParts: Map[String, Seq[Array[Byte]]])
谢谢你,托尔