Index routes in Scala Play Framework 2.5.x - scala

I am super new to the Play Framework and just started my first experiences with the play-scala-rest-api-example. I copied the post package as a new user package, added the UserRouter to the routes and appended the UserRepository to the Module configurations.
Everything works fine, it's just that I get an 404 error when visiting /v1/users. Only /v1/users/ works. For posts both with and without the slash at the end call the index route. I didn't change anything but renaming the classes and objects. Like I said I just started to play around with the framework and possibly it is something super trivial.
Here the routes file:
GET / controllers.HomeController.index
-> /v1/posts api.post.PostRouter
-> /v1/users api.user.UserRouter
# Map static resources from the /public folder to the /assets URL path
GET /assets/*file controllers.Assets.at(path="/public", file)
`

I found what was missing. So for any else using this tutorial and facing the same problem. You have to add your custom prefix to the app/RequestHandler.scala isREST() method. So the handlerForRequest method automatically adds a trailing slash at the end.
private def isREST(request: RequestHeader) = {
request.uri match {
case uri: String if uri.contains("post") => true
case uri: String if uri.contains("user") => true
case _ => false
}
}
or
private def isREST(request: RequestHeader) = {
request.uri match {
case uri: String if uri.contains("post") | uri.contains("user") => true
case _ => false
}
}

Your file should be
GET / controllers.HomeController.index
GET /v1/posts api.post.PostRouter
GET /v1/users api.user.UserRouter
# Map static resources from the /public folder to the /assets URL path
GET /assets/*file controllers.Assets.at(path="/public", file)
The -> symbol is for some especial cases that probably you do not need right now.
https://www.playframework.com/documentation/2.5.x/ScalaRouting

Related

Playframework Scala - Delete Route problems

I'm working with playframework for final project at university and I'm getting a problem when routing a delete or put method.
When I'm requesting a DELETE or PUT methods I'm getting:
[info] play.api.Play - Application started (Dev)
[debug] a.ErrorHandler - onClientError: statusCode = 404, uri = /Rest/deleteCity, message ="
My JQuery ajax call is:
$("#scalaDelete").click(function(){
$("#result").empty();
$.ajax({
url: "http://localhost:9000/Rest/deleteCity",
method: "DELETE",
data: {city: "Alvorada"},
dataType: "json",
success: function(result){
$("#result").append("Result: "+result.Result);
},
error: function (request, status, error) {
alert(status);
}
});
});
My Route Play Route:
DELETE /Rest/deleteCity controllers.RestController.deleteCity()
My Controller Method:
case class UserDelete(city:String)
class RestController #Inject()(db: Database, cc: ControllerComponents) extends AbstractController(cc) {
val userDeleteForm = Form(
mapping(
"city" -> text
)(UserDelete.apply)(UserDelete.unapply)
)
def deleteCity = Action{ implicit request=>
val userPar = userDeleteForm.bindFromRequest.get
//DatabaseDelete
Ok(jsonResult)
}
}
I've already activated cross domain in chrome, I've used a CORS extension for it.
Thanks for helping
This seems related to Restful http delete in play, i.e. DELETE with data can be sketchy.
Instead of passing data, I would just move this to the url:
DELETE /Rest/deleteCity/:city controllers.RestController.deleteCity(city: String)
# or with a query string
DELETE /Rest/deleteCity controllers.RestController.deleteCity(city: String)
and then do
http://localhost:9000/Rest/deleteCity/Alvorada
# or with a query string
http://localhost:9000/Rest/deleteCity?city=Alvorada
Personally I prefer the latter.
I agree with #AndyHayden.
Play ignores the body of the DELETE request, that is the correct behavior to my mind, but you can work around by explicitly passing a body parser:
def delete = Action(parse.json) { implicit request =>
val json = request.body
val someProp = (json \ "someprop").as[String]
Ok(s"Prop is: $someProp")
}
(this example was given by one of the developers of the Play itself:
https://github.com/playframework/playframework/issues/4606#issuecomment-109192802.)
About the doubts in comments:
I've seen another post here where a guy said some browsers just support get and post method.
POST and GET are only valid for the method attribute of the form tag.
You are using javascript request, so you can use any method that server supports. i.e. DELETE is completely fine there.
But something interesting for you to know is that playframework uses akka and this framework does not support DELETE request for security reasons, in fact it wasn't well explained on post. Then if you wanna make a DELETE method you gotta make a post method for complete your code.
Akka HTTP supports the DELETE request (as well as Play Framework): https://doc.akka.io/docs/akka-http/current/scala/http/routing-dsl/directives/method-directives/delete.html

Scala Play 2.5 Controller class to serve static HTML

I want to serve a static file from a Scala Play controller. I am looking for something that would allow me to do something like this example below.
NOTE: This obviously does not work.
It is very possible that I am look at the problem in the wrong way, I however do NOT want to redirect to the app.html
def loadApplication(): EssentialAction = Action.sync { request =>
val contents = Assets.contentsOf("/public/assets/app.html") //This doesnot return the contents, but that is what I want
Ok(contents)
}
You can just use the Assets and return contents via that. You might have to tweak the path though:
class MyController #Inject() (assets: Assets) extends Controller {
def loadApplication(): Action[AnyContent] = Action.async { request =>
assets.at("/public/assets/", "app.html").apply(request)
}
}
More information may be found in the documentation: https://www.playframework.com/documentation/2.5.x/AssetsOverview#The-Assets-controller
Also note that you can map a route to your assets instead of statically referencing the file from the controller, like so:
GET /assets/*file controllers.Assets.at(path="/public", file)

How to get list of all Route URL strings in play framework?

I have many controllers in my play 2.4.x application.
I want to get a list of all route URLs pointing their respective controllers. I know how to get the URL from the current request. But I need a list of all the URLs available inside a play application.I want to generate this list dynamically because URLs can be changed/added/deleted in future.
So,is there some way I can generate this URL list dynamically ? Or do I have the obligation to store all the URLs statically somewhere in cache or dictionary ?
I obtained the desired list by using the documentation method provided by Router trait. The documentation method returns Seq[(String, String, String)] .Here each tuple has the format:
( {http-method} , {url} , {controller method} )
The Router trait is extended by all the autogenerated Routes.scala classes. Scala-compiler generated a separate Routes.scala for each routes file in the application. These auto-generated Routes.scala files implement all the methods of Router trait including the documentation method that we discussed above.
So,to get list of all URLs, I simply had to inject the Router trait and then access the documentation method:
import play.api.routing.Router
class MyClass #Inject()(router: Router) {
def getAllURLs:Seq[String] = router.documentation.map(k => k._2)
}
Update for Play 2.7, Scala:
class MyController #Inject()(routesProvider: Provider[play.api.routing.Router]) {
lazy val routes: Seq[(String, String, String)] = routesProvider.get.documentation
}
from discussion for play 2.6
In response to the accepted answer: Note that if you're working in a Controller and you try to inject the Router, you'll get a Circular Dependency error (because the Router depends on your Controller). You can fix this by doing the following:
private final Provider<Router> routerProvider;
#Inject
public MainController(Provider<Router> routerProvider) {
this.routerProvider = routerProvider;
}
And then
// later in execution
Router router = routerProvider.get();
if (!router.documentation().isEmpty()) {
html.append("<ul>");
router.documentation().forEach(doc ->
html.append("<li>")
.append("<b>Method</b>: ")
.append(doc.getHttpMethod())
.append(" <b>Path</b>: ")
.append(doc.getPathPattern())
.append(" <b>Controller</b>: ")
.append(doc.getControllerMethodInvocation())
.append("</li>"));
html.append("</ul>");
}

Embed ETag in URL

A question about asset fingerprinting in Play.
How to ask Play to embed ETags in URLs without using a third-party plugin?
E.g., if /css/resource.css had the ETag of 1234, then it would become /css/responsive-1234.css.
Related questions: Custom ETag algorithm for asset fingerprinting & Automatically Insert ETag (asset fingerprinting) as comment at top of the resource
There'll be the "out" routing and the "in" reverse routing. Firstly the "out" routing:
package controllers
import play.api.mvc._
object Resources extends Controller {
def fingerprintedUrl(path: String): String = {
// calculates ETag and add to URL path
val pathWithEtag = addEtagToPath(path)
pathWithEtag
}
}
Then in templates use the following for example:
<script src="#Resources.fingerprintedUrl("js/toolkit.js")"></script>
Now the reverse routing, add the following to the same controller:
def at(path: String): Action = {
val pathWithoutEtag = removeEtagFromPath(path)
Assets.at("/public", pathWithoutEtag)
}
Then in routes:
GET /resources/*file controllers.Resources.at(file)

Play: additional public / assets directory

In order to have a clean directory structure, I would like to make an additional assets folder public. I've created a directory 'assets' in my project folder, wrote an 'PictureAssets' controller which is nearly identical to 'controller.Assets', added 'assets' to the playAssetsDirectories in the build.sbt and tried following some route entries without success.
PictureAssets:
package controllers
import play.api.mvc.Action
import play.api.mvc.AnyContent
object PictureAssets extends AssetsBuilder {
def create : Action[AnyContent] = Action {
Ok(views.html.fileUploadForm())
}
}
build.sbt
playAssetsDirectories <+= baseDirectory / "assets"
routes
# GET /file controllers.PictureAssets.at(path="/assets", file="MM1.png")
GET /datei/*file controllers.PictureAssets.at(path="/assets", file)
# GET /datei/*file controllers.Assets.at(path="/assets", file)
If I try to access the URL, either nothing is displayed, or the error 'The image http:9000//localhost/datei/MM1.png cannot be displayed because it contains errors' is displayed or the css references to handled by the 'controller.Assets' don't work any more.
What am I missing?
I think the issue comes from the fact that the at method used is the default one used previously by Assets.
I ran into the same issue at some point last year, where I wanted to serve images that would be stored in a external folder, a folder that is somewhere on disk, and here is how I coded this:
I created a simple controller called Photos, that contained one Action:
object Photos extends Controller {
val AbsolutePath = """^(/|[a-zA-Z]:\\).*""".r
/**
* Generates an `Action` that serves a static resource from an external folder
*
* #param absoluteRootPath the root folder for searching the static resource files.
* #param file the file part extracted from the URL
*/
def at(rootPath: String, file: String): Action[AnyContent] = Action { request =>
val fileToServe = rootPath match {
case AbsolutePath(_) => new File(rootPath, file)
case _ => new File(Play.application.getFile(rootPath), file)
}
if (fileToServe.exists) {
Ok.sendFile(fileToServe, inline = true)
} else {
Logger.error("Photos controller failed to serve photo: " + file)
NotFound
}
}
}
Then, in my routes, I defined the following:
GET /photos/*file controllers.Photos.at(path="/absolute/path/to/photos",file)
This worked just fine for me. Hope this helps.
PS: This was in addition to the normal Assets controller that helped serving js and css files.