一个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. jeecms系统_自定义对象流程

    库内新增对象Products 的流程说明: 第一步: com.jeecms.cms.entity.assist.base下建立模型基础类,BaseCmsProducts.java com.jeecms ...

  2. LRU Cache数据结构简介

    什么是LRU Cache LRU是Least Recently Used的缩写,意思是最近最少使用,它是一种Cache替换算法. 什么是Cache?狭义的Cache指的是位于CPU和主存间的快速RAM ...

  3. PAT甲级——A1047 Student List for Course

    Zhejiang University has 40,000 students and provides 2,500 courses. Now given the registered course ...

  4. 左神算法书籍《程序员代码面试指南》——2_02在单链表和双链表中删除倒数第k个字节

    [题目]分别实现两个函数,一个可以删除单链表中倒数第K个节点,另一个可以删除双链表中倒数第K个节点.[要求]如果链表长度为N,时间复杂度达到O(N),额外空间复杂度达到O(1).[题解]从头遍历链表, ...

  5. Maven实战07_依赖

    1:依赖声明 <project> ... <dependencies> <dependency> <groupId>...</groupId> ...

  6. Jmeter分布式测试过程中遇到的问题及摘抄前辈问题汇总

    遇到的常见问题: 1.在Controller端上控制某台机器Run,提示"Bad call to remote host". 解决方法:检查被控制机器上的jmeter-server ...

  7. eclipse设置提示信息

    1.设置 java 文件的代码提示功能  .abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ () 2.设置 xml 文件的代码提示功能 打 开 ...

  8. Leetcode137. Single Number II只出现一次的数字2

    给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次.找出那个只出现了一次的元素. 说明: 你的算法应该具有线性时间复杂度. 你可以不使用额外空间来实现吗? 示例 1: 输入: ...

  9. phpstudy安装好之后mysql无法启动(亲测可行)

    安装好phpstudy后,Apache可以启动,Mysql无法启动. 尝试解决办法:可能是之前已经装过Mysql,要把系统服务里面的MySQL删除,留下MySQLa服务. 在cmd命令行下输入:sc ...

  10. JavaScript中用var和不用var的区别

    Javascript声明变量的,虽然用var关键字声明和不用关键字声明,很多时候运行并没有问题,但是这两种方式还是有区别的.可以正常运行的代码并不代表是合适的代码. varnum=1; 是在当前域中声 ...