I'm about to set up a stupid play/scala app whose only job will be to make some http call once it receives calls itself
GET /abracadabra controllers.Application.abracadabra(stuff: String)
and then
def abracadabra(stuff: String) = Action {
Logger.info("called for stuff: "+stuff);
// call this other URL with 'stuff' as get parameter
// log http return status code and return Ok/200 anyways
}
Now for the second (commented) part I thought about using Dispatch.
I've read the docs but I can't just figure out how to use Promises and all that.
If anybody could point me to some sample code or something, it will be much appreciated
Since Play! has a built in Async library, you should probably go ahead and use that unless there's a feature in Dispatch that you specifically need.
Here's a short example:
def abracadabra(stuff: String) = Action {
Logger.info("called for stuff: "+stuff);
Async {
WS.url("http://stackoverflow.com/").get().map { response =>
Ok("I got it: " + response)
}
}
}
The documentation is here: https://github.com/playframework/Play20/wiki/ScalaWS
Related
I'm wondering if there's a way to do an internal redirect, re-route, or response forwarding inside Ktor.
call.respondRedirect("relative/url")
sends a HTTP 302 or 301 depending on the permantent: Boolean flag. I'm looking for something that would do the same without using HTTP, just internally in Ktor. Here's some pseudo-routing of what I want to achieve:
get("/foo") {
if (call.parameters["something"] != null) {
call.respondText("Yay, something!")
} else {
call.respondRedirect("/bar") // except without relying on client to handle HTTP redirects.
}
}
get("/bar") {
call.respondText("No thing :(")
}
The goal is that the client shouldn't make 2 requests, and shouldn't be aware of the redirection happening.
NB: I'm aware I can extract a function for /bar's body and invoke it, instead of responsdRedirect. However, I want to make Ktor handle it so that it goes through all the necessary lifecycle and pipeline with all the interceptors. This is to make sure it is handled as if it was an external request, except the network roundtrip.
I'm looking for something like Express.js' req.app.handle(req, res) as shown in the first half of this answer: https://stackoverflow.com/a/48790319/253468. A potential solution I couldn't understand yet is something like TestApplicationEngine.handleRequest (in io.ktor:ktor-server-test-host) is doing with pipeline.execute. I guess I could invoke call.application.execute(), the question is how to construct the ApplicationCall object then. Note this is for production use, so no TestApplicationCall.
You can do similar thing in Ktor by using call.application.execute function with cloned call object. For convenience let's define extension function for doing internal redirects:
suspend fun ApplicationCall.redirectInternally(path: String) {
val cp = object: RequestConnectionPoint by this.request.local {
override val uri: String = path
}
val req = object: ApplicationRequest by this.request {
override val local: RequestConnectionPoint = cp
}
val call = object: ApplicationCall by this {
override val request: ApplicationRequest = req
}
this.application.execute(call)
}
Here it creates a copy of an ApplicationCall object with the replaced path for a request. I use delegates to avoid boilerplate code. You can use redirectInternally function like this:
get("/foo") {
call.redirectInternally("/bar")
}
What is the correct way to alter a Request performing an asynchronous task before the Request happens?
So any request Rn need to become transparently Tn then Rn.
A little of background here: The Task is a 3rd party SDK that dispatch a Token I need to use as Header for the original request.
My idea is to decorate the Rn, but in doing this I need to convert my Tn task into a Siesta Request I can chain then.
So I wrapped the Asynchronous Task and chained to my original request.
Thus any Rn will turn into Tn.chained { .passTo(Rn) }
In that way, this new behaviour is entirely transparent for the whole application.
The problem
Doing this my code end up crashing in a Siesta internal precondition:
precondition(completedValue == nil, "notifyOfCompletion() already called")
In my custom AsyncTaskRequest I collect the callbacks for success, failure, progress etc, in order to trigger them on the main queue when the SDK deliver the Token.
I noticed that removing all the stored callback once they are executed, the crash disappear, but honestly I didn't found the reason why.
I hope there are enough informations for some hints or suggests.
Thank you in advance.
Yes, implementing Siesta’s Request interface is no picnic. Others have had exactly the same problem — and luckily Siesta version 1.4 includes a solution.
Documentation for the new feature is still thin. To use the new API, you’ll implement the new RequestDelegate protocol, and pass your implementation to Resource.prepareRequest(using:). That will return a request that you can use in a standard Siesta request chain. The result will look something like this (WARNING – untested code):
struct MyTokenHandlerThingy: RequestDelegate {
// 3rd party SDK glue goes here
}
...
service.configure(…) {
if let authToken = self.authToken {
$0.headers["X-Auth-Token"] = authToken // authToken is an instance var or something
}
$0.decorateRequests {
self.refreshTokenOnAuthFailure(request: $1)
}
}
func refreshTokenOnAuthFailure(request: Request) -> Request {
return request.chained {
guard case .failure(let error) = $0.response, // Did request fail…
error.httpStatusCode == 401 else { // …because of expired token?
return .useThisResponse // If not, use the response we got.
}
return .passTo(
self.refreshAuthToken().chained { // If so, first request a new token, then:
if case .failure = $0.response { // If token request failed…
return .useThisResponse // …report that error.
} else {
return .passTo(request.repeated()) // We have a new token! Repeat the original request.
}
}
)
}
}
func refreshAuthToken() -> Request {
return Request.prepareRequest(using: MyTokenHandlerThingy())
.onSuccess {
self.authToken = $0.jsonDict["token"] as? String // Store the new token, then…
self.invalidateConfiguration() // …make future requests use it
}
}
}
To understand how to implement RequestDelegate, you best bet for now is to look at the new API docs directly in the code.
Since this is a brand new feature not yet released, I’d greatly appreciate a report on how it works for you and any troubles you encounter.
I want to validate REST requests (GET and PUT) in LIFT before processing it. i.e. i need to check whether requests has a parameter requestor. if not need to response with exception saying missing parameter. Could you please let me know how to do this.
There are a few things you could do. The two I would try would be a helper function that would wrap your Rest call, something like:
def checkParam(r:Req):Boolean = {
r.param("paramName").isDefined
}
def requireParams[T<:LiftResponse](r:Req)(methodBody: => T):LiftResponse = {
if(checkParam(r))
methodBody
else
InMemoryResponse("Parameters not specified".getBytes(), List("content-type" -> "text/plain"), Nil, 500)
}
The function will check for the parameters and return an error if it doesn't work, or execute the call if it does. In your Rest call, you would use it like:
case "location" :: Nil Get req => requireParams(req){
//your rest body
}
Alternately, you could probably use a guard on the entire RestHelper assuming you wanted to check every method call, something like this might work:
val ensureParams: PartialFunction[Req, Unit] = {
case r if (r.get_? || r.put_?) && checkParam(r) =>
case r if (!r.get_? && !r.put_?) =>
}
and then guard your RestHelper instance in Boot with:
LiftRules.dispatch.append(ensureParams guard YourRestHelper)
I haven't tested the above code, so there may be some mistakes - but hopefully it should help get you started.
I know akka-http libraries marshal and unmarshal to class type while processing request.But now, I need to read request-parameters of GET request. I tried parameter() method and It is returning ParamDefAux type but i need those values as strings types
I check for answer at below questions.
How can I parse out get request parameters in spray-routing?
Query parameters for GET requests using Akka HTTP (formally known as Spray)
but can't do what i need.
Please tell me how can i extract query parameters from request. OR How can I extract required value from ParamDefAux
Request URL
http://host:port/path?key=authType&value=Basic345
Get method definition
val propName = parameter("key")
val propValue = parameter("value")
complete(persistanceMgr.deleteSetting(propName,propValue))
My method declarations
def deleteSetting(name:String,value:String): Future[String] = Future{
code...
}
For a request like http://host:port/path?key=authType&value=Basic345 try
path("path") {
get {
parameters('key.as[String], 'value.as[String]) { (key, value) =>
complete {
someFunction(key,value)
}
}
}
}
Even though being less explicit in the code, you can also extract all the query parameters at once from the context. You can use as follows:
// Previous part of the Akka HTTP routes ...
extract(_.request.uri.query()) { params =>
complete {
someFunction(key,value)
}
}
If you wish extract query parameters as one piece
extract(ctx => ctx.request.uri.queryString(charset = Charset.defaultCharset)) { queryParams =>
//useyourMethod()
}
So i have asked about this before and have changed a lot of code around.
Spray Routing Doesn't match anything
Now I am executing my functions that return HTTPresponses insided detach() blocks so that i dont block. These then are completed and return to the client, but I still can't seem to get my routing to work.
In my tests, a request to a single slash works fine, but anything else, such as this create user path shown below fails. I can't seem to figure out why, and spray routing uses so many constructs I'm having a hard time figuring out how the system works well enough to find out whats happening.
I tried inserting logRequest blocks around certain paths thinking that might show me whats happening, but none of them seem to get hit. Any help would be greatly appreciated.
val route: Route = {
host("fakebook.com", "www.fakebook.com") {
pathSingleSlash {
complete("pong")
} ~
pathPrefix("users") { req =>
path("newuser") {
put {
detach() {
complete(genericPut(CreateUser(req.request)))
}
}
} ~
... rest of routing
And here is what my scalatests look like, the simple Put passes, but the put with newuser doesn't
val createUserSuccessRequest = Put(Uri("http://www.fakebook.com/users/newuser") withQuery(F_User.lastNameString -> "Doe", F_User.firstNameString -> "John", F_User.bioString -> "i like foobar",
F_User.ageString -> "42", F_User.dobString -> dateFormatter.format(new Date(1970 - 1900, 5, 7))))
"The FakeBook route" when {
"When sending a single slash request" should {
"respond with a simple pong" in {
Get() ~> logRequestResponse("plain get final request and response")(sealRoute(route)) ~> check {
assert(responseAs[String] == "pong")
}
}
}
"Running route to create a user with PUT" should {
"forward a message to the backbone to create a new user" in {
createUserSuccessRequest ~> logRequest("create user final request and response"){sealRoute(route)} ~> check {
expectMsg(CreateUser(createUserSuccessRequest))
}
}
}
}
For anyone else trying to solve this issue:
a lot of these directives actually DONT extract anything, so having the lambda inputs i have like req => and req2 => will not work.
It turns out, spray routing is designed so that you never have to touch the RequestContext as I have done with my functions (which is why I try to access it). They extract only the useful data. Rather than do things as I should and change my function signatures, i am going to (for now) do a hotfix that has worked.
if you absolutely must have the requestcontext, so long as you don't break it somehow, you can extract it by making your own extraction directive like so
val extractRequestContext = extract(x => x) and wrap your code in that
I did this
path("somepath") {
detach() {
extractRequestContext { request => complete(someFunc(request)) }
}
}
In the future I should learn to use the DSL more correctly and extract what I need from the request context using directives and pass THOSE to the functions