[Play Framework]Body parsers——请求主体解析器

来源:互联网 发布:手机淘宝关注是收藏吗 编辑:程序博客网 时间:2024/05/01 23:06

什么是请求主体解析器

通常HTTP的PUT和POST请求都包含一个主体(body)。该主体可以是任意格式的,并且通过‘Content-Type‘定义其具体格式。在Play中,请求主体解析器将这些请求的主题内容转换为对应的Scala可以直接读取/操作的值。
虽然,HTTP请求的主体可以是非常庞大的,并且请求主体解析器通常也不能一直等待并将其载入到系统内存中。BodyParser[A]实际上是基于Iteratee[Array[Byte],A]的,这就意味着可以获取大量字节码并计算其对应的A类型的Scala值。
考虑以下几个例子:

  • 文本的请求主体解析器可以将字节码转换成为String,并且将该值作为Iteratee[Array[Byte],A]结果返回;
  • 文件的请求主体解析器可以存储文本的各个部分至本地,并且返回一个作为Iteratee[Array[Byte],File]结果的java.io.File类型的文本引用;
  • 一个S3的请求主体解析器可以将请求主体的内容推送至Amazon S3服务器,并且返回一个作为Iteratee[Array[Byte],S3ObjectId]结果的S3服务器Id对象。

作为请求主体解析器的附加功能,它也可以用来在解析请求实体之前访问HTTP请求头,并且适时的进行一些预检查。例如:实体解析器能够检查某些HTTP头并进行适当的集合操作。

其实请求主体解析器并不是真正意义上的Iteratee[Array[Byte],A],而是Iteratee[Array[Byte],Either[Result,A]],因此,请求主体解析器在它无法正确的计算出正确的请求值时,它具有直接传递HTTP请求结果的职责(例如,返回400 BAD_REQUEST,412 PRECONDITION_FAILED或者413 REQUEST_ENTITY_TOO_LARGE)。

一旦请求主体解析器完成了它的工作并且返回了对应A类型的值,对应的Action方法就根据解析出的HTTP请求中的值计算出相应的结果。

更多Action的内容

在之前提到Action是一个Request=>Result的方法。这样说其实并不完全正确。我们来仔细审视以下Action trait:

trait Action[A] extends (Request[A] => Result) {  def parser: BodyParser[A]}

A为泛化类型,Action特质中必须定义一个BodyParser[A]。对于Request[A]是这样定义的:

trait Request[+A] extends RequestHeader {  def body: A}

A为请求主体的类型。请求主体的类型可以是任意的Scala类型,例如:String,NodeSeq,Array[Byte],JsonValue或者java.io.Fil,只要是请求主体解析器能够处理的类型都可以。

综上所述,Action[A]使用了BodyParser[A]来从HTTP请求中获取类型A的值,并且构建一个Request[A]对象有Action方法进行处理。

定义请求实体解析器 parser: AnyContent

在以上的例子中,没有特例化请求实体解析器。那么默认情况下它是如何工作的?默认情况下Play会获取一个play.api.mvc.AnyContent的实例。
该请求主体解析器会检查Content-Type请求头并且确定所处理的请求主体的类型:

  • Text/plain——String
  • Application/json——JsValue
  • application/xml, text/xml or application/XXX+xml——NodeSeq
  • application/form-url-encoded——Map[String, Seq[String]]
  • multipart/form-data——MultipartFormData[TemporaryFile]
  • any other content typ——RawBuffer
    例如:
def save = Action { request =>  val body: AnyContent = request.body  val textBody: Option[String] = body.asText  // Expecting text body  textBody.map { text =>    Ok("Got: " + text)  }.getOrElse {    BadRequest("Expecting text/plain request body")  }}

特别指定请求主体解析器

Play定义的请求主体解析器在play.api.mvc.BodyParsers.parse中。例如,定义某个Action来处理某个文本主体:

def save = Action(parse.text) { request =>  Ok("Got: " + request.body)}

这段代码看似简单。其实一旦出现了某些错误,通常会得到一个400 BAD_REQUEST。我们没有必要在Action里定义检查的代码,我们可以假定request.body包含了String的内容实体。

我们也可以通过以下方式处理:

def save = Action(parse.tolerantText) { request =>  Ok("Got: " + request.body)}

以上代码不会检测Content-Type中的内容,并直接使用Request的主体内容为String类型。

> tolerant就是提供对应各种实体解析方式的方法

以下是用于文件存储的实例:

def save = Action(parse.file(to = new File("/tmp/upload"))) { request =>  Ok("Saved the request content to " + request.body)}

请求主体解析器的组合使用

在上述例子中,所有的请求主体都存放在同一个文件中。该问题的处理需要我们编辑一个自定义的主体解析器,该解析器可以从请求的Session中抽取出用户名或其他的一些信息。

val storeInUserFile = parse.using { request =>  request.session.get("username").map { user =>    file(to = new File("/tmp/" + user + ".upload"))  }.getOrElse {    sys.error("You don't have the right to upload here")  }}def save = Action(storeInUserFile) { request =>  Ok("Saved the request content to " + request.body)}

此处我们并没有编写特定的主体解析器,仅仅是结合了已有的解析器,让它们协同完成工作。通常情况下,这种做法是足以应付绝大多数的用例的。

实体内容长度限制

基于文本的请求主体解析器(例如 text,json,xml或者formUrlEncoded)通常会对文本长度有最长长度的限定,这样做的目的在于使得该内容可以全部载入到内存中。

文本内容的最大长度在Play中默认为100KB,这个值用户可以通过以下方式修改:

// Accept only 10KB of data.def save = Action(parse.text(maxLength = 1024 * 10)) { request =>  Ok("Got: " + text)}
0 0
原创粉丝点击