Play Framework - Store Information About Current Request - scala

In my play framework 2 application I'd like to have a log message with the request, response, and some details about the response - such as the number of search results returned from an external web call.
What I have now is a filter like this:
object AccessLog extends Filter {
import play.api.mvc._
import play.api.libs.concurrent.Execution.Implicits._
def apply(next: RequestHeader => Future[SimpleResult])(request: RequestHeader): Future[SimpleResult] = {
val result = next(request)
result map { r =>
play.Logger.info(s"Request: ${request.uri} - Response: ${r.header.status}")
}
result
}
}
At the point of logging, I've alread converted my classes into json, so it seems wasteful to parse the json back into objects so I can log information about it.
Is it possible to compute the number of search results earlier in the request pipeline, maybe into a dictionary, and pull them out when I log the message here?
I was looking at flash, but don't want the values to be sent out in a cookie at any cost. Maybe I can clear the flash instead. Buf if there's a more suitable way I'd like to see that.
This is part of a read-only API that does not involve user accounts or sessions.

You could try using the play.api.cache.Cache object if you can come up with a reproducible unique request identifier. Once you have logged your request, you can remove it from the Cache.

Related

validating response outside of context manager?

[EDITED: I realized after reading response that I oversimplified my question.]
I am new to Locust and not sure how to solve this problem.
I have function (call it "get_doc") that is passed a locust.HttpSession() and uses it to issue an HTTP request. It gets the response and parses it, returning it up several layers of call. One of these higher-level calls looks at the returned, parsed document to decide if the response was what was expected or not. If not, I want Locust to mark the request/response as failed. A code sketch would be:
class MyUser (HttpUser):
#task
def mytask(self):
behavior1 (self.client)
def bahavior1(session):
doc = get_doc(session, url1)
if not doc_ok (doc):
??? how to register a failure with Locust here...
doc2 = get_doc(session, url2)
...
def get_doc(http_session, url):
page = http_session.get(url)
doc = parse (page)
return doc
There may be several behavior[n] functions and several Locust users calling them.
A constraint is that I would like to keep Locust-specific stuff out of bahavior1() so that I can call it with an ordinary Requests session. I have tried to do something like this in get_doc() (the catch_response parameter and success/fail stuff is actually conditionalized on 'session' being an HttpSession object):
def get_doc (session, meth, url):
resp = session.request (meth, url, catch_response=True)
doc = parse (resp.content)
doc.logfns = resp.success, resp.failure
return doc
and then in behavior1() or some higher up-chain caller I can
doc.logfns[1]("Document not as expected")
or
doc.logfns[0] # Looks good!
Unfortunately this is not working; the calls to them produce no errors but Locust doesn't seem to record any successes or failures either. I am not sure if it should work or I bungled something in my code. Is this feasible? Is there a better way?
You can make get_doc a context manager, call .get with catch_response=True and yield instead of return inside it. Similar to how it is done here: https://github.com/SvenskaSpel/locust-plugins/blob/2cbbdda9ae37b6cbb0a11cf69aca80b164198aec/locust_plugins/users/rest.py#L22
And then use it like this
def mytask(self):
with get_doc(self.client, url) as doc:
if not doc_ok(doc):
doc.failure(”doc was not ok :(”)
If you want, you can add the parsed doc as a field on the response before yielding in your doc function, or call doc.failure() inside doc_ok.

how to log the response data in grails

Grails 2.5. I need to log the request and the response to log file.
This is to log REST/JSON api calls. I need to log both the request JSON (which is easy with a filter), and the response JSON (Which is looking impossible)
an api call looks like this:
class myController {
def myMethod() {
def json = request.JSON
:
render(status:200, contentType:'application/json' {
['something': "something",'sometingElse' : 42]
}
Tried creating a filter, e.g:
class LoggingFilters {
def filters = {
after = { Map model ->
log.debug "${response.???"
}
The question is, how do you get a handle on the output? Tried respose.outputStream.toString() etc, but it says something has already got the output stream.
I saw one suggestion which said don't use render, return a model with a map with a single item: data, and render this outside against some dummy jsp. Then you can get the model in the filter. The problem with this is that we have many hundreds of render statements, and we dont really want to retest everything (we have no automated testing).
Another other work around is to use tomcat request dumper filter, but this would not meet our requirements (e.g. we need to scrub certain output, such as passwords, want to be able to log to db, only want to log very specific things etc).
We tried this plugin: https://grails.org/plugin/httplogger , but it doesnt work with grails 2.5.

How can I redirect to an error page in my Play app?

Or more precisely...
I already have an error page route defined like so:
GET /error controllers.pages.ErrorController.page(msg: String, returnTo: String)
And a controller method like this:
object ErrorController extends Controller {
def page(msg: String, returnTo: String) = ReceiverRestricted { implicit req =>
val action = List(Button(F8, "Continue", Call("GET", returnTo)))
Results.Ok(views.html.base(Html("Oops"), List(Html(msg)), None, action))
}
}
If I programmatically call, say, ErrorController.page("You did something daft!", "/home") I get to a page that looks like I want, ie:
Oops
You did something daft!
F8 Continue
However the url is ugly:
http://localhost:9000/error?msg=You%20did%20something%20daft!&returnTo=/home
I want to change this so the msg= query parameter doesn't appear in the url. How can I accomplish this? I tried removing the query parameter and redirecting to the error page with the message passed in via the flash cookie - that worked but reloading the browser page loses the message. I can't use the session cookie because I already store other data in the session almost upto its limit.
You can use flash feature.
Here is a sample:
In your controller you can redirect the user to error page with:
Redirect("/error").flashing(
"reason" -> "The item has been created"
)
And in Error action:
def error = Action { implicit request =>
Ok {
val reason = flash.get("reason").getOrElse("General Error")
//DO your stuff with reason variable
}
}
Obviously you can have as many as flash variables you want.
Since Play is restful and stateless, I can't see an easy way to pass on an error message during a redirect without using Play's flash. Of course you could store the message in an temporary cookie in the browser. Another possibility could be to store it in your database (or whatever persistence technology you use), but this seems to be like cracking a nut with a sledgehammer.

Play framework make http request from play server to "somesite.com" and send the response back to the browser

I'm developing an application using Play framework in scala. I have to handle the below use case in my application.
For a particular request from the browser to the play server the Play server should make an http request to some external server (for Eg: somesite.com) and send the response from this request back to the web browser.
I have written the below code to send the request to external serever in the controller.
val holder = WS.url("http://somesite.com")
val futureResponse = holder.get
Now how do I send back the response recieved from "somesite.com" back to the browser?
There's an example in the Play documentation for WS, under Using in a controller; I've adapted it to your scenario:
def showSomeSiteContent = Action.async {
WS.url("http://somesite.com").get().map { response =>
Ok(response.body)
}
}
The key thing to note is the idiomatic use of map() on the Future that you get back from the get call - code inside this map block will be executed once the Future has completed successfully.
The Action.async "wrapper" tells the Play framework that you'll be returning a Future[Response] and that you want it to do the necessary waiting for things to happen, as explained in the Handling Asynchronous Results documentation.
You may also be interested in dynamically returning the status and content type:
def showSomeSiteContent = Action.async {
WS.url("http://somesite.com").get().map { response =>
Status(response.status)(response.body).as(response.ahcResponse.getContentType)
}
}
Dynamic status could help if the URL/service you call fails to answer correctly.
Dynamic content type can be handy if your URL/service can return different content HTML/XML... depending on some dynamic parameter.

How to use dispatch.json in lift project

i am confused on how to combine the json library in dispatch and lift to parse my json response.
I am apparently a scala newbie.
I have written this code :
val status = {
val httpPackage = http(Status(screenName).timeline)
val json1 = httpPackage
json1
}
Now i am stuck on how to parse the twitter json response
I've tried to use the JsonParser:
val status1 = JsonParser.parse(status)
but got this error:
<console>:38: error: overloaded method value parse with alternatives:
(s: java.io.Reader)net.liftweb.json.JsonAST.JValue<and>
(s: String)net.liftweb.json.JsonAST.JValue
cannot be applied to (http.HttpPackage[List[dispatch.json.JsObject]])
val status1 = JsonParser.parse(status1)
I unsure and can't figure out what to do next in order to iterate through the data, extract it and render it to my web page.
Here's another way to use Dispatch HTTP with Lift-JSON. This example fetches JSON document from google, parses all "titles" from it and prints them.
import dispatch._
import net.liftweb.json.JsonParser
import net.liftweb.json.JsonAST._
object App extends Application {
val http = new Http
val req = :/("www.google.com") / "base" / "feeds" / "snippets" <<? Map("bq" -> "scala", "alt" -> "json")
val json = http(req >- JsonParser.parse)
val titles = for {
JField("title", title) <- json
JField("$t", JString(name)) <- title
} yield name
titles.foreach(println)
}
The error that you are getting back is letting your know that the type of status is neither a String or java.io.Reader. Instead, what you have is a List of already parsed JSON responses as Dispatch has already done all of the hard work in parsing the response into a JSON response. Dispatch has a very compact syntax which is nice when you are used to it but it can be very obtuse initially, especially when you are first approaching Scala. Often times, you'll find that you have to dive into the source code of the library when you are first learning to see what is going on. For instance, if you look into the dispatch-twitter source code, you can see that the timeline method actually performs a JSON extraction on the response:
def timeline = this ># (list ! obj)
What this method is defining is a Dispatch Handler which converts the Response object into a JsonResponse object, and then parses the response into a list of JSON Objects. That's quite a bit going on in one line. You can see the definition for the operand ># in the JsHttp.scala file in the http+json Dispatch module. Dispatch defines lots of Handlers that do a conversion behind the scenes into different types of data which you can then pass to block to work with. Check out the StdOut Walkthrough and the Common Tasks pages for some of the handlers but you'll need to dive into the various modules source code or Scaladoc to see what else is there.
All of this is a long way to get to what you want, which I believe is essentially this:
val statuses = http(Status(screenName).timeline)
statuses.map(Status.text).foreach(println _)
Only instead of doing a println, you can push it out to your web page in whatever way you want. Check out the Status object for some of the various pre-built extractors to pull information out of the status response.