问题
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)
.
回答1:
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.
回答2:
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.
来源:https://stackoverflow.com/questions/13651153/return-exact-response-header