├── blogy
├── conf
│ ├── application.conf
│ ├── routes
│ └── logback.xml
├── project
│ ├── build.properties
│ └── plugins.sbt
├── build.sbt
├── app
│ ├── views
│ │ ├── layout.scala.html
│ │ └── index.scala.html
│ └── controllers
│ │ └── IndexController.scala
└── .gitignore
├── images
├── template-for.png
├── template-index.png
└── action-not-found.png
├── README.md
├── 02-templates.md
└── 01-hello-world.md
/blogy/conf/application.conf:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/blogy/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version=0.13.16
--------------------------------------------------------------------------------
/blogy/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.3")
--------------------------------------------------------------------------------
/images/template-for.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shekhargulati/play-the-missing-tutorial/HEAD/images/template-for.png
--------------------------------------------------------------------------------
/images/template-index.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shekhargulati/play-the-missing-tutorial/HEAD/images/template-index.png
--------------------------------------------------------------------------------
/images/action-not-found.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shekhargulati/play-the-missing-tutorial/HEAD/images/action-not-found.png
--------------------------------------------------------------------------------
/blogy/build.sbt:
--------------------------------------------------------------------------------
1 | name := "blogy"
2 |
3 | version := "1.0.0-SNAPSHOT"
4 |
5 | lazy val root = (project in file(".")).enablePlugins(PlayScala)
6 |
7 | scalaVersion := "2.12.3"
8 |
9 | libraryDependencies += guice
--------------------------------------------------------------------------------
/blogy/conf/routes:
--------------------------------------------------------------------------------
1 | # Routes
2 | # This file defines all application routes (Higher priority routes first)
3 | # ~~~~
4 |
5 |
6 | GET / controllers.IndexController.index()
7 | GET /index controllers.IndexController.index()
--------------------------------------------------------------------------------
/blogy/app/views/layout.scala.html:
--------------------------------------------------------------------------------
1 | @(title: String)(content: Html)
2 |
3 |
4 |
5 | @title
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/blogy/app/views/index.scala.html:
--------------------------------------------------------------------------------
1 | @(title: String, user: Map[String, String], posts: List[Map[String, String]])
2 | @layout(title) {
3 | @if(user.get("username").isDefined){
4 | Hello, @user.get("username").get!
5 | }else{
6 | Hello, Guest!
7 | }
8 | @for(post <- posts){
9 | @post.getOrElse("author","Guest") says: @post.getOrElse("body","Hello!")
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/blogy/app/controllers/IndexController.scala:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import play.api.mvc._
4 |
5 | class IndexController extends Controller {
6 |
7 | def index = Action {
8 | val user = Map("username" -> "shekhargulati")
9 | val posts = List(
10 | Map("author" -> "Shekhar",
11 | "body" -> "Getting started with Play"
12 | ),
13 | Map("author" -> "Rahul",
14 | "body" -> "Getting started with Docker"
15 | )
16 | )
17 | Ok(views.html.index("Welcome to Blogy", user, posts))
18 | }
19 |
20 | }
--------------------------------------------------------------------------------
/blogy/conf/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | %coloredLevel %logger{15} - %message%n%xException{10}
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/blogy/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 | ### SBT template
3 | # Simple Build Tool
4 | # http://www.scala-sbt.org/release/docs/Getting-Started/Directories.html#configuring-version-control
5 |
6 | target/
7 | lib_managed/
8 | src_managed/
9 | project/boot/
10 | .history
11 | .cache
12 | ### PlayFramework template
13 | # Ignore Play! working directory #
14 | bin/
15 | /db
16 | .eclipse
17 | /lib/
18 | /logs/
19 | /modules
20 | /project/project
21 | /project/target
22 | /target
23 | tmp/
24 | test-result
25 | server.pid
26 | *.iml
27 | *.eml
28 | /dist/
29 | .cache
30 | ### Scala template
31 | *.class
32 | *.log
33 |
34 | # sbt specific
35 | .cache
36 | .history
37 | .lib/
38 | dist/*
39 | target/
40 | lib_managed/
41 | src_managed/
42 | project/boot/
43 | project/plugins/project/
44 |
45 | # Scala-IDE specific
46 | .scala_dependencies
47 | .worksheet
48 | ### JetBrains template
49 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio
50 |
51 | *.iml
52 |
53 | ## Directory-based project format:
54 | .idea/
55 | # if you remove the above rule, at least ignore the following:
56 |
57 | # User-specific stuff:
58 | # .idea/workspace.xml
59 | # .idea/tasks.xml
60 | # .idea/dictionaries
61 |
62 | # Sensitive or high-churn files:
63 | # .idea/dataSources.ids
64 | # .idea/dataSources.xml
65 | # .idea/sqlDataSources.xml
66 | # .idea/dynamic.xml
67 | # .idea/uiDesigner.xml
68 |
69 | # Gradle:
70 | # .idea/gradle.xml
71 | # .idea/libraries
72 |
73 | # Mongo Explorer plugin:
74 | # .idea/mongoSettings.xml
75 |
76 | ## File-based project format:
77 | *.ipr
78 | *.iws
79 |
80 | ## Plugin-specific files:
81 |
82 | # IntelliJ
83 | /out/
84 |
85 | # mpeltonen/sbt-idea plugin
86 | .idea_modules/
87 |
88 | # JIRA plugin
89 | atlassian-ide-plugin.xml
90 |
91 | # Crashlytics plugin (for Android Studio and IntelliJ)
92 | com_crashlytics_export_strings.xml
93 | crashlytics.properties
94 | crashlytics-build.properties
95 |
96 | tmp.ws
97 |
98 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Play: The Missing Tutorial ™
2 | ------
3 |
4 | Last weekend(19-20 March 2016) I started learning [Play Framework](https://www.playframework.com/) as part of my [52 Technologies in 2016](https://github.com/shekhargulati/52-technologies-in-2016) blog series. Rather than writing about Play framework in one tutorial I decided to create a tutorial series(in a separate Github repository) so that I can focus on it individually. This repository will host both the content and sample application source code. This work is inspired by [The Flask Mega Tutorial](http://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-i-hello-world) written by [Miguel Grinberg](https://twitter.com/miguelgrinberg). The Flask Mega Tutorial is very well written step by step introduction on how to build Python web applications using Flask framework.
5 |
6 | Play Framework is not an easy framework to get started. You can very quickly get overwhelmed by its features and complexity. The goal of this tutorial is to help you build an application step by step so that you remain focussed and get most out of the Play framework.
7 |
8 | **This tutorial will cover Play Scala framework version 2.5.0**
9 |
10 | ## Application
11 |
12 | In this tutorial, we will build a blogging platform called `blogy` that you can use to write and publish blogs. To build this web application, we will cover topics mentioned below.
13 |
14 | ## Table of Contents
15 |
16 | These are some of the topics I will cover as we make progress with this project:
17 |
18 | * [Part 1: Hello World!](./01-hello-world.md)
19 | * [Part 2: Templates](./02-templates.md)
20 |
21 | ## Contributing to the Play: The Missing Tutorial ™
22 |
23 | Please contribute if you see an error or something that could be better! Raise an [issue](https://github.com/shekhargulati/play-the-missing-tutorial/issues) or send me a pull request to improve. Contributions of all kinds, including corrections, additions, improvements, and translations, are welcome!
24 |
25 | -----------
26 | You can follow me on twitter at [https://twitter.com/shekhargulati](https://twitter.com/shekhargulati) or email me at . Also, you can read my blogs at [http://shekhargulati.com/](http://shekhargulati.com/)
27 |
28 | [](https://github.com/igrigorik/ga-beacon)
29 |
--------------------------------------------------------------------------------
/02-templates.md:
--------------------------------------------------------------------------------
1 | # Play Scala Templates
2 |
3 | In the [previous chapter](./01-hello-world.md), we created a basic Play Scala application that renders `Hello, World!` when a HTTP GET request is made to index `/`. You can run the application by executing `sbt run` command from inside the application root directory.
4 |
5 | We will start this chapter from where we left in last chapter, so you may want to make sure you have the above application correctly installed and working.
6 |
7 | ## Github repository
8 |
9 | The code for demo application is available on github: [blogy](./blogy). You should get the `part-02` release. If you are not comfortable with Git, then you can directly download the [part-02.zip](https://github.com/shekhargulati/play-the-missing-tutorial/archive/part-02.zip).
10 |
11 | ## Why we need templates?
12 |
13 | Let's start with the use case that we have to render HTML page when user visits our application. The page should have welcome message with name of the logged in user. In this chapter, we will ignore the fact that we don't have user authentication and authorization in place. We will use a dummy user to convey the point.
14 |
15 | One way to render HTML would be to return the HTML in the response body as shown below.
16 |
17 | ```scala
18 | package controllers
19 |
20 | import javax.inject._
21 | import play.api.mvc._
22 |
23 | class IndexController @Inject()(val controllerComponents: ControllerComponents) extends BaseController {
24 |
25 | def index() = Action {
26 | val user = Map("username" -> "shekhargulati")
27 | Ok {
28 | s"""
29 | |
30 | |
31 | | Home Page
32 | |
33 | |
34 | | Hello, ${user.getOrElse("username", "guest")}
35 | |
36 | |
37 | """.stripMargin
38 | }.as("text/html")
39 | }
40 |
41 | }
42 | ```
43 |
44 | In the code shown above, we modified the `index` method to return HTML in the response body. `Ok` is factory method to produce `Result`. `Result` is value object which defines the response header and a body ready to send to the client. We set the `Content-Type` to `text/html` using the `as` method so that browser render it as HTML.
45 |
46 | Go to [http://localhost:9000/](http://localhost:9000/) to see how it looks in your browser.
47 |
48 | The approach we have taken above is a quick and easy hack to render HTML but it will not scale for complex pages. It will very quickly become unmanageable as we are mixing multiple concerns together.
49 |
50 | ## Templates can help
51 |
52 | Template allows you to separate presentation concern from the rest of the application. This makes it easy for UI designers to work independently on the user interface concern without being bothered by the application source code.
53 |
54 | Play comes bundled with [Twirl](https://github.com/playframework/twirl), a powerful Scala based template engine.
55 |
56 | Let's write our first template. Templates are typically created inside the `app/views` directory. Create a new folder called `views`, a new file in the newly created `views` folder called `index.scala.html`, and populate it with following content.
57 |
58 | ```html
59 | @(title: String, user: Map[String, String])
60 |
61 |
62 |
63 | @title
64 |
65 |
66 | Hello, @user.getOrElse("username", "guest")!
67 |
68 |
69 | ```
70 |
71 | As you case see above, we wrote a mostly standard HTML page with some placeholders marked with `@` for dynamic content. Every time Twirl template engine encounters `@` character, it indicates the beginning of a dynamic statement. If you have used other templates engines like mustache or jinja then you would have used placeholders like `{{}}`. The statement after `@` is valid Scala code as you can see from `@user.getOrElse("username","guest")`. We are calling `getOrElse` function of `Map`.
72 |
73 | > **Because @ is a special character, you’ll sometimes need to escape it. Do this by using @@.**
74 |
75 | One thing that we have not discussed is the first line of template `@(title: String, user: Map[String, String])`. Let's decipher this statement. Each Twirl template is compiled to a function. The first line defines the parameters of this template function. The index template function needs two parameters of type `String` and `Map[String, String]`. Now, the controller who will use this template will have to pass these as method parameters or template will not compile and you will see compilation error when you will view the page.
76 |
77 | Now, let's see how we can use the template defined above in our controller.
78 |
79 | ```scala
80 | package controllers
81 |
82 | import javax.inject._
83 | import play.api.mvc._
84 |
85 | class IndexController @Inject()(val controllerComponents: ControllerComponents) extends BaseController {
86 |
87 | def index = Action {
88 | val user = Map("username" -> "shekhargulati")
89 | Ok(views.html.index("Welcome to Blogy", user))
90 | }
91 |
92 | }
93 | ```
94 |
95 | In the code shown above, we are returning the index template instead of hard coded HTML. You can also notice that we are passing the function parameters as well. `index` is a class with an `apply` method so you can call as you are calling a function.
96 |
97 | Let's understand what happens when you return a template using `views.html.index("Welcome to Blogy", user)`. When the view is rendered for the first time, `index.scala.html` template is compiled to a class with same name as template i.e. index in our case. This class extends from `BaseScalaTemplate`, which is the base class for all Twirl templates. This class has all the utility methods that the template might need to convert the `index.scala.html` template to actual html. `index` class also extend one trait `play.twirl.api.Template*`, `*` depends on the number of parameters template needs. In our case, `play.twirl.api.Template2` as we have two parameters defined in the `index.scala.html` template. The index class looks as shown below.
98 |
99 | ```scala
100 | import play.twirl.api._
101 |
102 | class index extends BaseScalaTemplate[HtmlFormat.Appendable,Format[HtmlFormat.Appendable]](HtmlFormat) with Template2[String,Map[String, String]],HtmlFormat.Appendable] {
103 |
104 | def apply(title: String, user: Map[String, String]):HtmlFormat.Appendable = {
105 | ...
106 | }
107 | }
108 | ```
109 |
110 | You can view the application by opening [http://localhost:9000/](http://localhost:9000/) in your favorite browser. It will look something like as shown below.
111 |
112 | 
113 |
114 | ## Using Control statements in templates
115 |
116 | Twirl templates have support for control statements. Let's add an if statement that will render username if it exists in the Map or `Guest` otherwise.
117 |
118 | ```html
119 | @(title: String, user: Map[String, String])
120 |
121 |
122 | @title
123 |
124 |
125 | @if(user.get("username").isDefined){
126 | Hello, @user.get("username").get!
127 | } else {
128 | Hello, Guest!
129 | }
130 |
131 |
132 | ```
133 |
134 | Feel free to test it by removing username from the Map to see control statement in action.
135 |
136 | ## Iterating in templates
137 |
138 | The logged in user in our `blogy` application will probably want to see recent posts from followed users in the home page, so let's see how we can do that.
139 |
140 | Let's update the controller to return a list of posts as well. Now, the `views.html.index` function is taking three parameters. We have to change our template to reflect that.
141 |
142 | ```scala
143 | package controllers
144 |
145 | import play.api.mvc._
146 |
147 | class IndexController extends Controller {
148 |
149 | def index() = Action {
150 | val user = Map("username" -> "shekhargulati")
151 | val posts = List(
152 | Map(
153 | "author" -> "Shekhar",
154 | "body" -> "Getting started with Play"
155 | ),
156 | Map(
157 | "author" -> "Rahul",
158 | "body" -> "Getting started with Docker"
159 | )
160 | )
161 | Ok(views.html.index("Welcome to Blogy", user, posts))
162 | }
163 |
164 | }
165 | ```
166 |
167 | We are using a List to store a post. Each post is Map with key value pairs. Later in this series, we will use a real database to store and fetch the data. For now, we will go with in-memory data structure representing data from the database.
168 |
169 | Let's update our template to add `posts` as its parameter. To iterate over list, we will use the `for` loop as shown below.
170 |
171 | ```html
172 | @(title: String, user: Map[String, String], posts: List[Map[String, String]])
173 |
174 |
175 | @title
176 |
177 |
178 | @if(user.get("username").isDefined) {
179 | Hello, @user.get("username").get!
180 | } else {
181 | Hello, Guest!
182 | }
183 | @for(post <- posts) {
184 | @post.getOrElse("author","Guest") says: @post.getOrElse("body","Hello!")
185 | }
186 |
187 |
188 | ```
189 |
190 | The page will be rendered as shown below.
191 |
192 | 
193 |
194 | ## Defining reusable layout templates
195 |
196 | One good practice when working with templates is to make sure they don't duplicate content. To achieve that, you should define layout that templates can reuse. Our `blogy` web application will need to have a navigation bar at the top of the page with a few links. Here you will get the link to edit your profile, to login, logout, etc.
197 |
198 | We can create a new template `layout.scala.html` that will define a base template that includes the navigation bar and page structure.
199 |
200 | ```html
201 | @(title: String)(content: Html)
202 |
203 |
204 |
205 | @title
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 | ```
214 |
215 | As you can see above, layout template takes two parameters: a title and an HTML content block.
216 |
217 | We will update `index.scala.html` to use the base layout.
218 |
219 | ```html
220 | @(title: String, user: Map[String, String], posts: List[Map[String, String]])
221 | @layout(title) {
222 | @if(user.get("username").isDefined){
223 | Hello, @user.get("username").get!
224 | }else{
225 | Hello, Guest!
226 | }
227 | @for(post <- posts){
228 | @post.getOrElse("author","Guest") says: @post.getOrElse("body","Hello!")
229 | }
230 | }
231 | ```
232 |
233 | ---
234 |
235 | That's it for the second part of Play framework tutorial. If you have any feedback then you can add a comment to this Github issue [https://github.com/shekhargulati/play-the-missing-tutorial/issues/1](https://github.com/shekhargulati/play-the-missing-tutorial/issues/1).
236 |
237 | [](https://github.com/igrigorik/ga-beacon)
238 |
--------------------------------------------------------------------------------
/01-hello-world.md:
--------------------------------------------------------------------------------
1 | # Let's say Hello World! with Play Framework
2 |
3 |
4 | [Play framework](https://www.playframework.com/) is a MVC style web application framework for JVM built using Scala, Akka, and Netty. It provides API for both Java and Scala programming languages. You can use it to build either your traditional web applications with server side rendering or modern [single-page application (SPA)](https://en.wikipedia.org/wiki/Single-page_application) that uses REST with JavaScript MVC framework like AngularJS. One design decision that makes Play different from other Java/Scala MVC web frameworks is that it is not built on top of the Servlet standard. It is full stack Java framework that runs your application stand-alone. **Play is a framework not a library**.
5 |
6 | ## Application
7 |
8 | In this tutorial series, we will build a blogging platform called `blogy` that you can use to write and publish blogs.
9 |
10 | ## Prerequisite
11 |
12 | To work along with this tutorial, you will need following installed on your machine.
13 |
14 | 1. [Download and install latest JDK 8 update](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html) on your operating system. I am using JDK version `1.8.0_60`.
15 |
16 | 2. [Download and install latest Scala version](http://www.scala-lang.org/download/) on your machine. I am using Scala `2.12.3`.
17 |
18 | 3. [Download and install the latest SBT version](http://www.scala-sbt.org/download.html) on your machine. I am using SBT version `0.13.16`.
19 |
20 | 4. An IDE of your choice. You can use either of [IntelliJ](https://www.jetbrains.com/idea/download/) or [Eclipse](http://scala-ide.org/). I prefer IntelliJ.
21 |
22 | 5. You should be comfortable reading and writing Scala code. Throughout the tutorial series, we will also use SBT so in case you are not familiar with SBT then you can read [my SBT tutorial](https://github.com/shekhargulati/52-technologies-in-2016/tree/master/02-sbt).
23 |
24 | ## Github repository
25 |
26 | The code for demo application is available on github: [blogy](./blogy). You should get the `part-01` release. If you are not comfortable with Git, then you can directly download the [part-01.zip](https://github.com/shekhargulati/play-the-missing-tutorial/archive/part-01.zip).
27 |
28 | ## Getting started with Play framework
29 |
30 | Okay, let's get started with application development.
31 |
32 | In this series, we will use the latest Play framework version `2.6.3`. Play documentation recommends that one should [download a Starter Project](https://playframework.com/download#starters) to quickly get started with Play framework. These starter projects contains everything you need to go from zero to a running Play project. There are a lot of official and templates that one can choose from. You can also start a new project using `sbt new` like:
33 |
34 | ```bash
35 | $ sbt new playframework/play-scala-seed.g8
36 | ```
37 |
38 | For Scala projects or:
39 |
40 | ```bash
41 | $ sbt new playframework/play-java-seed.g8
42 | ```
43 |
44 | For Java based projects.
45 |
46 | Both templates above generate minimum projects so that you can get (almost) only the basic project structure. But you can also create the project manually if you think it is less intimidating and easier to understand.
47 |
48 | ### Manually creating the project
49 |
50 | Open a new command-line terminal and navigate to a convenient location on your file system where you want to host the application source code. Let's call the application folder `blogy`.
51 |
52 | ```bash
53 | $ mkdir -p blogy/{app/controllers,conf,project}
54 | $ cd blogy
55 | ```
56 |
57 | Create the following directory layout:
58 |
59 | ```
60 | blogy/
61 | ├── app
62 | │ └── controllers
63 | ├── conf
64 | └── project
65 | ```
66 |
67 | We will start with creating our build file `build.sbt`. Play uses SBT to build and run the application. Create a new file `build.sbt` inside the root i.e. `blogy` directory and populate it with following contents.
68 |
69 | ```scala
70 | name := "blogy"
71 |
72 | version := "1.0.0-SNAPSHOT"
73 |
74 | lazy val root = (project in file(".")).enablePlugins(PlayScala)
75 |
76 | scalaVersion := "2.12.3"
77 |
78 | libraryDependencies += guice
79 | ```
80 |
81 | The build file shown above does the following:
82 |
83 | 1. It specified name of the project as `blogy`.
84 | 2. Then, we specified version of the project as `1.0.0-SNAPSHOT`.
85 | 3. Next, it enables `PlayScala` plugin for `blogy` project. Later, we will add Play SBT plugin to the `blogy` application.
86 | 4. Finally, we set Scala version to `2.12.3`.
87 |
88 | Now, we will add Play Scala plugin to our project so that it is treated as a Play application. In SBT, you declare plugins you need in your project by adding them to `plugins.sbt` file inside the `project` directory. Create a new file `plugins.sbt` inside the project directory and populate it with following contents.
89 |
90 | ```scala
91 | addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.3")
92 | ```
93 |
94 | This is one required SBT plugin that we will need in our project. There are many more plugins like `sbt-coffeescript`, `sbt-less`, `sbt-scalariform`, etc that we can add for adding different capabilities to our project. We will add few more plugins later in this series.
95 |
96 | It is a good practice in SBT projects to lock down the version of SBT. By default, the installed version of SBT will be used by the application. This might cause compatibility issues if the future version of SBT becomes incompatible with Play. To force a SBT version, create a new file `build.properties` inside the `project` directory and add the following line to it.
97 |
98 | ```
99 | sbt.version=0.13.16
100 | ```
101 |
102 | Now, to test our current setup launch the `sbt` command from inside the `blogy` directory. You will see output as shown below.
103 |
104 | ```bash
105 | $ sbt
106 | ```
107 |
108 | ```
109 | [info] Loading global plugins from /Users/shekhargulati/.sbt/0.13/plugins
110 | [info] Loading project definition from /Users/shekhargulati/dev/git/play-the-missing-tutorial/blogy/project
111 | [info] Updating {file:/Users/shekhargulati/dev/git/play-the-missing-tutorial/blogy/project/}blogy-build...
112 | [info] Resolving org.fusesource.jansi#jansi;1.4 ...
113 | [info] Done updating.
114 | [info] Set current project to blogy (in build file:/Users/shekhargulati/dev/git/play-the-missing-tutorial/blogy/)
115 | [blogy] $
116 | ```
117 |
118 | Write `play` and press tab you will see all play specific tasks.
119 |
120 | ```
121 | [blogy] $ play
122 | playAggregateReverseRoutes playAllAssets playAssetsClassloader playAssetsWithCompilation playCommonClassloader playCompileEverything playDefaultAddress
123 | playDefaultPort playDependencyClasspath playDevSettings playDocsJar playDocsModule playDocsName playExternalizeResources
124 | playExternalizedResources playGenerateReverseRouter playGenerateSecret playInteractionMode playJarSansExternalized playMonitoredFiles playNamespaceReverseRouter
125 | playOmnidoc playPackageAssets playPlugin playPrefixAndAssets playReload playReloaderClasspath playRoutes
126 | playRoutesGenerator playRoutesImports playRoutesTasks playRunHooks playStop playUpdateSecret
127 |
128 | ```
129 |
130 | To run a Play project, you can use the `run` task. It will start the server at port `9000`.
131 |
132 | ```
133 | [blogy] $ run
134 |
135 | --- (Running the application, auto-reloading is enabled) ---
136 |
137 | [info] p.c.s.AkkaHttpServer - Listening for HTTP on /0:0:0:0:0:0:0:0:9000
138 |
139 | (Server started, use Enter to stop and go back to the console...)
140 | ```
141 |
142 | > **You can specify a different port number by just passing it along with run task like `run 8080`. This will start Play application at port 8080.**
143 |
144 | You can access the application at [http://localhost:9000/](http://localhost:9000/). On accessing the page, you will be greeted with following error message.
145 |
146 | ```
147 | java.io.IOException: resource not found on classpath: application.conf, application.json
148 | ```
149 |
150 | Every play framework application needs a configuration file `application.conf` inside the configuration directory. Create a new file `application.conf`. You can have an empty configuration for now and then the defaults will be used. `application.conf` uses HOCON(Human Optimized Config Object Notation) notation. According to [HOCON documentation](https://github.com/typesafehub/config/blob/master/HOCON.md),
151 |
152 | > **The primary goal is: keep the semantics (tree structure; set of types; encoding/escaping) from JSON, but make it more convenient as a human-editable config file format.**
153 |
154 | Access the index page [http://localhost:9000/](http://localhost:9000/) again, this time you will be greeted by different error page.
155 |
156 | 
157 |
158 | The reason for this error message is that we have not mapped any action to the GET request to index `/` path.
159 |
160 | ## "Hello, World" with Play
161 |
162 | You now have a working Play application environment. Let's write our first Play controller. In Play, you write controllers which define `Action`s that process the request and return response. The decision to select an `Action` is made by the `router` which uses the `routes` configuration to select the correct `Action`. The `router` looks at the request details like method, path and query parameters to decide which `Action` should handle the request.
163 |
164 | Create your first controller inside the `app/controllers` directory. We will name our controller `IndexController` as it is handling the index request.
165 |
166 | ```scala
167 | package controllers
168 |
169 | import javax.inject._
170 | import play.api.mvc._
171 |
172 | class IndexController @Inject()(val controllerComponents: ControllerComponents) extends BaseController {
173 |
174 | def index() = Action {
175 | Ok("Hello, World!")
176 | }
177 |
178 | }
179 | ```
180 |
181 | The code shown above does the following:
182 |
183 | 1. It import all the classes and traits inside the `javax.inject` and `play.api.mvc` packages.
184 | 2. Next, we created a Scala class that extends `BaseController` trait. `BaseController` provides access to all the utility methods to generate `Action` and `Result` types and requires that you implements `controllerComponents` method.
185 | 3. So we inject an instance of `ControllerComponents` as a `val` to have a full implementation of `BaseController`.s
186 | 3. Finally, we created a method `index` that returns an `Action`. Action is a function `Request[A] => Result` that takes a request and returns a result. We returned HTTP status Ok i.e. 200 with `Hello, World!` text in the response body. Action syntax `Action {}` is possible because of a `apply` method defined in the `Action` object that takes a block `def apply(block: => Result): Action[AnyContent]`.
187 |
188 | Now, we will map the index URL `/` to index `Action` by defining configuration in the `routes` configuration. Create a new file `routes` inside the `conf` directory.
189 |
190 | ```
191 | # Routes
192 | # This file defines all application routes (Higher priority routes first)
193 | # ~~~~
194 |
195 | GET / controllers.IndexController.index()
196 | ```
197 |
198 | As is obvious, we are mapping HTTP GET request to `/` to `controllers.IndexController.index()` action method.
199 |
200 | You can now test that `/` url is working by either opening [http://localhost:9000/](http://localhost:9000/) in the browser or using a command-line tool like cURL as shown below.
201 |
202 | ```bash
203 | $ curl -i http://localhost:9000/
204 | ```
205 |
206 | ```
207 | HTTP/1.1 200 OK
208 | Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin
209 | X-Frame-Options: DENY
210 | X-XSS-Protection: 1; mode=block
211 | X-Content-Type-Options: nosniff
212 | Content-Security-Policy: default-src 'self'
213 | X-Permitted-Cross-Domain-Policies: master-only
214 | Date: Fri, 08 Sep 2017 22:33:29 GMT
215 | Content-Type: text/plain; charset=UTF-8
216 | Content-Length: 12
217 |
218 | Hello, World
219 | ```
220 |
221 | You can map multiple URLs to the same action by defining the route configuration for new URL. Let's suppose we want to map both `/` and `/index` to the same controller then we can update our `routes` file as shown below.
222 |
223 | ```
224 | GET / controllers.IndexController.index()
225 | GET /index controllers.IndexController.index()
226 | ```
227 |
228 | Every Play application has access to the Play documentation at [http://localhost:9000/@documentation/Home](http://localhost:9000/@documentation/Home).
229 |
230 | > **When you run the Play application using the `run` task, Play application is launched in the `dev` mode. In dev mode, sources are constantly watches for changes, and whenever source changes project is reloaded with new changes. There are two other modes of Play application - `test` and `production`. We will look at them later in this series.**
231 |
232 | You can stop the running server by pressing `Ctrl+D` Or `ENTER`. This will take you back to the SBT shell. If you will press `Ctrl+C` then SBT process will also exit and you will be back to your command-line terminal.
233 |
234 | ---
235 |
236 | That's it for the first part of Play framework tutorial. If you have any feedback then you can add a comment to this Github issue [https://github.com/shekhargulati/play-the-missing-tutorial/issues/1](https://github.com/shekhargulati/play-the-missing-tutorial/issues/1).
237 |
238 | You can read next part [here](./02-templates.md).
239 |
240 | [](https://github.com/igrigorik/ga-beacon)
241 |
--------------------------------------------------------------------------------