Error in sending data as multi form data in play /scala - scala

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 Requesttype Status reportmessage Bad Requestdescription The request sent by the client was syntactically incorrect.Apache Tomcat/6.0.39

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

Related

PlaySpec not found in IntelliJ

Below is a Scala test of websocket:
import java.util.function.Consumer
import play.shaded.ahc.org.asynchttpclient.AsyncHttpClient
import play.api.inject.guice.GuiceApplicationBuilder
import play.api.test.{Helpers, TestServer, WsTestClient}
import scala.compat.java8.FutureConverters
import scala.concurrent.Await
import scala.concurrent.duration._
import org.scalatestplus.play._
class SocketTest extends PlaySpec with ScalaFutures {
"HomeController" should {
"reject a websocket flow if the origin is set incorrectly" in WsTestClient.withClient { client =>
// Pick a non standard port that will fail the (somewhat contrived) origin check...
lazy val port: Int = 31337
val app = new GuiceApplicationBuilder().build()
Helpers.running(TestServer(port, app)) {
val myPublicAddress = s"localhost:$port"
val serverURL = s"ws://$myPublicAddress/ws"
val asyncHttpClient: AsyncHttpClient = client.underlying[AsyncHttpClient]
val webSocketClient = new WebSocketClient(asyncHttpClient)
try {
val origin = "ws://example.com/ws"
val consumer: Consumer[String] = new Consumer[String] {
override def accept(message: String): Unit = println(message)
}
val listener = new WebSocketClient.LoggingListener(consumer)
val completionStage = webSocketClient.call(serverURL, origin, listener)
val f = FutureConverters.toScala(completionStage)
Await.result(f, atMost = 1000.millis)
listener.getThrowable mustBe a[IllegalStateException]
} catch {
case e: IllegalStateException =>
e mustBe an[IllegalStateException]
case e: java.util.concurrent.ExecutionException =>
val foo = e.getCause
foo mustBe an[IllegalStateException]
}
}
}
}
}
But compile is failing on line import org.scalatestplus.play._ with error :
Cannot resolve symbol scalatestplus
From https://www.playframework.com/documentation/2.8.x/ScalaTestingWithScalaTest I have added scalatest and play to build:
build.sbt:
name := "testproject"
version := "1.0"
lazy val `testproject` = (project in file(".")).enablePlugins(PlayScala)
resolvers += "scalaz-bintray" at "https://dl.bintray.com/scalaz/releases"
resolvers += "Akka Snapshot Repository" at "https://repo.akka.io/snapshots/"
scalaVersion := "2.12.2"
libraryDependencies ++= Seq( jdbc , ehcache , ws , guice , specs2 % Test)
// https://mvnrepository.com/artifact/com.typesafe.scala-logging/scala-logging
libraryDependencies += "com.typesafe.scala-logging" %% "scala-logging" % "3.9.2"
libraryDependencies ++= Seq(
"org.scalatestplus.play" %% "scalatestplus-play" % "3.0.0" % "test"
)
unmanagedResourceDirectories in Test <+= baseDirectory ( _ /"target/web/public/test" )
I've tried rebuilding the project and module in IntelliJ "build" option and "Build Option" when I right click on build.sbt but the import is not found.
sbt dist from Intellij "sbt shell" then File -> "Invalidate caches" with restart of IntelliJ seems to fix the issue
:Invalidate caches screenshot

Get content from Akka ResponseEntity in Scala

I do a GET HTTP call to a rest service which returns a json. I would like to parse the json to a scala object but here I got stuck. I am using the Akka api and I can't manage to retrieve the content from the Akka's ResponseEntity
Here is my sbt file:
name := "ScalaHttp"
version := "1.0"
scalaVersion := "2.11.8"
libraryDependencies ++={
val akkaV = "2.4.5"
Seq(
"com.typesafe.akka" %% "akka-http-core" % akkaV,
"com.typesafe.play" %% "play-json" % "2.4.0-M3"
)
}
And here is the app
import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.model.HttpRequest
import akka.stream.ActorMaterializer
import scala.concurrent.ExecutionContext.Implicits.global
object Sender {
def main(args: Array[String]): Unit = {
implicit val system = ActorSystem()
implicit val materializer = ActorMaterializer()
Http().singleRequest(HttpRequest(uri = "http://declinators.com/declinator?noun=komunikacja")) foreach {
y => println(y.entity)
println(y.entity.getContentType())
println(y.entity.contentType)
}
}
}
This prints:
HttpEntity.Strict(application/json,{"nominative":"komunikacja","genitive":"komunikacji","dative":"komunikacji","accusative":"komunikację","instrumental":"komunikacją","locative":"komunikacji","vocative":"komunikacjo"})
application/json
application/json
Here come the questions:
1. Why ResponseEntity supplies getContentType() and contentType()? They return the same thing.
2. Getting the contentyType is easy, you have two ways for doing it, but how can I get the content itself, so I can play (i.e. parse it using play) with the json!
You can use entity.data.toString for Binary content type, or the following code piece
data.decodeString(nb.charset.value)
Please follow the HttpEntity.Strict.toString implementation here for detail:
https://github.com/akka/akka/blob/master/akka-http-core/src/main/scala/akka/http/scaladsl/model/HttpEntity.scala#L316

Why does spray-can server not respond to a http request?

I'm trying to set a very basic HTTP server using spray-can. If I call the endpoint I've set a mapping for, I'm getting a timeout (although using a debugger I can see that the actor receives the message).
My source looks like this:
import akka.actor.{Actor, ActorRef, ActorSystem, Props}
import akka.io.IO
import spray.can.Http
import spray.http.{HttpMethods, HttpRequest, HttpResponse, Uri}
class App extends Actor {
implicit val system = context.system
override def receive = {
case "start" =>
val listener: ActorRef = system.actorOf(Props[HttpListener])
IO(Http) ! Http.Bind(listener, interface = "localhost", port = 8080)
}
}
class HttpListener extends Actor {
def receive = {
case _: Http.Connected =>
sender() ! Http.Register(self)
case HttpRequest(HttpMethods.GET, Uri.Path("/ping"), _, _, _) =>
HttpResponse(entity = "PONG")
}
}
object Main {
def main(args: Array[String]) {
val system = ActorSystem("my-actor-system")
val app: ActorRef = system.actorOf(Props[App], "app")
app ! "start"
}
}
Executing run shows:
> run
[info] Running Main
[INFO] [09/10/2014 21:33:38.839] [my-actor-system-akka.actor.default-dispatcher-3] [akka://my-actor-system/user/IO-HTTP/listener-0] Bound to localhost/127.0.0.1:8080
The following HTTP/1.1 500 Internal Server Error shows up when I hit http://localhost:8080/ping:
➜ ~ curl --include http://localhost:8080/ping
HTTP/1.1 500 Internal Server Error
Server: spray-can/1.3.1
Date: Wed, 10 Sep 2014 19:34:08 GMT
Content-Type: text/plain; charset=UTF-8
Connection: close
Content-Length: 111
Ooops! The server was not able to produce a timely response to your request.
Please try again in a short while!
My build.sbt looks like:
scalaVersion := "2.11.2"
resolvers += "spray repo" at "http://repo.spray.io"
libraryDependencies ++= Seq(
"io.spray" %% "spray-can" % "1.3.1",
"io.spray" %% "spray-routing" % "1.3.1",
"com.typesafe.akka" %% "akka-actor" % "2.3.5"
)
Any ideas on what I'm doing wrong?
case HttpRequest(HttpMethods.GET, Uri.Path("/ping"), _, _, _) =>
HttpResponse(entity = "PONG")
should be
case HttpRequest(HttpMethods.GET, Uri.Path("/ping"), _, _, _) =>
sender ! HttpResponse(entity = "PONG")
You are returning HttpResponse instead of sending a message to the sender.

Sending post with json using spray?

Sorry I can't manage to make this work: I need to add some json to a post, so following the documentation: http://spray.io/documentation/1.1-M8/spray-httpx/request-building/ :
import scala.util.{Success, Failure}
import akka.actor.{Props, ActorSystem}
import spray.can.client.DefaultHttpClient
import spray.client.HttpConduit
import spray.httpx.SprayJsonSupport
import spray.http._
import spray.json.JsonParser._
import spray.json._
import HttpMethods._
import HttpHeaders._
import MediaTypes._
import spray.httpx.RequestBuilding._
import scala.concurrent.ExecutionContext.Implicits.global
...
val req = HttpRequest(method = POST, uri = "/api/1.0/users/ping.json", entity = HttpEntity(`application/json`,"""{ "key"="whatever" }"""))
and it never compiles:
overloaded method value apply with alternatives:
[error] (optionalBody: Option[spray.http.HttpBody])spray.http.HttpEntity <and>
[error] (buffer: Array[Byte])spray.http.HttpEntity <and>
[error] (string: String)spray.http.HttpEntity
[error] cannot be applied to (spray.http.MediaType, String)
[error] val req = HttpRequest(method = POST, uri = "/api/1.0/users/ping.json", entity = HttpEntity(`application/json`,"""{ "key"="whatever"}"""))
Had the same problem and found the solution here:
https://github.com/spray/spray-json/blob/master/src/main/scala/spray/json/AdditionalFormats.scala#L30-41
This finally worked for me:
import spray.httpx.SprayJsonSupport
import spray.json.AdditionalFormats
object Client extends SprayJsonSupport with AdditionalFormats {
val email = "..."
val password = "..."
val pipeline = sendReceive
pipeline(Post("http://something.com/login", s"""{
"email": "$email",
"password": "$password"
}""".asJson.asJsObject))
}
Sorry, but you question is a bit cumbersome, for me at least. If you want to make a POST request in Spray with some Json as HttpEntity, then you should try to do it with Spray-Clients pipelining, it's drop dead simple.
You need to create a simple pipe:
val pipe: HttpRequest => Future[HttpResponse] = sendReceive
and then build a request:
import spray.json.SprayJsonSupport._
pipe(Post("/api/1.0/users/ping", """{ "key"="whatever" }""".asJson))
This will return you a Future with HttpResponse, if you want some specific result, let's say, for example, some confirmation code, then add unmarshall step to your pipe:
val pipe: HttpRequest => Future[ConfCode] = sendReceive ~> unmarshal[ConfCode]
That is what i tried too, but it doesn't work it is telling me that I am missing an implicit:
val pipeline = sendReceive(conduit)
val responseF = pipeline(Post("/api/1.0/users/ping.json", """{ "key": "whatever" }""".asJson))
responseF onComplete { ...
but i always get :
could not find implicit value for evidence parameter of type spray.httpx.marshalling.Marshaller[spray.json.JsValue]
[error] val responseF = pipeline(Post("/api/1.0/users/ping.json", """{ "key": "whatever" }""".asJson))
also the import you have in your previous snapshot does not work.... Am i using a bad version of the library? My sbt settings are:
val sprayVersion = "1.1-M7"
val akkaVersion = "2.1.1"
"io.spray" %% "spray-json" % "1.2.5",
"com.typesafe.akka" %% "akka-actor" % akkaVersion,
"com.typesafe.akka" %% "akka-slf4j" % akkaVersion,
"io.spray" % "spray-client" % sprayVersion,

POST request using spray-client

I want to send XML over HTTP POST request to server using spray-client with some headers set etc. However, only examples that I can find are for JSON requests.
Can somebody provide a simple snippet of code for XML over HTTP POST communication using spray-client?
Thanks!
Here is a small code sample for creating a spray HttpRequest that has an xml NodeSeq based payload. Let me know if you this helps or if you need more code (like submitting the request):
import spray.httpx.RequestBuilding._
import spray.http._
import HttpMethods._
import HttpHeaders._
import MediaTypes._
object SprayXml {
def main(args: Array[String]) {
val xml = <root>foo</root>
val req = Post("/some/url", xml)
}
}
The two dependencies I was using to make this code work are spray-client and spray-httpx.
The relevant pieces from my build.sbt are:
scalaVersion := "2.10.0"
resolvers ++= Seq(
"Scala Tools Repo Releases" at "http://scala-tools.org/repo-releases",
"Typesafe Repo Releases" at "http://repo.typesafe.com/typesafe/releases/",
"spray" at "http://repo.spray.io/"
)
libraryDependencies ++= Seq(
"io.spray" % "spray-httpx" % "1.1-M7",
"io.spray" % "spray-client" % "1.1-M7",
"com.typesafe.akka" %% "akka-actor" % "2.1.0"
)
With a hacky way to be specific about the content type. Note payload can be string or xml literal.
import spray.client.pipelining._
import spray.http._
val pipeline: HttpRequest => Future[HttpResponse] = {
addHeader("My-Header-Key", "myheaderdata") ~>
((_:HttpRequest).mapEntity( _.flatMap( f => HttpEntity(
f.contentType.withMediaType(MediaTypes.`application/xml`),f.data))))
~> sendReceive
}
pipeline(
Post(
"http://www.example.com/myendpoint", <MyXmlTag>MyXmlData</MyXmlTag>
)
)