There is some documentation about sending post request in dispatch http://dispatch.databinder.net/Combined+Pages.html but yet it's not clear. What are myRequest and myPost there?
I want to send a https post request + add some cookies manually via headers + add some customs headers like form data, etc, and then read the response by reading the headers and cookies.
I only know how to prepare url for sending post request:
val url = host(myUrl + "?check=1&val1=123").secure
What do I do next?
Dispatch is built on top of Async Http Client. Therefore, myRequest in the example:
val myRequest = url("http://example.com/some/path")
is com.ning.http.client.RequestBuilder.
Calling the POST method on RequestBuilder turns the request into a POST request. That's what's happening in the myPost example:
def myPost = myRequest.POST
I often find the Dispatch documentation difficult to follow. For a quick overview of the all the various Dispatch operators, see: Periodic Table of Dispatch Operators
If you're asking how to build a POST request and add custom form parameters, you probably want to use the <<(values) operator like this:
val params = Map("param1" -> "val1", "param2" -> "val2")
val req = url("http://www.example.com/some/path" <<(params)
Likewise if you want to add some custom headers you can use the <:<(map) operator like this:
val headers = Map("x-custom1" -> "val1", "x-custom2" -> "val2")
val req = url("http://www.example.com/some/path" <<(params) <:<(headers)
Update: Actually, there is no POST method on RequestBuilder. The call to POST is part of Dispatch and calls setMethod on the underlying RequestBuilder. See dispatch.MethodVerbs for more details.
Related
I am trying to do cross-domain api calling from one server to another. For this OPTIONS request is completing successfully but actual requests(GET, POST, PUT, DELETE) are failing with CORS Missing Allow Origin
Following is the code I'm adding
private val corsResponseHeaders = List(
`Access-Control-Allow-Origin`(AllOrigins),
`Access-Control-Allow-Credentials`(true),
`Access-Control-Allow-Headers`("Authorization", "Content-Type", "Csrf-Token"),
`Access-Control-Max-Age`(1728000)//Tell browser to cache OPTIONS requests
)
//this directive adds access control headers to normal responses
private def addAccessControlHeaders: Directive0 = {
respondWithHeaders(corsResponseHeaders)
}
//this handles preflight OPTIONS requests.
private def preflightRequestHandler: Route = options {
println("allowed-method==================================")
complete(HttpResponse(StatusCodes.OK).
withHeaders(`Access-Control-Allow-Methods`(OPTIONS, POST, PUT, GET, DELETE)))
}
// Wrap the Route with this method to enable adding of CORS headers
def corsHandler(r: Route): Route = addAccessControlHeaders {
println("cors-handler==================================")
preflightRequestHandler ~ r
}
// Helper method to add CORS headers to HttpResponse
// preventing duplication of CORS headers across code
def addCORSHeaders(response: HttpResponse):HttpResponse =
response.withHeaders(corsResponseHeaders)
I'm using it this corsHandler directive as :
lazy val apiRoutes: Route = corsHandler({
pathPrefix("api") {
jsonRoutes ~
reject(UnmatchedPathRejection())
}
})
Note: jsonRoutes contains all routes of GET, POST, PUT, DELETE(so request becomes api/something)
And getting errors as
So how to make these requests work?
I got the same error when my client code sent a request for GET http://localhost:8080//menu, so with a double / after the port number. Removing the extra / from the client call fixed the problem.
I'd like to test that a Http4s Client is being called from my class ClassUnderTest (to make a HTTP request) and that the request made contains the headers I expect.
In 0.18.x, I did something like below. Using a side effect to store the value of the headers so that I can make an assertion after the call. In the example below, f.execute(...) is expected to make a PUT with the client instance and I'm trying to record all request handling and storing the headers.
"Request has headers" >> {
var headers = List[String]()
val client = Client[IO](new Kleisli[IO, Request[IO], DisposableResponse[IO]](request => {
headers = request.headers.map(_.name.toString()).toList
Ok().map(DisposableResponse(_, IO.pure(())))
}), IO.pure(()))
val f = ClassUnderTest(client)
f.execute("example")
headers must_== List(
"Content-Type",
"X-Forwarded-For",
"Content-Length"
)
}
The real code is here if you're interested.
ClassUnderTest took a Client[IO] so I could get the above working.
class ClassUnderTest(client: http4s.Client[IO])
In Http4s 0.20.12, I had to change the signature to:
class ClassUnderTest(client: Resource[IO, http4s.Client[IO]])
...and now I can't figure out how to stub out the client for tests. I experimented with JavaNetClientBuilder but that doesn't help because I can get an instance of Client (after .create) and now I need a Resource[IO, http4s.Client[IO]].
How can I use a test double to stand in for the Client / Resource[F, Client[F]] so that I can test the requests it makes?
The testing page on the docs doesn't really help me. I want a test double, not to test all the functionality of the Service (I don't want to startup a server).
This is the scenario I'm trying to achieve in Play 2.5.x for Scala (all the requests and responses are Json):
Browser sends HTTP Request to URL1.
URL1 enriches the Json it receives with some data, and forwards the entire request to URL2.
URL2 responds to browser.
In the last point, I'm not sure if URL2 can send it back to the browser or has to do it through URL1 (I believe it's the latter).
This is the request in URL1 (URL2 is a simple request/response):
val request: WSRequest = ws.url("/url2")
val request2: WSRequest = request.withHeaders("Accept" -> "application/json")
val data = Json.obj(
"aaa" -> some_data1,
"bbb" -> some_data2
)
val futureResponse: Future[JsValue] = request2.post(data).map {
response => response.json
}
When I send the future I get this exception:
Execution exception[[NullPointerException: scheme]]
How to fix this?
The clue is in the function name - it's ws.url, not ws.uri. You need to specify a complete path. You can use ws.url("http://localhost:9000/url2"), with customised elements if necessary such as the scheme and the port based on your configuration.
I have looked at the dispatch tutorial, and can easily find how to get the header information (if the status is 200, if I have understood other posts) for instance with;
def main(args: Array[String]){
val svc = url("http://www.google.com")
val country = Http(svc OK as.String)
for (c <- country){
println(c)
}
}
However, I can not find how to get the response content. I would be thankful if someone could help me with that. I assume it should be a function applied on svc.
The documentation explains it:
The above defines and initiates a request to the given host where 2xx
responses are handled as a string. Since Dispatch is fully
asynchronous, country represents a future of the string rather than
the string itself.
(emphasis mine) where country refers to the request from your example and your example actually returns the body.
Note that your code example explicitely casts into String, but you can get the raw response object like this:
val svc = url("http://www.google.com")
val request = Http(svc)
val response = request()
print(s"Status\n ${response.getStatusCode}\nHeaders:\n ${response.getHeaders}\nBody:\n ${response.getResponseBody}")
This gets you the HTTP status code, all response headers and the entire response body.
See the entire reference for the Response here
I'm trying to figure out how to create a basic HTTP POST request with the Akka HTTP library. This is what I came up with:
val formData = Await.result(Marshal(FormData(combinedParams)).to[RequestEntity], Duration.Inf)
val r = HttpRequest(POST, url, headers, formData)
The thing is that it seems a bit non-idiomatic to me. Are there other ways to create a HttpEntity from FormData? Especially the fact that I have to use Await or return a Future even though the data is readily available seems overly complex for such a simple task.
You can use Marshal in a for comprehension with other Futures, such as the ones you need to send the request and unmarshall the response:
val content = for {
request <- Marshal(formData).to[RequestEntity]
response <- Http().singleRequest(HttpRequest(method = HttpMethods.POST, uri = s"http://example.com/test", entity = request))
entity <- Unmarshal(response.entity).to[String]
} yield entity
Apparently a toEntity method was added to the FormData class at some point. So this now seems like the simplest solution to the problem:
val formData = FormData(combinedParams).toEntity
val r = HttpRequest(POST, url, headers, formData)
You can also use RequestBuilding:
Http().singleRequest(RequestBuilding.Post(url, formData)).flatMap(Unmarshal(_).to[String])