Have a List in Play framework web service parameters - scala

I have written this web service in play framework.
controller
def getByGenre(genre: String) = Action {
val result = Await.result(Movies.getByGenre(genre), 5 seconds)
Ok(toJson(result))
}
routes
GET /movies/genre/:genre controllers.MoviesController.getByGenre(genre: String)
However a user may select multiple Genre. Therefore I need to convert the genre parameter to a List[String]
I also need to know how to pass that Array parameter to the web service using CURL.

If you can pass the genres parameter as part of the query string, just repeat the parameter with different values and then retrieve it like this:
def getByGenre() = Action.async { implicit request =>
val genres = request.queryString.get("genres")
Movies.getByGenre(genres).map { movies =>
Ok(toJson(movies))
}
}
Your route will be:
GET /movies/genre controllers.MoviesController.getByGenre()
Also, notice that you will need to change the Movies.getByGenre signature to:
def getByGenre(genres: Option[Seq[String]]): Seq[Movies]
An final url will be something like #mfirry showed:
myhost.com/movies/genre?genre=action&genre=drama
Finally, as you may have noticed, I've removed the blocking code from you action. Using Await at your controller means that you action would be blocking for at least 5 seconds at the worst case scenario. I suggest you to take a look at the following page of Play docs:
https://www.playframework.com/documentation/2.5.x/ScalaAsync

Related

How to pass a Future data to the view?

Iam trying to get data from a web weather API, I'am getting the data by using WSClient.
Actually, I can println and visualize the data like this :
val futureResponse: Future[WSResponse] = complexRequest.get()
def weather = Action {
futureResponse.map {
response =>
println(response.json)
}
println(futureResponse)
Ok(views.html.weather("data"))
}
but I have trouble passing it to the view layer using Ok(views.html.weather("data")) cause when i println(futureResponse) its not json data it shows :
Future(Success(AhcWSResponse(StandaloneAhcWSResponse(200, OK))))
only println(response.json) shows the valid data i want to send but its unreachable outside.
You need something on the lines of
def weather = Action.async {
complexRequest.get().map(response => Ok(views.html.weather(response.json)))
}
So basically, the json is only available when the future is completed, so you can only pass it to the view inside the map function, also notice that I've used Action.async this creates an action that expects a Future[WsResponse] rather than just a WsResponse
Also bear in mind that the Futures are memoised, so if you store the reference to it in a val it will only execute once
EDIT: Fixed the future being stored in a val to avoid problems with the memoisation
It's unreachable because you will have to use a callback method to access/pass the content inside Future. That's the reason println(response.json) inside map callback shows the data/content you are interested.
You may refer to Accessing value returned by scala futures

Create two or more APIs with same URL in play framework

I have use case where I need to read value from query string.
Currently I have two different APIs(Some other person has created the code) which maps to same URL
GET /service/class/:className/details controllers.Student.getStudentDetails(studentId)
GET /service/class/:className/details controllers.Student.getAllStudentsDetails()
If query string is present in URL then API1 should execute, otherwise API2.
As URL is same for both APIs, I am able to hit only get-student-details API(Because it has higher priority in routes file).
I am looking for alternatives to fix this problem.
As per my knowledge we don't need to create different APIs just to handle query strings.
I am thinking to merge 2 different APIs in single APIs which takes action depending upon presence of query string in request.
What I want to know is if there is way to execute two different APIs which maps to same URL(Only difference is with query string).
NOTE: I am using play 2.4.6.
I see few ways using a single controller function (say we chose getStudentDetails)
1) Having an Option parameter:
def getStudentDetails(studentId: Option[String]) = Action { studentId match {
case Some(id) => // do something
case None => // do something else
}
// ..
}
2) Look for your query string parameters inside your http request:
def getStudentDetails = Action { request =>
request.queryString.get("studentId") match {
case Some(list) => // do something...beware this is a List
case None => // do something else
}
//...
}

Play Framework Search Bar

I'm trying to make a search bar with only one variable - the search input. I'm sure there's a fairly simple way to do this, but everything that I've found about getting input from the DOM (the views file) has been about using a Form and getting multiple variables. Is there a simpler way to do this if it's just a single variable?
I have a function in my Applications
def singleElement = Action { implicit request =>
val databaseSupport = new InteractWithDatabase(comm, db)
val put = Future {
while (true) {
val data = databaseSupport.getFromDatabase()
if (data.nonEmpty) {
comm.communicator ! data.head
}
}
}
Ok(views.html.singleElement)
}
I want to take some input from the user on the page singleElement and pass it into getFromDatabase which calls a MySQL query. How do I do this?
You can use restful and do something like this
routs file
GET /content/search/:search controllers.ContentController.search(search:String)
and in controller:
public Result search(String saerch) {}

How to filter requests for access code in Play 2.1

I'd like to add a (per method / global) filter to requests, that simply rejects (404/403 page) any request that doesn't have a specific URL parameter.
I know Play has one-two mechanism to do this (e.g. register on Global.onRouteRequest()), so don't just send me a link to the documentation unless it contains a code sample that covers this question. I tried playing with the API but got a bit stuck.
Is this what you mean?
object Global extends WithFilters(AccessCheck)
object AccessCheck extends Filter with Results {
override def apply(next:RequestHeader => Result)(request:RequestHeader):Result =
request
.getQueryString("myCheck")
.map( myCheck => next(request))
.getOrElse(Forbidden)
}
http://www.playframework.com/documentation/2.1.0/ScalaInterceptors
If you are just trying to make some reusable code to filter requests on specific actions you my want to try creating an EssentialAction like this. This is known as action composition. This is what it would look like in the case you described. There is more information in the docs: http://www.playframework.com/documentation/2.1.1/ScalaActionsComposition
Note that you can do this in Play 2.0.X as well but EssentialAction doesn't exist, instead you use an Action, and there is just a little more syntax involved.
def RequireMyCheck(action: => EssentialAction): EssentialAction = {
EssentialAction { request =>
request
.getQueryString("myCheck")
.map( myCheck => action()(request))
.getOrElse(Forbidden)
}
}
You can use it like this:
def index = RequireMyCheck {
Action { request =>
Ok("Hello")
}
}

Really confused about snippets

I have a problem with my lift view. The thing is, I am making an expensive remote rest-api call twice - where I should really need to do it only once.
But I can't figure out how to solve this.
Basically I have an HTML template like this, that needs to display the list of users and their count:
//UserSearchResults.html
Num users: <span class="lift:UserSearchResults.userCount"></span>
User list:
<ul>
<lift:UserSearchResults.userList>
<li><user:userName/></li>
</lift:UserSearchResults.userList>
</ul>
And then I have an actual snippet that goes and retrieves the list of users from the rest-api server. However, note that it actually does this TWICE - once to count the number of users, and once to render the list.
//UserSearchResults.scala
/** Get list of users from api */
def users: List[User] = {
val url = "http://server/rest-api/user-search";
val result = io.Source.fromURL(url).mkString
//... parse users into List[User] and return it
return entries
}
/** Render user count */
def userCount =
"* *" #> users.length //<-- ONE call
def userList(in: NodeSeq): NodeSeq = {
users.flatMap(user => Helpers.bind("user", in, //<--SECOND call
"userName" -> user.user_name))
}
Is there a better place to put the api call? Is there like a "constructor" for the snippet, that I can use cache the user list, and to share it across all the functions in the class?
Any help is appreciated.
If UserSearchResults is a class (as opposed to an object), then there will be a per-request instance of that class. As such, all you have to do is change your def users to a lazy val users and you should be good to go.
If your snippet extends StatefulSnippet, you can just save the list in an instance variable. Another option would be to put the list into a RequestVar. Then it could also be accessed from other snippets.