Play framework Ws: how create request without encode? - scala

In this moment, I do integration of our project on Play framework and gis service.
Gis service works with http get queries.
Here the right example of get point request:
http://myGisServer.ru/myservice?token=XYZ%3D&query=[{"id":"123","objectIds":["5"]}]
Where token is token form auth service, and this must be encoded for http request.
And query is not encoded json with specified parameters.
I create this request with ws:
def getPoint = Action.async{
val data = Json.obj(
"id" -> "123",
"objectIds" -> Json.arr("5")
)
val ws = WS.url("http://myGisServer.ru/myservice").withQueryString(
"token" -> currentToken,
"query" -> data.toString()
)
val futureResponse: Future[WSResponse] = ws.get()
futureResponse.map(response => {
Ok(response.json)
})
}
But that doesn't work because ws encode all request, including json string.
How can I exclude json from encoding, or create other query without encoding?

Related

How to send file in response with name in akka http

I am new to AKKA world,
How to send a file as a response using akka http?
I got a solution for how to send the file in RESTAPI response with Akka-HTTP, from the above post but I want to provide a name and extension to that file.
so what is a way to send the file as a response with filename and extension means I want downloaded file should have a name with .zip extension in Akka-HTTP with scala
Adding onto the other answer you can use the Akka HTTP DSL as follows:
val fileContentsSource: (String, String) => Source[ChunkStreamPart, _] =
(fileName, enc) =>
Source
.fromIterator(() => scala.io.Source.fromFile(fileName, enc).getLines)
.map(ChunkStreamPart.apply)
val fileEntityResponse: (String, String) => HttpResponse =
(fileName, enc) =>
HttpResponse(entity = Chunked(ContentTypes.`text/plain(UTF-8)`,
fileContentsSource(fileName, enc)))
val route: Route = {
path("file" / Segment) { fileName =>
complete(fileEntityResponse(fileName, "UTF-8"))
}
}
The Segment is called a PathMatcher and will extract the file name from the route so call it as follows
curl --location --request GET 'localhost:PORT/file/filename.txt'

Akka HTTP: ByteString as a file payload in a form-data request

In one of my previous questions, I asked how to represent a form-data request using Akka HTTP? According to answers I created a working sample, but faced a "scaleability" issue - when the number of form-data requests is high, I need to handle a lot of files in the file system.
I'm curious, is it possible to send ByteString as a file payload in a form-data request?
case class FBSingleChunkUpload(accessToken: String,
sessionId: String,
from: Long,
to: Long,
file: ByteString) //this property is received from S3 as array of bytes
I created a following sample:
def defaultEntity(content: String) =
HttpEntity.Default(
ContentTypes.`text/plain(UTF-8)`,
content.length, Source(ByteString(content) :: Nil)
)
def chunkEntity(chunk: ByteString) =
HttpEntity.Strict(
ContentType(MediaTypes.`application/octet-stream`),
chunk
)
val formData = Multipart.FormData(
Source(
Multipart.FormData.BodyPart("access_token", defaultEntity(upload.fbUploadSession.fbId.accessToken)) ::
Multipart.FormData.BodyPart("upload_phase", defaultEntity("transfer")) ::
Multipart.FormData.BodyPart("start_offset", defaultEntity(upload.fbUploadSession.from.toString)) ::
Multipart.FormData.BodyPart("upload_session_id", defaultEntity(upload.fbUploadSession.uploadSessionId)) ::
Multipart.FormData.BodyPart("video_file_chunk", chunkEntity(upload.chunk)) :: Nil
)
)
val req = HttpRequest(
HttpMethods.POST,
s"/v2.3/${upload.fbUploadSession.fbId.pageId}/videos",
Nil,
formData.toEntity()
)
In this case Facebook sends me back a message:
Your video upload timed out before it could be completed. This is
probably because of a slow network connection or because the video
you're trying to upload is too large
But if I send the same ByteString as a File it works fine.
What could be the reason of this? I already tried to use MediaTypes.multipart/form-data in chunkEntity but it behaves in the same way.
In order to send a ByteString as a form-data file you need to use a following BodyPart:
def fileEntity(chunk: ByteString) = Multipart.FormData.BodyPart.Strict("video_file_chunk",
HttpEntity(ContentType(MediaTypes.`application/octet-stream`), chunk), Map("fileName" -> "video_chunk"))
Pay extra attention to Map("fileName" -> "video_chunk") this parameters is mandatory in order to construct a form-data HTTP request correctly.
So instead of chunkEntity from the question, use fileEntity from this answer :)

Scala testing Web Service Client for POST request

So the Play documentation you can find here gives a very simple example for a GET call without anything like auth header or parameters.
Could somebody help me figure out how to use a similar strategy for something more complex like a POST request with JSON data as body and auth header required? I can't get it to work right now.
I want to know how to test a client using ws to do it's external http requests.
Thanks
Here is a snippet of code from one of my projects which sends sms via twilio api:
class SmsServiceImpl #Inject()(config: Configuration, ws: WSClient) extends SmsService {
val twilloAccountSid = config.getString("twillo.accountSid").get
val twilloAuthToken = config.getString("twillo.authToken").get
val smsApiUrl = config.getString("twillo.apiBaseUrl").get + "/" +twilloAccountSid+"/Messages.json"
override def send(phone: String, message: String): Future[Unit] = {
val data = Map(
"To" -> Seq(phone),
"From" -> Seq(config.getString("twillo.fromPhone").get),
"Body" -> Seq(message)
)
val response: Future[WSResponse] = ws.url(smsApiUrl).withAuth(twilloAccountSid, twilloAuthToken, WSAuthScheme.BASIC).post(data)
response.map { response =>
response.status match {
case 201 => Unit
case _ => throw new SmsSendingException(s"Failed to send sms. Got https status: ${response.status}")
}
}
}
It is POST request with authentication.

Playframework Scala Transform Request from Json to x-www-form-urlencoded

Hi I am new to Scala and Playframework. I am getting an ajax request that is json format and I need to make another request to another server with x-www-form-urlencoded format.
I have this code in the controller
def getToken = Action.async(parse.json) { request =>
WS.url("https://api.xxxx.com/v1/yyyy")
.withHeaders(
"accept" -> "application/json",
"content-type" -> "application/x-www-form-urlencoded",
"Authorization" -> "Auth %s".format(apiKey)
).post(request.body) map { response =>
Logger.info("response from get user: " + Json.prettyPrint(response.json))
Ok("ok")
}
}
I tried different ways but I can't get this working. Maybe I should do a formatter in the model. Which would be the best way to cast the request json to a request x-www-form-urlencoded?
thank you
Short Answer
You just need to pass a Map[String, Seq[String]] to the post method.
If you have some keys and values you can easily construct such a Map.
For instance:
WS.url(url).post(Map("key1" -> Seq("value1"), "key2" -> Seq("value2")))
Details
As mentioned in Form Submission section of Play WS docs, if you want to submit a form you have to pass a Map[String, Seq[String]] to the post method.
What you are passing here, instead, is a JsValue (because the request.body is of type JsValue because of the body parser type which is parser.json). So first you have to extract the set of key/value pairs from the json object and then construct a Map[String, Seq[String]] from it and pass it to post method.
For example if you are sure that the json object (extracted from the request body by body parser) is a JSON Object (not an Array or String or a Numeric Value) you can construct the needed Map easily (just for the first level key/value pairs):
def getToken = Action.async(parse.json) { request =>
import play.api.libs.json.JsObject
val json = request.body.as[JsObject]
val formParamsMap = json.value.mapValues(_.asOpt[String].toSeq)
WS.url("https://api.xxxx.com/v1/yyyy")
.withHeaders(
"accept" -> "application/json",
"content-type" -> "application/x-www-form-urlencoded",
"Authorization" -> "Auth %s".format(apiKey)
)
.post(formParamsMap) map { response =>
Ok("ok")
}
}

HTTP Post with scala and Dispatch?

I want to write a function for uploading photos to flickr as http://www.flickr.com/services/api/upload.api.html. I wrote the following code:
val http = new Http with thread.Safety
val uploadEndPoint = :/("api.flickr.com") / "services" / "upload"
then I sign the method using dispatch
def signUploadRequest(userParams: Map[String, String], accessToken: Token, verifier: String): Map[String, String] = {
var map = userParams
map += "api_key" -> consumerKey
sign("", uploadEndPoint.toString, userParams, consumer, Some(accessToken), Some(verifier), Some(OAuth.oob))
}
Then I call the following method:
def sendUploadRequest(reqParms: Map[String, String]) = {
http(uploadEndPoint.POST <:< reqParms as_str)
}
but I got the following error:
<rsp stat="fail">
<err code="100" msg="Invalid API Key (Key has invalid format)" />
</rsp>
I use the same procedure for requests and it works fine. What is the problem with the Post?
Thanks,
Feras
I don't know this flickr api, but shouldn't the map pass as the request body ?
Another remark is that, they say that the photo can't be part of the signature (just in case the userParams contains its).
So, if you should use post's body instead of putting the map in the headers (which does <:<):
def sendUploadRequest(reqParms: Map[String, String]) = { http(uploadEndPoint << reqParms as_str) }
The << convert the request to post, using the given map as the payload. Note that using POST will set the Map body as empty.