I am trying to set response headers during post request. While everything compiles properly, headers are not set.
Here is my code:
post("/get_value"){
val jsonString = request.body;
response.setHeader("Access-Control-Allow-Origin", "*")
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE")
response.setHeader("Access-Control-Max-Age", "3600")
response.setHeader("Access-Control-Allow-Headers", "x-requested-with, content-type")
jsonString
}
What is the valid way to set headers of this kind?
Thanks!
I'm not familiar with Scalatra but you can notice that ActionResult is a case class;
case class ActionResult(status: ResponseStatus, body: Any, headers: Map[String, String])
3rd parameter of this case class is Map[String,String] which should be response header.
There is also;
object Ok {
def apply(body: Any = Unit, headers: Map[String, String] = Map.empty, reason: String = "") = ActionResult(responseStatus(200, reason), body, headers)
}
Returns http response with status code 200, you can create it like;
Ok("response",Map('HeaderKey' -> 'HeaderValue'))
As a conclusion final solution can be like;
post("/get_value") {
val jsonString = request.body;
val headers = Map("Access-Control-Allow-Origin" -> "*",
"Access-Control-Allow-Methods" -> "POST, GET, OPTIONS, DELETE",
"Access-Control-Max-Age" -> "3600",
"Access-Control-Allow-Headers" -> "x-requested-with, content-type")
Ok(jsonString,headers)
}
Related
Whenever I hit unknown route in my http4s application it returns 404 error page with Content-Type: text/plain and body:
Not found
How can I force it to always return body as JSON with Content-Type: application/json?
{"message": "Not found"}
I figured out that when I assembly httpApp I can map over it and "adjust" responses:
val httpApp = Router.publicRoutes[F].orNotFound.map(ErrorTranslator.handle)
where ErrorTranslator just detects responses with status code of client error and Content-Type which is not application/json and then just wraps body into JSON:
object ErrorTranslator {
val ContentType = "Content-Type"
val ApplicationJson = "application/json"
private def translate[F[_]: ConcurrentEffect: Sync](r: Response[F]): Response[F] =
r.headers.get(CaseInsensitiveString(ContentType)).map(_.value) match {
case Some(ApplicationJson) => r
case _ => r.withEntity(r.bodyAsText.map(ErrorView(_))) //wrap reponse body into enity
}
def handle[F[_]: ConcurrentEffect: Sync]: PartialFunction[Response[F], Response[F]] = {
case Status.ClientError(r) => translate(r)
case r => r
}
}
It works, but I wonder if there is maybe some less convoluted solution?
It would be also great if a solution could "translate" other errors, like 400 Bad request into JSON, similarily to presented code.
You can also make it with value and mapF function:
val jsonNotFound: Response[F] =
Response(
Status.NotFound,
body = Stream("""{"error": "Not found"}""").through(utf8Encode),
headers = Headers(`Content-Type`(MediaType.application.json) :: Nil)
)
val routes: HttpRoutes[F] = routes.someRoutes().mapF(_.getOrElse(jsonNotFound))
I suppose you have defined your routes in a similar fashion, then you can add a default case statement
HttpRoutes.of[IO] {
case GET -> Root / "api" =>
Ok()
case _ -> Root =>
// Your default route could be done like this
Ok(io.circe.parser.parse("""{"message": "Not Found"}"""))
}
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")
}
}
In routes:
POST /login controllers.ApplicationCtrl.login()
In Controller:
def login = Action(parse.json) { implicit request => {
val email = (request.body \ "email").as[String]
val password = (request.body \ "password").as[String]
Ok(Json.toJson(
Map("status" -> "OK",
"message" -> "%s created".format(email))
))
}
In tests
"login" in new WithApplication{
val request = route( FakeRequest(
Helpers.POST,
controllers.routes.ApplicationCtrl.login.url,
FakeHeaders(Seq(CONTENT_TYPE -> Seq("application/json"))),
""" {"email" : "bob#mail.com", "password" : "secret"} """
)).get
status(request) must equalTo(OK)
}
When I test using command line:
curl --header "Content-type: application/json" --request POST --data '{"email" : "bob#mail.com", "password" : "secret"}' http://localhost:9000/login
It gets desirable response.
{"status":"OK","message":"bob#mail.com created"}
But the test returns 400 error.
What's wrong?
(command line test wins by simplicity and understandability)
What's happening here is that Play sets the content type of the request according to the type of the body. You're using a string body so that the content type header you're setting is later overridden by text/plain; charset=utf-8.
Because you're explicitly parsing the body as Json the body parser will return a bad request 403 if the content type is not either text/json or application/json.
The best thing to do in your case is to use a Json body, i.e:
"login" in new WithApplication {
val request = route( FakeRequest(
POST,
controllers.portal.routes.Portal.test.url,
FakeHeaders(Seq.empty),
play.api.libs.json.Json.obj("email" -> "bob#mail.com", "password" -> "secret")
)).get
status(request) must equalTo(OK)
}
Note that you can make that a bit more succinct by letting an alternate FakeRequest constructor infer the method and URL of your action from the call:
val request = route(FakeRequest(controllers.portal.routes.Portal.test)
.withBody(Json.obj("email" -> "bob#mail.com", "password" -> "secret"))).get
Data types you can use as the body parameter and their content type mapping:
JsValue -> application/json
NodeSeq -> text/xml
String -> text/plain
Map[String, Seq[String]] -> application/x-www-form-urlencoded
Array[Byte] -> nothing
There's also the option of using the tolerantJson as a body parser to skip checking the content type completely.
how to remove headers from Response (SimpleResult)
the example of code:
def NoCache[A](action: Action[A]): Action[A] = Action(action.parser) { request =>
action(request) match {
case s: SimpleResult[_] =>
s.withHeaders(PRAGMA -> "no-cache")
// remove all headers with name "ETAG" HERE ??
case result => result
}
}
i did not find this functionality in documentation.
thanks.
Since SimpleResult and ResponseHeader are both case classes, you can copy them to modify the headers:
...
val headers = s.header.headers - ETAG + (PRAGMA -> "no-cache")
s.copy(header = s.header.copy(headers = headers))
...
From the client-side of a webapp, I hit a server-side route which is just a wrapper for a third-party API. Using dispatch, I am trying to make that server-side request return the exact header and response of the third-party API to the client-side AJAX call.
When I do this:
val req = host("third-pary.api.com, 80)
val post = req.as("user", "pass") / "route" << Map("key" -> "akey", "val" -> "aval")
Http(post > as.String)
I always see a 200 response returned to the AJAX call (kind of expectedly). I have seen an Either syntax used, but I'm really more of an Any, as it's just the exact response and header. How would this be written?
I should mention I'm using Scalatra on the server-side, so the local route is:
post("/route") {
}
EDIT:
Here is the suggested Either matching example, which I'm playing with, but the match syntax doesn't make sense - I don't care if there is an error, I just want to return it. Also, I can't seem to get the BODY returned with this method.
val asHeaders = as.Response { response =>
println("BODY: " + response.getResponseBody())
scala.collection.JavaConverters.mapAsScalaMapConverter(
response.getHeaders).asScala.toMap.mapValues(_.asScala.toList)
}
val response: Either[Throwable, Map[String, List[String]]] =
Http(post > asHeaders).either()
response match {
case Left(wrong) =>
println("Left: " + wrong.getMessage())
// return Action with header + body
case Right(good) =>
println("Right: " + good)
// return Action with header + body
}
Ideally, the solutions returns the Scalatra ActionResult(responseStatus(status, reason), body, headers).
It's actually very easy to get response headers while using Dispatch. For example with 0.9.4:
import dispatch._
import scala.collection.JavaConverters._
val headers: java.util.Map[String, java.util.List[String]] = Http(
url("http://www.google.com")
)().getHeaders
And now, for example:
scala> headers.asScala.mapValues(_.asScala).foreach {
| case (k, v) => println(k + ": " + v)
| }
X-Frame-Options: Buffer(SAMEORIGIN)
Transfer-Encoding: Buffer(chunked)
Date: Buffer(Fri, 30 Nov 2012 20:42:45 GMT)
...
If you do this often it's better to encapsulate it, like this, for example:
val asHeaders = as.Response { response =>
scala.collection.JavaConverters.mapAsScalaMapConverter(
response.getHeaders
).asScala.toMap.mapValues(_.asScala.toList)
}
Now you can write the following:
val response: Either[Throwable, Map[String, List[String]]] =
Http(url("http://www.google.com") OK asHeaders).either()
And you've got error checking, nice immutable collections, etc.
We needed the response body of failed requests to an API, so we came up with this solution:
Define your own ApiHttpError class with code and body (for the body text):
case class ApiHttpError(code: Int, body: String)
extends Exception("Unexpected response status: %d".format(code))
Define OkWithBodyHandler similar to what is used in the source of displatch:
class OkWithBodyHandler[T](f: Response => T) extends AsyncCompletionHandler[T] {
def onCompleted(response: Response) = {
if (response.getStatusCode / 100 == 2) {
f(response)
} else {
throw ApiHttpError(response.getStatusCode, response.getResponseBody)
}
}
}
Now, near your call to the code that might throw and exception (calling API), add implicit override to the ToupleBuilder (again similar to the source code) and call OkWithBody on request:
class MyApiService {
implicit class MyRequestHandlerTupleBuilder(req: Req) {
def OKWithBody[T](f: Response => T) =
(req.toRequest, new OkWithBodyHandler(f))
}
def callApi(request: Req) = {
Http(request OKWithBody as.String).either
}
}
From now on, fetching either will give you the [Throwable, String] (using as.String), and the Throwable is our ApiHttpError with code and body.
Hope it helped.