I have same routes in routes file but their action is different as shown
GET /counts controllers.Application.getAllCountsByFeature(features)
GET /counts controllers.Application.getAllCounts()
I'm calling both routes as
http://localhost:9000/segments/counts?features=feature_1,feature_2-feature_3
http://localhost:9000/segments/counts
But it's not working. I want play to recognize which route is called based on query string. If query string is provided then it should hit getAllCountsByFeature method and so on.
Is there any way?
I'm using Play 2.5.9
Use one route with optional parameter
GET /counts controllers.Application.getAllCountsByFeature(features: Option[String])
and then
def getAllCountsByFeature(features: Option[String]) = Action {
features match{
case Some(f) => //..
case None => getAllCounts()
}
}
Related
I would like to implement an asynchronous session token control in an Akka HTTP-based web server before a series of sensible routes would be processed.
In my idea of implementation, I would have a method that performs the authentication that would look like the following:
def performAuthentication(sessionToken: String): Future[Result]
where Result would be a case class containing the authentication result. In order to perform the authentication, I would like to write a Directive that could be placed before the sensible routes, that would look like the following:
def authenticate: Directive1[SessionToken] = optionalHeaderValueByName("session-token").flatMap {
case Some(sessionToken) if (...) => provide(SessionToken(sessionToken))
case _ => complete(StatusCodes.Unauthorized)
}
with SessionResult a case class wrapping the sessionToken that I would provide to the subsequents routes. In place of the suspension points, I'm forced to await for the Promise result, because if I implement the onComplete ... Success ... Failure pattern, like in the following:
onComplete(this.performAuthentication(sessionToken)) {
case Success(value) if (value.isAuthenticated) => provide(SessionToken(sessionToken))
case Failure(ex) => failWith(ex)
case _ => complete(StatusCodes.Unauthorized)
}
the compiler warns me that provide and complete returns two different types... and it's right.
My question is: is there a way to write an asynchronous Directive that could also provide some value, that could be placed in the normal route definition?
I think you could create a Directive like this
type Token = String
def checkRequest(f: HttpRequest => Future[Token])(implicit ec: ExecutionContext): Directive1[Token] =
Directive { inner => ctx =>
val futureToken = f(ctx.request)
futureToken.flatMap(tkn => inner(Tuple1(tkn))(ctx))
}
You can create this directive by giving it a function that does the authentication. If you want to handle rejections as well Token could be an Either[ErrorAuth, Token]
I am reading this example from their docs:
class Email(val username: String, val domainName: String)
object Email {
def fromString(emailString: String): Option[Email] = {
emailString.split('#') match {
case Array(a, b) => Some(new Email(a, b))
case _ => None
}
}
}
println(Email.fromString("scala.center#epfl.ch"))
val scalaCenterEmail = Email.fromString("scala.center#epfl.ch")
scalaCenterEmail match {
case Some(email) => println(
s"""Registered an email
|Username: ${email.username}
|Domain name: ${email.domainName}
""")
case None => println("Error: could not parse email")
}
My questions:
What is Some and Option?
What is a factory method (just some function that creates a new object and returns it?)
What is the point of companion objects? Is it just to contain functions that are available to all instances of class? Are they like class methods in Ruby?
What is Some and Option?
Option is a data structure that represents optionality, as the name suggests. Whenever a computation may not return a value, you can return an Option. Option has two cases (represented as two subclasses): Some or None.
In the example above, the method Email.fromString can fail and not return a value. This is represented with Option. In order to know whether the computation yielded a value or not, you can use match and check whether it was a Some or a None:
Email.fromString("scala.center#epfl.ch") match {
case Some(email) => // do something if it's a Some
case None => // do something it it's a None
}
This is much better than returning null because now whoever calls the method can't possibly forget to check the return value.
For example compare this:
def willReturnNull(s: String): String = null
willReturnNull("foo").length() // NullPointerException!
with this
def willReturnNone(s: String): Option[String] = None
willReturnNone("foo").length() // doesn't compile, because 'length' is not a member of `Option`
Also, note that using match is just a way of working with Option. Further discussion would involve using map, flatMap, getOrElse or similar methods defined on Option, but I feel it would be off-topic here.
What is a factory method (just some function that creates a new object and returns it?)
This is nothing specific to Scala. A "factory method" is usually a static method that constructs the value of some type, possibly hiding the details of the type itself. In this case fromString is a factory method because it allows you create an Email without calling the Email constructor with new Email(...)
What is the point of companion objects? Is it just to contain functions that are available to all instances of class? Are they like class methods in Ruby?
As a first approximation, yes. Scala doesn't have static members of a class. Instead, you can have an object associated with that class where you define everything that is static.
E.g. in Java you would have:
public class Email {
public String username;
public String domain;
public static Optional<Email> fromString(String: s) {
// ...
}
}
Where as in Scala you would define the same class as roughly:
class Email(val username: String, val domain: String)
object Email {
def fromString(s: String): Option[Email] = {
// ...
}
}
I would like to add some examples/information to the third question.
If you use akka in companion object you can put every message that you use in case method (it should proceed and use by actor). Moreover, you can add some val for a name of actors or other constant values.
If you work with JSON you should create a format for it (sometimes custom reads and writes). This format you should put inside companion object. Methods to create instances too.
If you go deeper to Scala you can find case classes. So a possibility to create an object of this class without new is because there is a method apply in "default" companion object.
But in general, it's a place where you can put every "static" method etc.
About Option, it provides you a possibility to avoid some exception and make something when you don't have any values.
Gabriele put an example with email, so I'll add another one.
You have a method that sends email, but you take email from User class. The user can have this field empty, so if we have something like it
val maybeEmail: Option[String] = user.email you can use for example map to send an email
maybeEmail.map(email => sendEmail(email))
So if you use it, during writing methods like above you don't need to think that user specify his email or not :)
I'm trying to serve an HTML page using Spray. It's fairly easy using getFromResource and getFromResourceDirectory, but I additionally need to pass some query parameters so that some Javascript on the page knows what to do. Is that possible ? All my prior attempts consisted in this kind of things
val route = path("show-repo") { serveResourceWithParams(SHOW_REPO) } ~ getFromResourceDirectory("web")
def serveWithParams(page: String, params: (String, String)*) = {
val url = page + (if (params.isEmpty) "" else "?") + params.map { case (k, v) => s"$k=$v" }.mkString("&")
getFromResource(url)
}
but I now realize it was a bit naive
I would create a new case class with members of type Array[Byte] and List[(String, String)].
case class FileWithParams(file: Array[Byte], params: List[(String, String)])
If you know the file won't be huge, you can read it in one shot with
FileUtils.readAllBytes
Assuming you're using(which you need to run Spray) https://github.com/sirthias/parboiled/blob/master/parboiled-core/src/main/java/org/parboiled/common/FileUtils.java
Then manually read the file contents, create the case class, and marshall it as you would normally do.
Of course, my preferred way would be to split this up into two async requests and wait for both to complete. You'll probably be waiting on the file route to complete anyway.
I'm writing a Play 2.3.2 application in Scala.
In my application I'm writing a method that call an other controller method like the following:
def addTagToUser = CorsAction.async { request =>
implicit val userRestFormat = UserFormatters.restFormatter
implicit val inputFormat = InputFormatters.restFormatter
implicit val outputWriter = OutputFormatters.restWriter
//update the tag of a user
def updateTagToUserDB(value: JsValue): Future[Boolean] = {
val holder : WSRequestHolder = WS.url("http://localhost:9000/recommendation/ advise")
val complexHolder = holder.withHeaders("Content-Type" -> "application/json")
complexHolder.post(value).map(response => response.status match {//handle the response
case 200 => true
case _ => false
}
)
}
val jsonData = request.body.asJson //get the json data
jsonData match {
case Some(x) => x.validate[Input] match {
case JsSuccess(input, _) => updateTagToUserDB(x).flatMap(status => status match {
case true => Future{Ok}
case _ => Future{InternalServerError("Error on update the users tags")}
})
case e: JsError => Future{BadRequest("json bad formed")}
}
case None => Future{BadRequest("need a json value")}
}
}
But in this code I've the problem that the url is create static, Is possible to get the absolute uri of a Controller method in Play??
How can I make that??
As mentioned in reverse routing section of Play docs, you can achieve this with the following method call:
routes.Application.advise()
Note that routes exists in controllers so if you are in controllers package you can simply access reverse routes with routes.ControllerName.methodName.
From other parts of the code you need to use the fully qualified package, i.e. controllers.reverse.Application.advise().
If controller method takes a parameter you need to pass the desired argument and get the actual route, for example routes.Application.someMethod(10).
Reverse routing is a powerful asset in Play toolbox which frees you from repeating yourself. It's future proof in a sense that if you change your route, the change will be reflected automatically to the whole application.
Alternative
This approach may not be the best approach.
Redirecting to another controller makes sense, but sending a request to another controller which resides just inside the same web app is overkill and unnecessary. It would be more wise if your web app serves responses to outside not to request a response from itself.
You can easily avoid it by putting the common logic somewhere else and use it from both controllers. According to best practices a good controller is a thin one! By better layering life will be much easier.
I'm trying to use Swagger to document a Play 2 REST API but swagger-play2 doesn't seem to understand optional parameters defined with Scala's Option type - the normal way to make a param optional in Play 2:
GET /documents controllers.DocumentController.getDocuments(q: Option[String])
I want the q param to be optional. There is a matching annotated controller method with this Option[String] param. On startup I'm getting UNKOWN TYPE in the log and the json produced by api-docs breaks swagger-ui:
UNKNOWN TYPE: scala.Option
[info] play - Application started (Dev)
Is there another way to specify an optional parameter in Play 2 and have Swagger understand it?
One workaround I've found so far is to remove the param from the params list, use Swagger's #ApiImplicitParams annotation and grab the param from the request object in your controller method. Swagger will then consider the param to be optional.
GET /documents controllers.DocumentController.getDocuments()
and then in the controller:
#ApiOperation(...)
#ApiImplicitParams(Array(
new ApiImplicitParam(name = "q", value = "Query", required = false, dataType = "string", paramType = "query"),
))
def getDocuments = Action { implicit request =>
// use param via request object
}
This is certainly not as nice as using Scala's Option type but it produces correct Swagger docs.
I've worked around this similarly to #Tom Wadley's answer.
This code creates the problem:
#ApiOperation( ... )
def foo(#ApiParam(value="Argument 1") #PathParam("a1") a1 : Option[Int]) = ...
To avoid the problem just remove the annotations from the argument, and instead declare an implicit parameter with the same name:
#ApiOperation( ... )
#ApiImplicitParams(Array(new ApiImplicitParam(name="a1", dataType="Int", required=false, paramType="query", ...)
def foo(a1 : Option[Int]) = ...
(Scala 2.11.2, Play 2.3, Swagger 1.3.8)
I logged Issue 706 against Swagger too.
Alternatively you can use this lib
https://github.com/iheartradio/play-swagger
This library takes a different approach than annotation (which force you into learning a new API), you write swagger spec directly in your routes file as comments. It automatically generates parameters definition based on routes file and for Option[T] typed parameters it automatically mark them as required=false.
The APIImplicitParam workaround was not working for me.
Another workaround is to omit the option param from the routes
GET /documents controllers.DocumentController.getDocuments()
But grab it in the code:
val qSeq = request.queryString.get("q")
val q = qSeq match {
case None => None
case Some(seq) => seq.headOption
}
and annotate it with ApiImplicitParam for Swagger docs