Error in sending data as multi form data in play /

2019-08-11 04:18发布

问题:

This is my code the same code is working in play 2.1.5 but I am unable to create war file in play 2.1.5 so I switched to play 2.4.3 and now in response it is coming that 400 bad request client is sending wrong syntactically even post rest api is not hitting. Can some tell me what I am missing?

import play.api._
import play.api.libs.ws._
import play.api.mvc._
//import javax.inject.Inject
import play.api.Play.current
import scala.concurrent.ExecutionContext.Implicits.global
import play.api.libs.ws.WSAuthScheme
import com.ning.http.client.AsyncHttpClient
import com.ning.http.client.multipart.FilePart
import com.ning.http.client.multipart.StringPart
import java.io.File
import org.apache.commons.codec.binary.Base64.encodeBase64

class Application extends Controller {

  def index = Action {
    Ok(views.html.index("Your new application is ready."))
  }



def postUpload=Action(parse.multipartFormData){request=>

   val groupingType= request.body.dataParts("Grouping type").mkString
    val email=request.body.dataParts("NotificationRecepients").mkString
    val custId=request.body.dataParts("CustomerId").mkString
    val custIdFinal:Int=custId.toInt


   request.body.file("file").map { file =>

  val file1=file.ref.file.getAbsolutePath;


  val fileName = file.filename 
    val contentType = file.contentType

  //file.ref.moveTo(new File("/home/kanchan/Desktop/"+fileName),true)


     val user = "myUser";
      val password = "myPassword";

 val encodedCredentials =
      new String(encodeBase64("%s:%s".format(user, password).getBytes))

    val asyncHttpClient:AsyncHttpClient =WS.client.underlying
    val postBuilder = asyncHttpClient.preparePost(url)
    val builder = postBuilder
      .addHeader("Authorization", "Basic " + encodedCredentials)
      .addBodyPart(new StringPart("selectedGroupType", "Functional Grouping", "UTF-8"))
      .addBodyPart(new StringPart("mailRecipients", "kancgupt@cisco.com", "UTF-8"))
      .addBodyPart(new StringPart("selectedFile", "Sample_group_upload_file.xlsx", "UTF-8"))
      .addBodyPart(new FilePart("file",new File("/home/kanchan/Downloads/Sample_group_upload_file.xlsx")))

    val response = asyncHttpClient.executeRequest(builder.build()).get();
   Ok(response.getResponseBody)


  }.getOrElse {

     Ok( "Missing file")

  }
}
}

Play version 2.4.3 sbt:0.13.8

getting following error: Apache Tomcat/6.0.39 - Error report

HTTP Status 400 - Bad Request

type Status report

message Bad Request

description The request sent by the client was syntactically incorrect.

Apache Tomcat/6.0.39

回答1:

You describing something really strange. I took your code, curl, REST server echo (http://httpbin.org, exactly http://httpbin.org/post url), create new play-scala application with the 2.4.3 play. It works like a charm.

Code:

package controllers

import play.api._
import play.api.libs.ws._
import play.api.mvc._
import play.api.Play.current
import scala.concurrent.ExecutionContext.Implicits.global
import play.api.libs.ws.WSAuthScheme
import com.ning.http.client.AsyncHttpClient
import com.ning.http.client.multipart.FilePart
import com.ning.http.client.multipart.StringPart
import java.io.File
import org.apache.commons.codec.binary.Base64.encodeBase64

class Original extends Controller {

  def postUpload = Action(parse.multipartFormData) { request =>

    val groupingType = request.body.dataParts("Groupingtype").mkString
    val email = request.body.dataParts("NotificationRecepients").mkString
    val custId = request.body.dataParts("CustomerId").mkString
    val custIdFinal: Int = custId.toInt

    request.body.file("file").map { file =>

      val file1 = file.ref.file.getAbsolutePath;

      val fileName = file.filename
      val contentType = file.contentType

      val url = "http://httpbin.org/post"

      val user = "myUser";
      val password = "myPassword";

      val encodedCredentials =
        new String(encodeBase64("%s:%s".format(user, password).getBytes))

      val asyncHttpClient: AsyncHttpClient = WS.client.underlying
      val postBuilder = asyncHttpClient.preparePost(url)
      val builder = postBuilder
        .addHeader("Authorization", "Basic " + encodedCredentials)
        .addBodyPart(new StringPart("selectedGroupType", groupingType, "UTF-8"))
        .addBodyPart(new StringPart("mailRecipients", email, "UTF-8"))
        .addBodyPart(new StringPart("selectedFile", fileName, "UTF-8"))
        .addBodyPart(new FilePart("file", new File(file1)))

      val response = asyncHttpClient.executeRequest(builder.build()).get();
      Ok(response.getResponseBody)

    }.getOrElse {

      Ok("Missing file")

    }
  }
}

curl command (from the project root folder):

curl -i -X POST \
   -H "Content-Type:multipart/form-data" \
   -F "Groupingtype=Groupingtype" \
   -F "NotificationRecepients=NotificationRecepients" \
   -F "CustomerId=123" \
   -F "file=@app/controllers/Application.scala" \
 'http://localhost:9000/post'

curl response:

HTTP/1.1 100 Continue

HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Date: Tue, 06 Oct 2015 18:38:11 GMT
Content-Length: 3122

{
  "args": {}, 
  "data": "", 
  "files": {
    "file": "package controllers..."
  }, 
  "form": {
    "mailRecipients": "NotificationRecepients", 
    "selectedFile": "Application.scala", 
    "selectedGroupType": "Groupingtype"
  }, 
  "headers": {
    "Accept": "*/*", 
    "Authorization": "Basic bXlVc2VyOm15UGFzc3dvcmQ=", 
    "Content-Length": "3215", 
    "Content-Type": "multipart/form-data; boundary=2EmAF3UE9xSuA6KQpUS3q-Ohzkp0f_7-8", 
    "Host": "httpbin.org", 
    "User-Agent": "AHC/1.0"
  }, 
  "json": null, 
  "origin": "111.111.111.111", 
  "url": "http://httpbin.org/post"
}

Try to create new simple project and use my code - then migrate yours in to this new project - maybe you will find something. It could be some wrong migration steps from 2.1.5 or so.

my build.sbt

name := """scala-send-multipart-clean"""

version := "1.0-SNAPSHOT"

lazy val root = (project in file(".")).enablePlugins(PlayScala)

scalaVersion := "2.11.6"

libraryDependencies ++= Seq(
  jdbc,
  cache,
  ws,
  specs2 % Test
)

resolvers += "scalaz-bintray" at "http://dl.bintray.com/scalaz/releases"

// Play provides two styles of routers, one expects its actions to be injected, the
// other, legacy style, accesses its actions statically.
routesGenerator := InjectedRoutesGenerator

my project/plugins.sbt

// The Play plugin
addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.4.3")

// web plugins

addSbtPlugin("com.typesafe.sbt" % "sbt-coffeescript" % "1.0.0")

addSbtPlugin("com.typesafe.sbt" % "sbt-less" % "1.0.6")

addSbtPlugin("com.typesafe.sbt" % "sbt-jshint" % "1.0.3")

addSbtPlugin("com.typesafe.sbt" % "sbt-rjs" % "1.0.7")

addSbtPlugin("com.typesafe.sbt" % "sbt-digest" % "1.1.0")

addSbtPlugin("com.typesafe.sbt" % "sbt-mocha" % "1.1.0")

my project/build.properties

#Activator-generated Properties
#Wed Oct 07 00:44:35 EEST 2015
template.uuid=7da33b5a-3aef-4d2e-b318-dae93632b999
sbt.version=0.13.8

java version "1.8.0_40", updated to the "build 1.8.0_60-b27" - no difference.

I am getting this [StringPart name=selectedGroupType contentType=UTF-8 charset=US-ASCII tranferEncoding=8bit contentId=null dispositionType=null, StringPart name=mailRecipients contentType=UTF-8 charset=US-ASCII tranferEncoding=8bit contentId=null dispositionType=null, StringPart name=selectedFile contentType=UTF-8 charset=US-ASCII tranferEncoding=8bit contentId=null dispositionType=null, FilePart name=file contentType=application/octet-stream charset=null tranferEncoding=binary contentId=null dispositionType=null filename=Sample_group_upload_file.xlsx]

It is the way toString is realised in the Parts. That's all, it does not tell something about "correct" or "not correct".

My results for

println(builder.build().getParts())

is

[StringPart name=selectedGroupType contentType=UTF-8 charset=US-ASCII tranferEncoding=8bit contentId=null dispositionType=null, StringPart name=mailRecipients contentType=UTF-8 charset=US-ASCII tranferEncoding=8bit contentId=null dispositionType=null, StringPart name=selectedFile contentType=UTF-8 charset=US-ASCII tranferEncoding=8bit contentId=null dispositionType=null, FilePart name=file contentType=application/octet-stream charset=null tranferEncoding=binary contentId=null dispositionType=null filename=multipartBody1697314279257602964asTemporaryFile]

As you see it very similar to yours, but if you will output names and values for the string part, I am pretty sure you will receive both:

import scala.collection.JavaConversions._

... 

builder.build().getParts().map{ part =>
    if(part.isInstanceOf[StringPart]){
      val stringPart: StringPart = part.asInstanceOf[StringPart]
      println(stringPart.getName() + ":" + stringPart.getValue())
    }
}

results:

selectedGroupType:Groupingtype
mailRecipients:NotificationRecepients
selectedFile:Application.scala