bl双性强迫侵犯h_国产在线观看人成激情视频_蜜芽188_被诱拐的少孩全彩啪啪漫画

Scala下Play框架學習筆記(Bodyparsers)-創新互聯

什么是Body Parsers

成都創新互聯2013年開創至今,先為輝縣等服務建站,輝縣等地企業,進行企業商務咨詢服務。為輝縣企業網站制作PC+手機+微官網三網同步一站式服務解決您的所有建站問題。

     一個HTTP請求是一個頭部后面緊隨著一個body,頭部很小,可以在內存中緩存,因此Play的模型中使用了RequestHeader這個類。Body有時候也可能很長,以致于不能緩存,反而作為一種流而被建模。但是,許多請求體的有效載荷是小的,可以在內存中建模。因此描繪body流對于一個內存中的對象,Play提供 BodyParser

由于Play是一個異步框架,傳統的InputStream方法不能用來讀請求體,當你調用read方法時,輸入流被阻塞,調用它的線程必須等到數據可用為止。Play使用一個異步流的庫 Akka Streams ,它是 Reactive Streams 的一個實現,是一個允許許多異步流API無縫協同工作的SPI。因此雖然傳統的InputStream不適合用在Play上,但是Akka Streams以及以Reactive Streams為核心的整個異步庫的整個生態系統能提供你一切所需。

更多關于Actions

   之前我們說過,Action是一個Request => Result類型的函數, 這并不完全正確,讓我們更仔細地看一下Action這個特質:

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

 BodyParser[A] , 另外Request[A]可以被定義如下:

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

  A類型是請求體的類型,我們可以用任何Scala的類型來作為請求體的類型,例如 StringNodeSeqArray[Byte]JsonValue或者java.io.File,只要有一個body parser能夠處理它就行。

  總而言之,一個Action[A]會使用一個BodyParser[A]來從HTTP請求中檢索A類型的值,來建立傳遞給Action代碼的request[A]類型的對象。

使用內置的body parsers

  許多典型的web apps都不需要使用客戶端的body parsers,它們能使用Play內置的body parsers正常工作。包括JSON、XML、表單的解析器,還包括把plain text當做String來處理,把byte當做byteString來處理。

默認的body parser

   當沒有明確指定一個body parser的時候,默認的body parser會根據頭部的content-type來解析body。舉例來說,content-type是Application/json類型的話,會被解析成JSValue,content-type為application/x-www-form-urlencoded類型的會被解析成Map[String, Seq[String]]。

  默認解析器產生的AnyContent類型的body,AnyContent能通過as類方法來支持各種類型,譬如asJson,返回body類型的一個Option類型:

def save = Action { request =>
  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/plain:通過asText轉換成String。

   application/json:通過asJson轉換成JSValue。

   application/xml,text/xml或者application/XXX+xml:通過asXML轉換成scala.xml.NodeSeq

   application/x-www-form-urlencoded:通過asFormUrlEncoded轉換成Map[String, Seq[String]]

   multipart/form-data:通過asMultipartFormData轉換成MultipartFormData

   任何其他的類型:通過asRaw轉換成rawBuffer。

     默認的body parser,出于性能的考慮,如果請求方法中沒有定義一個有意義的body,就不會解析該請求方法的body,默認body parser只解析post、put、patch請求,而不會解析get、head、delete請求,如果要為這些方法解析請求體,就需要使用Anycontent Body Parser。

選擇顯式的body parser

    如果需要顯式地指定body parser,就需要向Action的apply或async方法傳遞一個body parser。

    Play提供了許多框架之外的body parser,通過用Controllers引入 BodyParsers.parse 對象來實現。舉例說明,定義一個期望得到json body的Action如下:

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

    注意到現在body的類型是JSValue,當它不再是Option類型時,工作變得相對簡單。沒有Option類型的原因是json body parser要驗證一個請求有一個application/json的content-type,如果請求沒達到期望,然后回送415 Unsupported Media Type應答。因此我們在Action代碼中不用再次校驗。

    客戶端必須發送正確的content-type頭部,同時附上他們的請求。如果你想更輕松點,可以使用tolerantJson,這將會忽略content-type,嘗試把body解析成json格式:

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

    另一個例子是把請求體放在文件里:

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

    抽取用戶名,給每一個用戶一個獨有的文件:

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

    我們不是真正寫一個自己的body parser,而是結合已有的body parser而已, 這已經足夠了,能涵蓋大多數的實例。

大內容長度

    給予文本的body parser,譬如 textjsonxml或者formUrlEncoded這些,使用大內容長度限制,因為他們要將所有內容加載到內存,默認的能解析的大內容長度是100KB,通過指定application.conf中的play.http.parser.maxMemoryBuffer就可以實現:

play.http.parser.maxMemoryBuffer=128K

     對于一個解析器而言,在磁盤上的緩沖內容,譬如raw parser或者multipart/form-data,大內容長度通過play.http.parser.maxDiskBuffer這一屬性指定,默認10MB。為了數據域的統計,multipart/form-data解析器強制指定了文本大長度這一屬性。

     在Action中也可以修改默認大長度:

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

寫一個自定義的body parser:

     通過實現body parser特質,可以實現一個自定義的body parser,body parser特質定義如下:

trait BodyParser[+A] extends (RequestHeader => Accumulator[ByteString, Either[Result, A]])

     這個特質傳入的是一個RequestHeader對象,用來驗證請求的合法性,只有得到content-type,請求才能被正確解析。特質的返回類型是Accumulator,一個accumulator在 Akka Streams Sink中是輕量級的。一個accumulator會異步地將元素流匯集到result中,這可以通過在 Akka Streams Source中傳遞來執行。當accumulator結束工作的時候,會返回一個Future對象,這就相當于Sink[E, Future[A]],一個類的封裝類,不過有一個大的區別是,Accumulator提供便利的方法,如mapmapFuturerecover等。處理的是Result類型,因此好像是一個promise,可是Sink實際上所有類似的操作都被封裝在mapMaterializedValue回調里。

     Apply方法返回的accumulator產生ByteString類型的元素。這些實際上是Bytes數組,但和byte[]又有所區別, ByteString是不可變的,譬如切分和追加等操作都是在常量時間內完成的。

  如果accumulator的返回類型是Either[Result, A] ,那么它會返回一個Result類型或A類型。A一般是拋出異常時返回的錯誤類型,這些錯誤包括解析失敗、content-type和body parser接受的類型不匹配,或者緩沖區溢出。如果body parser 返回Result類型,它會縮短Action的過程,body parsers的Result會馬上返回,Action永遠不會被調用。


定位另一處的body

  一個普通的用例是,當你向解析一個body,并且你希望在另一個地方流式化,此時需要自定義一個body parser:

import javax.inject._import play.api.mvc._import play.api.libs.streams._import play.api.libs.ws._import scala.concurrent.ExecutionContextimport akka.util.ByteStringclass MyController @Inject() (ws: WSClient)(implicit ec: ExecutionContext) {

 def forward(request: WSRequest): BodyParser[WSResponse] = BodyParser { req =>
  Accumulator.source[ByteString].mapFuture { source =>
   request     // TODO: stream body when support is implemented
    // .withBody(source)
    .execute()
    .map(Right.apply)
  }
 }

 def myAction = Action(forward(ws.url("https://example.com"))) { req =>
  Ok("Uploaded")
 }}


通過Akka Streams自定義解析

   在極少數情況下會通過Akka Streams來寫一個自定義解析器。通常先在ByteString中緩存body是沒問題的,另一種更簡易的途徑在body上是使用必要的方法和隨機存取。

   當然也有不適合的時候,如果你的body需要解析的內容太長以致于內存中不能匹配合適的空間,這時候你需要寫一個自定義解析器。

   在來自ByteStrings的流的Parsing Lines下建立起來的CSV Parser,具體使用demo如下,文檔來自于Akka Streams cookbook:

import play.api.mvc._import play.api.libs.streams._import play.api.libs.concurrent.Execution.Implicits.defaultContextimport akka.util.ByteStringimport 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)}

另外有需要云服務器可以了解下創新互聯scvps.cn,海內外云服務器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務器、裸金屬服務器、高防服務器、香港服務器、美國服務器、虛擬主機、免備案服務器”等云主機租用服務以及企業上云的綜合解決方案,具有“安全穩定、簡單易用、服務可用性高、性價比高”等特點與優勢,專為企業上云打造定制,能夠滿足用戶豐富、多元化的應用場景需求。

本文題目:Scala下Play框架學習筆記(Bodyparsers)-創新互聯
標題鏈接:http://vcdvsql.cn/article28/cdgdcp.html

成都網站建設公司_創新互聯,為您提供全網營銷推廣網站改版網站維護微信公眾號建站公司網站內鏈

廣告

聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯

成都seo排名網站優化