一个http请求是一个请求头后面跟着一个请求体,头部信息比较短,可以安全的缓存在内存中,在Play中头部信息使用RequestHeader类进行建模。请求体的内容可能较大,使用流stream的形式进行建模。

然而,有许多请求体是比较小的,因此Play提供了一个BodyParser抽象用于将流中的信息转换为内存对象。由于Play是一个异步框架,对流的处理不使用传统的InputStream,因为该方法在读取时会阻塞

整个线程直到数据读取完毕。Play中使用异步的Akka Stream进行处理,Akka Stream是Reactive Streams的一个实现。

一、使用play内部的解析器

1.在不指定解析器的情况下,play会使用内部的解析器根据请求头的Content-Type字段来判断解析后的类型,application/json会被解析为JsValue,application/x-www-form-urlencoded会被解析为Map[String, Seq[String]]

def save = Action { request: Request[AnyContent] =>
val body: AnyContent = request.body
val jsonBody: Option[JsValue] = body.asJson // Expecting json body
jsonBody.map { json =>
Ok("Got: " + (json \ "name").as[String])
}.getOrElse {
BadRequest("Expecting application/json request body")
}
}
  • text/plainString, accessible via asText.
  • application/jsonJsValue, accessible via asJson.
  • application/xmltext/xml or application/XXX+xmlscala.xml.NodeSeq, accessible via asXml.
  • application/x-www-form-urlencodedMap[String, Seq[String]], accessible via asFormUrlEncoded.
  • multipart/form-dataMultipartFormData, accessible via asMultipartFormData.
  • Any other content type: RawBuffer, accessible via asRaw.

2.指定解析器类型

def save = Action(parse.json) { request: Request[JsValue]  =>Ok("Got: " + (request.body \ "name").as[String])}

忽略content-type,尽力把请求体解析为json 类型

def save = Action(parse.tolerantJson) { request: Request[JsValue]  =>Ok("Got: " + (request.body \ "name").as[String])}

将请求体存为一个文件

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

之前是所有用户都存在一个文件里,现在每个用户都存一个文件

val storeInUserFile = parse.using { request =>
request.session.get("username").map { user =>
parse.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)
}

默认内存中最大缓存为100KB,但是可以通过application.conf进行设置: play.http.parser.maxMemoryBuffer=128K

而磁盘最大缓存默认为10MB, play.http.parser.maxDiskBuffer

也可以在函数中设置:

// 设置最大为10KB数据
def save = Action(parse.text(maxLength = 1024 * 10)) { request: Request[String] =>
Ok("Got: " + text)
}
def save = Action(parse.maxLength(1024 * 10, storeInUserFile)) { request =>
Ok("Saved the request content to " + request.body)
}

二、自定义一个解析器

1.当你不想解析时,可以把请求体转到别的地方

import javax.inject._
import play.api.mvc._
import play.api.libs.streams._
import play.api.libs.ws._
import scala.concurrent.ExecutionContext
import akka.util.ByteString class MyController @Inject() (ws: WSClient, val controllerComponents: ControllerComponents)
(implicit ec: ExecutionContext) extends BaseController { def forward(request: WSRequest): BodyParser[WSResponse] = BodyParser { req =>
Accumulator.source[ByteString].mapFuture { source =>
request
.withBody(source)
.execute()
.map(Right.apply)
}
} def myAction = Action(forward(ws.url("https://example.com"))) { req =>
Ok("Uploaded")
}
} 

2.通过akka Streams定制解析器

对akka Stream的讨论超出本文的内容,最好去参考akka的相关文档,https://doc.akka.io/docs/akka/2.5/stream/index.html?language=scala

以下例子是一个CSV文件的解析器,该实例基于 
https://doc.akka.io/docs/akka/2.5/stream/stream-cookbook.html?language=scala#parsing-lines-from-a-stream-of-bytestrings

import play.api.mvc.BodyParser
import play.api.libs.streams._
import akka.util.ByteString
import akka.stream.scaladsl._ val csv: BodyParser[Seq[Seq[String]]] = BodyParser { req => // A flow that splits the stream into CSV lines
val sink: Sink[ByteString, Future[Seq[Seq[String]]]] = Flow[ByteString]
// We split by the new line character, allowing a maximum of 1000 characters per line
.via(Framing.delimiter(ByteString("\n"), 1000, allowTruncation = true))
// Turn each line to a String and split it by commas
.map(_.utf8String.trim.split(",").toSeq)
// Now we fold it into a list
.toMat(Sink.fold(Seq.empty[Seq[String]])(_ :+ _))(Keep.right) // Convert the body to a Right either
Accumulator(sink).map(Right.apply)
}

PLAY2.6-SCALA(四) 请求体解析器的更多相关文章

  1. body-parser Node.js(Express) HTTP请求体解析中间件

    body-parser Node.js(Express) HTTP请求体解析中间件 2016年06月08日     781     声明 在HTTP请求中,POST.PUT和PATCH三种请求方法中包 ...

  2. body-parser 是一个Http请求体解析中间件

    1.这个模块提供以下解析器 (1) JSON body parser (2) Raw body parser (3)Text body parser (4)URL-encoded form body ...

  3. 爬虫笔记(四)------关于BeautifulSoup4解析器与编码

    前言:本机环境配置:ubuntu 14.10,python 2.7,BeautifulSoup4 一.解析器概述 如同前几章笔记,当我们输入: soup=BeautifulSoup(response. ...

  4. Solr系列五:solr搜索详解(solr搜索流程介绍、查询语法及解析器详解)

    一.solr搜索流程介绍 1. 前面我们已经学习过Lucene搜索的流程,让我们再来回顾一下 流程说明: 首先获取用户输入的查询串,使用查询解析器QueryParser解析查询串生成查询对象Query ...

  5. XML解析器(转)

    常见C/C++ XML解析器有tinyxml.XERCES.squashxml.xmlite.pugxml.libxml等等,这些解析器有些是支持多语言的,有些只是单纯C/C++的.如果你是第一次接触 ...

  6. 解析器组件和序列化组件(GET / POST 接口设计)

    前言 我们知道,Django无法处理 application/json 协议请求的数据,即,如果用户通application/json协议发送请求数据到达Django服务器,我们通过request.P ...

  7. python 之网页解析器

    一.什么是网页解析器 1.网页解析器名词解释 首先让我们来了解下,什么是网页解析器,简单的说就是用来解析html网页的工具,准确的说:它是一个HTML网页信息提取工具,就是从html网页中解析提取出“ ...

  8. XML的四种解析器(dom_sax_jdom_dom4j)原理及性能比较[收藏]

    1)DOM(JAXP Crimson解析器)    DOM是用与平台和语言无关的方式表示XML文档的官方W3C标准.DOM是以层次结构组织的节点或信息片断的集合.这个层次结构允许开发人员在树中寻找特定 ...

  9. SpringMVC入门案例及请求流程图(关于处理器或视图解析器或处理器映射器等的初步配置)

    SpringMVC简介:SpringMVC也叫Spring Web mvc,属于表现层的框架.Spring MVC是Spring框架的一部分,是在Spring3.0后发布的 Spring结构图 Spr ...

随机推荐

  1. 常用 docker 容器 使用

    mongo: 单点 docker run -idt --name=mongo --restart=always -p : -v /home/hylas/opt/mongo/data:/data/db ...

  2. DVWA 之low级别sql注入

    将Security level设为low,在左侧列表中选择“SQL Injection”,然后在右侧的“User ID”文本框中输入不同的数字就会显示相应的用户信息. 我们首先需要判断这里所传输的参数 ...

  3. if _name_ == " _main_"

    1.作用 py文件有2种使用方法,第1是自己本脚本自己独立执行:第2是被import到其他文件脚本中执行. if  _name_ == " _main_" 该语句控制其他下一步的脚 ...

  4. Python基础-列表、元组、字典、字符串(精简解析),全网最齐全。

    一.列表 =====================================================1.列表的定义及格式: 列表是个有序的,可修改的,元素用逗号隔开,用中括号包围的序列 ...

  5. css3之文本和颜色功能之text-overflow,word-wrap

    语法 text-overflow: clip|ellipsis|string; clip修剪文本.ellipsis显示省略符号来代表被修剪的文本.string使用给定的字符串来代表被修剪的文本. 效果 ...

  6. Tensorflow通过CNN实现MINST数据分类

    import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data mnist = input_dat ...

  7. StoryBoard拆分(Storyboard References)

    https://www.jianshu.com/p/78dc76204c8e iOS UI篇10- Storyboard(Storyboard Reference) https://www.aliyu ...

  8. Centos Apache 多站点配置

    首先明白APACHE配置文件位置 /etc/httpd/ 系统会自动加载 "/etc/httpd/conf.d" 目录下面的 "*.conf"文件 创建多个 & ...

  9. 关于github 代码管理,协作开发

    公司要用github 进行项目管理,  了解了一下github相关权限管理. 并做笔记如下: 个人账户可以建立公有/私有  repository ,  公有的全天下的人都能看到,私有的全天下人都看不到 ...

  10. C# EventWaitHandle用法

    waithander就是用来阻塞当前线程的,然后通过set()方法放开 namespace waithandler { class Program { //static EventWaitHandle ...