Help us understand the problem. What is going on with this article?

【Play超入門】Play FrameworkでWeb APIを作る ~導入編~【Scala】

Play Framework(Scala)の入門記事です。

シリーズ一覧です。

  1. 【Play超入門】Play FrameworkでWeb APIを作る ~導入編~【Scala】 ← この記事
  2. 【Play超入門】Play FrameworkでWeb APIを作る ~パラメータ取得編~【Scala】



(19/12/07 20:38追記)

作業環境をWindowsからLinux(Linux Mint 19.2 Tina)に変更しました。
そのため、既存のPowerShell箇所のコマンド・出力をBashに変更しました。


最近Play Frameworkを勉強し始めたので、アウトプットがてらやったことを書き記して行こうと思います。
今回はとりあえずひな形をインストールして、GET/POSTでアクセスできるようになるまでです。

Play Frameworkとは

軽量・ステートレス・非同期処理が特徴の、ScalaおよびJava用のWebフレームワークです。
Play Framework - Build Modern & Scalable Web Apps with Java and Scala

作りたいもの

Play Frameworkを使用し、簡単なWeb APIを作ります。
サーバーサイドレンダリングは行いません。

想定対象読者

  • Scalaの基本的な文法を理解している方。
  • Webの基本的な用語(GET/POST、リクエストヘッダー、Content-Typeなど)を理解している方。

前提条件

自身のマシンに以下のものをインストールしておく必要があります。

  • JDK(1.8またはそれ以降)
  • ビルドツール(sbt または Gradle)

動作環境

エディタはIntelliJ IDEAを使用していますが、できるだけIDEに依存しない書き方をするつもりです。
お好きなエディタを使ってください。

それと途中経過を(自分用に)保存するために、Gitで途中途中コミットしています。

なおOSがWindows10のため、コマンド類はPowerShellで実行しています。
MacやLinuxの方は適宜読み替えてください。

(19/12/07 追記) Linuxに変更したためbashの記述に書き換えました。

また、各ファイルの文字コードはUTF-8で統一します。

インストール

sbtを使い、Play Frameworkのひな形をインストールします。
便宜上、ホームディレクトリ配下のxxxフォルダにインストールします。
実際には好きなフォルダを使ってください。

~/xxx$ sbt new playframework/play-scala-seed.g8



上記コマンドを実行すると、nameとorganizationを求められます。
nameはここでは「play-api-sample」とします。
organizationは空欄のままEnterを押します(デフォルト値として「com.example」が設定されます)。

This template generates a Play Scala project

name [play-scala-seed]: play-api-sample
organization [com.example]:
  • 補足
    sbt 1.3.0以降ではnameへの入力値が表示されません(不具合?)。
    入力自体はされているので、一通り入力してからEnterを押していただければ先に進めます。



なおWindowsの場合下記のようなエラーが出るようですが、調べた限りでは無視して良いようです。
参考: Play Frameworkハンズオン環境構築 - Qiita

[error] java.io.IOException: Unable to delete file: C:\Users\xxx\AppData\Local\Temp\giter8-***\src\main\g8\.gitignore
[error]         at org.apache.commons.io.FileUtils.forceDelete(FileUtils.java:2400)
...



play-api-sampleフォルダができているので、そのまま移動します。

~/xxx$ cd play-api-sample/
~/xxx/play-api-sample$ 

以降、カレントディレクトリを表す~/xxx/play-api-sampleの部分は省略します。
フォルダ・ファイル構成としては以下のようになっています。

$ tree
.
├── app
│   ├── controllers
│   │   └── HomeController.scala
│   └── views
│       ├── index.scala.html
│       └── main.scala.html
├── build.sbt
├── conf
│   ├── application.conf
│   ├── logback.xml
│   ├── messages
│   └── routes
├── project
│   ├── build.properties
│   └── plugins.sbt
├── public
│   ├── images
│   │   └── favicon.png
│   ├── javascripts
│   │   └── main.js
│   └── stylesheets
│       └── main.css
└── test
    └── controllers
        └── HomeControllerSpec.scala

11 directories, 14 files

主要なフォルダ・ファイル

上記のフォルダ・ファイルの内、押さえておきたいものだけ説明します。
下記を含めた全体像を見たい場合は https://www.playframework.com/documentation/2.7.x/Anatomy で確認してください。

  • build.sbt: ビルドスクリプト
  • app: アプリケーションのソースフォルダ
  • conf: アプリケーションの設定ファイル用フォルダ
    • application.conf: スレッドプール、データベース、セキュリティなど、アプリケーションの基本的な設定ファイル
    • routes: HTTPメソッド、リクエストパスに応じたルーティング設定ファイル
  • project: sbt 設定ファイル群
    • build.properties: 使用する sbt のバージョンを宣言する指標的なファイル
    • routes: Play 自身の定義を含む sbt プラグイン
  • test: 単体、および機能テスト用のソースフォルダ

初回実行

一度この状態で起動してみます。
sbt runコマンドを実行し、ブラウザでlocalhost:9000にアクセスします。

$ sbt run

(中略)

--- (Running the application, auto-reloading is enabled) ---

[info] p.c.s.AkkaHttpServer - Listening for HTTP on /0:0:0:0:0:0:0:0:9000

(Server started, use Enter to stop and go back to the console...)

image.png

正常にアクセスできました。
Ctrl + C」でsbtを終了します。

この時点で一度コミットしておきます。
なお、コミットメッセージに日本語を使うと表記がずれる可能性がありますが、Gitに関しては今回の主題ではないため取り上げません。

$ git init
Initialized empty Git repository in /home/xxx/play-api-sample/.git/
$ git add .
$ git commit -m "初回インストール"
[master (root-commit) b5f3afb] 初回インストール
 19 files changed, 305 insertions(+)
...

インポート

IntelliJをお使いの方は、以下の手順でプロジェクトをインポートしてください(お使いでない方はこの部分は飛ばしてください)。

① 「Import Project」を選択し、play-api-sampleフォルダを指定
無題.png

② 「Import Project from external model」からsbtを選択してNext
image.png
③ そのままFinish
image.png

バージョンの指定

本記事執筆時点(2019年9月)のScalaの最新メジャーバージョンは2.13.0です。
build.sbtscalaVersionもそうなっていると思いますが、このチュートリアルでは2.12.8で進めていきます(ライブラリ互換性のため)。

(19/12/07 追記) 2.13.1を使うよう変更しました。

それ以外はそのままにしておきます。

build.sbt
name := """play-api-sample"""
organization := "com.example"

version := "1.0-SNAPSHOT"

lazy val root = (project in file(".")).enablePlugins(PlayScala)

scalaVersion := "2.13.1"

libraryDependencies += guice
libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "4.0.3" % Test
また、sbtのバージョンは1.2.8とします。

(19/12/07 追記) 1.3.4に変更しました。

project/build.properties
sbt.version=1.3.4

初期化

インストールした時点でサンプルコードがいくつか存在していますが、それらは今回使わないため削除します。

まず、ルーティングを初期化するためにconf/routesファイルの中身を空にします。

$ truncate -s0 conf/routes

次に、コントローラーのサンプルファイルとテストファイルを削除します。

$ rm ./app/controllers/HomeController.scala \
 ./test/controllers/HomeControllerSpec.scala

合わせて、Web APIには不要なフォルダを削除します。
/app/viewsフォルダはサーバーサイドレンダリングに使用するフォルダで、/publicフォルダは公開アセット(画像ファイル・CSS・JavaScriptなど)を格納するフォルダです。
どちらも今回は使用しないため削除します。

$ rm -r ./app/views ./public

最初の開発

最初のテスト実行

ここからソースを編集していきます。

まず最初に、/helloパスにGETでアクセスできるようにしてみます。
テストファーストの精神にのっとって、先にテストコードから書くことにします。
テストフレームワークにはplayframework/scalatestplus-playを使用しています。1
test/controllers配下にHelloControllerSpec.scalaを作成し、以下のように記述します。

$ touch ./test/controllers/HelloControllerSpec.scala
test/controllers/HelloControllerSpec.scala
package controllers

import org.scalatestplus.play._
import org.scalatestplus.play.guice._
import play.api.test._
import play.api.test.Helpers._

class HelloControllerSpec
    extends PlaySpec
    with GuiceOneAppPerTest
    with Injecting {

  "HelloController GET" should {

    "「/hello」にGETメソッドでアクセスできる" in {
      val request = FakeRequest(GET, "/hello")
      val home = route(app, request).get

      status(home) mustBe OK
    }
  }
}

FakeRequestオブジェクトを使うことで、HTTPメソッドとパスを指定して擬似的なリクエストを作成できます。
routeで、指定したパスの処理を呼びだしています。
statusで、レスポンスのHTTPステータスコードを取得しています。
OKは単なる数値の200を定数にしたもので、HTTPステータスコード200を表しています。
mustBeメソッドで両者が同じものかどうかをテストしています。
(参考: https://www.playframework.com/documentation/2.7.x/ScalaTestingWithScalaTest)

ここではとりあえず200が返ってきさえすれば良しとします。
sbt testでテストを実行してみます。

$ sbt test

(中略)

[info] HelloControllerSpec:
[info] HelloController GET
[info] - should 「/hello」にGETメソッドでアクセスできる *** FAILED ***
[info]   404 was not equal to 200 (HelloControllerSpec.scala:19)
[info] ScalaTest
[info] Run completed in 2 seconds, 85 milliseconds.
[info] Total number of tests run: 1
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 0, failed 1, canceled 0, ignored 0, pending 0
[info] *** 1 TEST FAILED ***

...

テストが失敗しました。
HTTPステータスが200ではなく404(NotFound)だと言われています。
/helloにアクセスできるよう、conf/routesを下記のように編集します。

GET     /hello                controllers.HelloController.hello

左から順に、「HTTPメソッド」、「パス」、「そのパスにそのHTTPメソッドでアクセスした際に呼び出されるScalaのメソッド」です。
パッケージはappフォルダ配下から始まるため、ここではapp/controllers/HelloController.scalaのhelloメソッドが呼び出されます。

app/controllers/HelloController.scalaを作成し、最低限動くように以下の記述をします。

$ touch ./app/controllers/HelloController.scala
app/controllers/HelloController.scala
package controllers

import javax.inject.Inject
import play.api.mvc._

class HelloController @Inject()(cc: ControllerComponents)
    extends BaseControllerHelpers {

  override protected def controllerComponents: ControllerComponents = cc

  def hello(): Action[AnyContent] = {
    val actionBuilder: ActionBuilder[Request, AnyContent] = controllerComponents.actionBuilder
    actionBuilder.apply(new Status(200))
  }
}

Play FrameworkでControllerに指定するクラスは、BaseControllerHelpersトレイトを継承します。
BaseControllerHelpersトレイトを継承したクラスは、controllerComponentsメソッドを実装する必要があります。

Controller.scala
trait BaseControllerHelpers extends ControllerHelpers {

  protected def controllerComponents: ControllerComponents

(以下略)

controllerComponentsメソッドはControllerComponents型の値を返します。
HelloControllerクラスでは、 @Inject()(cc: ControllerComponents)でDIされたオブジェクトをそのまま返しています。2
参考: https://www.playframework.com/documentation/2.7.x/ScalaDependencyInjection

helloメソッドが、実際に/helloにGETでアクセスしたときに呼び出されるメソッドです。

app/controllers/HelloController.scala
  def hello(): Action[AnyContent] = {
    val actionBuilder: ActionBuilder[Request, AnyContent] = controllerComponents.actionBuilder
    actionBuilder.apply(new Status(200))
  }

conf/routesで指定されたメソッドは、 Action型の値を返す必要があります(そうでない場合コンパイルエラーになります)。
Action型の値を作るため、ActionBuilderトレイトのapplyメソッドを使用します。
ここでは、ControllerComponentsトレイトに定義されているactionBuilderメソッド経由でapplyメソッドを呼び出しています。

Controller.scala
trait ControllerComponents {
  def actionBuilder: ActionBuilder[Request, AnyContent]
  ...

controllerComponents.actionBuilderの部分は直接cc.actionBuilderと書くのと同じですが、後で少し加工するためにcontrollerComponentsメソッドを噛ませています。

new Status(200)でHTTPステータスコード200を生成しています。
このStatusクラスはResultクラスを継承しており、ActionBuilder#applyResult型の値からActionを生成します。

この状態で、一度テストを実行してみます。

なお、毎回sbt testと入力するとその度にsbtの起動から始まり時間がかかってしまうので、一度sbtシェルを立ち上げ、今後はこちらからテストを実行することとします。

$ sbt

(中略)

[play-api-sample] $ test

(中略)

[info] HelloControllerSpec:
[info] HelloController GET
[info] - should 「/hello」にGETメソッドでアクセスできる
[info] ScalaTest
[info] Run completed in 2 seconds, 74 milliseconds.
[info] Total number of tests run: 1
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.
...

テストが成功した(HTTPステータスコード200が返ってきた)ことがわかります。

冗長な部分の省略

実は先のHelloController.scalaは、説明用にかなり冗長に書いています。
なので、簡略化出来る部分を簡略化していきます。

継承元の変更

継承元をBaseControllerHelpersトレイトからAbstractControllerクラスに変更します。
AbstractControllerクラスはBaseControllerトレイトを継承しており、BaseControllerトレイトがBaseControllerHelpersトレイトを継承しています。

Controller.scala
abstract class AbstractController(protected val controllerComponents: ControllerComponents) extends BaseController

trait BaseController extends BaseControllerHelpers {
...

AbstractControllerクラスは、抽象メソッドであるcontrollerComponentsメソッドを、controllerComponentsフィールド経由で実装しています。3

controllerComponentsフィールドは、HelloControllerクラスからDI経由で渡されています。
そのため、HelloControllerクラス内でcontrollerComponentsメソッドを実装する必要がなくなります。

Actionメソッドの使用

BaseControllerトレイトには、controllerComponents.actionBuilderと同じ処理を行うActionという名前のメソッドが定義されています。

Controller.scala
trait BaseController extends BaseControllerHelpers {
  def Action: ActionBuilder[Request, AnyContent] = controllerComponents.actionBuilder
}

そのため、helloメソッドは、下記のように書き換えられます。

app/controllers/HelloController.scala
  def hello(): Action[AnyContent] = Action.apply(new Status(200))

ここで、左辺のAction[AnyContent]と右辺のActionが全く別物であることを理解しておいてください。
左は戻り値の型ですが、右は単なるcontrollerComponents.actionBuilderのラッパーメソッドです。

Scalaにおいてapplyメソッドは省略可能なので、合わせてそちらも省略します。

app/controllers/HelloController.scala
  def hello(): Action[AnyContent] = Action(new Status(200))

また、ステータスコード200は既にOkという定数オブジェクトがResults.scalaに定義されているため、そちらを使います。
200だけでなく、主要なステータスコードは全て定義されています。4

app/controllers/HelloController.scala
  def hello(): Action[AnyContent] = Action(Ok)



まとめると、HelloControllerは下記のように書き換えられます。

app/controllers/HelloController.scala
package controllers

import javax.inject.Inject
import play.api.mvc._

class HelloController @Inject()(cc: ControllerComponents) extends AbstractController(cc) {
  def hello(): Action[AnyContent] = Action(Ok)
}

sbtシェルでテストを再度実行し上記のコードでも動くことを確認します。

[play-api-sample] $ test

(中略)

[info] HelloControllerSpec:
[info] HelloController GET
[info] - should 「/hello」にGETメソッドでアクセスできる
[info] ScalaTest
[info] Run completed in 2 seconds, 36 milliseconds.
[info] Total number of tests run: 1
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.
...

問題なく成功しました。
今後はこちらの書き方をベースに進めていきます。

この時点で一度コミットしておきます。
sbtシェルが立ち上がっているので、別のターミナルからコミットします。

$ cd ~/xxx/play-api-sample/
$ git add .
$ git commit -m  "「/hello」にGETメソッドでアクセスできる"
[master 9cfe817] 「/hello」にGETメソッドでアクセスできる
 11 files changed, 29 insertions(+), 110 deletions(-)
...

これで、Play Frameworkを通してWeb APIにアクセスすることが出来るようになりました。

レスポンスの指定

文字列の取得

先程の処理ではHTTPステータスコードだけを指定していましたが、実際にはそれ以外にも何かしらのレスポンス(htmlやjsonなど)が返ってくるのを期待すると思います。
まずは、恒例の"Hello World"文字列を取得できるようにします。
HelloControllerSpecクラスのテストメソッドを、下記のように編集します。

test/controllers/HelloControllerSpec.scala
package controllers

import org.scalatestplus.play._
import org.scalatestplus.play.guice._
import play.api.test.Helpers._
import play.api.test._

class HelloControllerSpec extends PlaySpec with GuiceOneAppPerTest {

  "HelloController GET" should {

    "「/hello」にGETメソッドでアクセスすると「Hello World」が返る"  in {
      val request = FakeRequest(GET, "/hello")
      val home = route(app, request).get

      status(home) mustBe OK
      contentType(home) mustBe Some("text/plain")
      contentAsString(home) mustBe "Hello World"
    }
  }
}

ステータスコードだけでなく、Content-Typeと中身の文字列(レスポンスボディ)もテストするようにしました。
この状態で実行すると、下記のように失敗します。

[play-api-sample] $ test

(中略)

[info] HelloControllerSpec:
[info] HelloController GET
[info] - should 「/hello」にGETメソッドでアクセスすると「Hello World」が返る *** FAILED ***
[info]   None was not equal to Some("text/plain") (HelloControllerSpec.scala:17)
[info] ScalaTest
[info] Run completed in 2 seconds, 41 milliseconds.
[info] Total number of tests run: 1
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 0, failed 1, canceled 0, ignored 0, pending 0
[info] *** 1 TEST FAILED ***
...

中身の文字列以前に、Content-Typeが指定されていないために失敗していることがわかります。
テストが通るよう、HelloControllerクラスを編集します。

app/controllers/HelloController.scala
package controllers

import javax.inject.Inject
import play.api.mvc._

class HelloController @Inject()(cc: ControllerComponents) extends AbstractController(cc) {
  def hello(): Action[AnyContent] = {
    val result: Result = Ok("Hello World")
    Action(result.as("text/plain"))
  }
}

先ほどと違い、Okオブジェクトに"Hello World"という文字列を渡しています。5
さらに、Result#asメソッドによってContent-Typeを指定した上で、Actionを生成しています。

この状態でテストが通るかどうか確認してみます。

[play-api-sample] $ test

(中略)

[info] HelloControllerSpec:
[info] HelloController GET
[info] - should 「/hello」にGETメソッドでアクセスすると「Hello World」が返る
[info] ScalaTest
[info] Run completed in 2 seconds, 22 milliseconds.
[info] Total number of tests run: 1
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.
...

無事通りました。
Content-Typeがtext/plainであること、およびレスポンスボディが"Hello World"だったことが確認できました。

なお、実はレスポンスのContent-Typeは、レスポンスボディとして指定された値から自動的に推論されます。
そのため、as("text/plain")の部分は省略可能となり、helloメソッドは下記のように書いてもテストは通ります。

app/controllers/HelloController.scala
package controllers

import javax.inject.Inject
import play.api.mvc._

class HelloController @Inject()(cc: ControllerComponents) extends AbstractController(cc) {
  def hello(): Action[AnyContent] = Action(Ok("Hello World"))
}
[play-api-sample] $ test

(中略)

[info] HelloControllerSpec:
[info] HelloController GET
[info] - should 「/hello」にGETメソッドでアクセスすると「Hello World」が返る
[info] ScalaTest
[info] Run completed in 2 seconds, 52 milliseconds.
[info] Total number of tests run: 1
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.
...

JSONの取得

昨今のWebサイトはSPA + Web APIで構成されることも増え、その場合Web API側はJSONを返すことが多いかと思います。
/helloにPOSTメソッドでアクセスしたときはJson(今回は固定値)が返ってくるようにしてみます。

HelloControllerSpecに新たなテストを追加します。

test/controllers/HelloControllerSpec.scala
package controllers

import org.scalatestplus.play._
import org.scalatestplus.play.guice._
import play.api.libs.json.Json
import play.api.test.Helpers._
import play.api.test._

class HelloControllerSpec extends PlaySpec with GuiceOneAppPerTest {

  "HelloController GET" should {

    "「/hello」にGETメソッドでアクセスすると「Hello World」が返る"  in {
      val request = FakeRequest(GET, "/hello")
      val home = route(app, request).get

      status(home) mustBe OK
      contentType(home) mustBe Some("text/plain")
      contentAsString(home) mustBe "Hello World"
    }
  }

  "HelloController POST" should {

    "「/hello」にPOSTメソッドでアクセスするとJsonが返る"  in {
      val request = FakeRequest(POST, "/hello")
      val home = route(app, request).get

      status(home) mustBe OK
      contentType(home) mustBe Some("application/json")
      contentAsJson(home) mustBe Json.obj("hello"-> "world", "language" -> "scala")
    }
  }
}

「/hello」にPOSTメソッドでアクセスするとJsonが返る というテストを追加しました。
JSONなのでContent-Typeはapplication/jsonとなるはずです。
返ってくるJSONの中身については今回は固定値とします。

JSONを扱うために、今回はplay.api.libs.jsonのJSON用ライブラリを使います。
Json.objメソッドを使い、JSONに変換可能なオブジェクト(JsValue)を生成します。
また、レスポンスボディにcontentAsJsonメソッドを適用することで、レスポンスボディから同じJSON用オブジェクトを生成します。

テストを実行すると、JSON以前に/helloにPOSTメソッドでアクセスした際のルーティングが指定されてないと言われます。

[play-api-sample] $ test

(中略)

[info] HelloControllerSpec:
[info] HelloController GET
[info] - should 「/hello」にGETメソッドでアクセスすると「Hello World」が返る
[info] HelloController POST
[info] - should 「/hello」にPOSTメソッドでアクセスするとJsonが返る *** FAILED ***
[info]   404 was not equal to 200 (HelloControllerSpec.scala:29)
[info] ScalaTest
[info] Run completed in 2 seconds, 176 milliseconds.
[info] Total number of tests run: 2
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 1, failed 1, canceled 0, ignored 0, pending 0
[info] *** 1 TEST FAILED ***
...

conf/routesに、/helloにPOSTメソッドでアクセスした際のルーティング先を記述します。
ここでは、HelloControllerのhelloJsonメソッドに紐付けることにします。

GET     /hello                           controllers.HelloController.hello
POST    /hello                           controllers.HelloController.helloJson

HelloControllerにhelloJsonメソッドを追加します。

app/controllers/HelloController.scala
package controllers

import javax.inject.Inject
import play.api.libs.json._
import play.api.mvc._

class HelloController @Inject()(cc: ControllerComponents) extends AbstractController(cc) {
  def hello(): Action[AnyContent] = Action(Ok("Hello World"))

  def helloJson(): Action[AnyContent] = {
    val json: JsValue = Json.obj("hello"-> "world", "language" -> "scala")
    Action(Ok(json))
  }
}

テストコードと同様に固定値のJSONを返しています。
Content-Typeの記載はありませんが、レスポンスボディがJsValue型の値の場合は自動的にapplication/jsonが推論されます。

テストを実行してみます。

[play-api-sample] $ test

(中略)

[info] HelloControllerSpec:
[info] HelloController GET
[info] - should 「/hello」にGETメソッドでアクセスすると「Hello World」が返る
[info] HelloController POST
[info] - should 「/hello」にPOSTメソッドでアクセスするとJsonが返る
[info] ScalaTest
[info] Run completed in 2 seconds, 212 milliseconds.
[info] Total number of tests run: 2
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 2, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.
...

無事、JSONを取得することが出来ました。

値がちゃんとJSONになっているかどうか確かめるために、テストではなく実際に動かしてみます。
sbtシェルでrunコマンドを実行します。

[play-api-sample] $ run

--- (Running the application, auto-reloading is enabled) ---

[info] p.c.s.AkkaHttpServer - Listening for HTTP on /0:0:0:0:0:0:0:0:9000

(Server started, use Enter to stop and go back to the console...)

別のターミナルを立ち上げ、HTTPieを使って実験します。6

まず下記のようにURLのみ指定した場合、デフォルトでGETメソッドとしてリクエストされます。

$ http http://localhost:9000/hello
HTTP/1.1 200 OK
Content-Length: 11
Content-Type: text/plain; charset=UTF-8

(中略)

Hello World

sbtシェルでテストした場合と同じく、レスポンスボディにHello Worldが取得できていることがわかります。

次に、POSTメソッドでアクセスしてみます。

$ http POST http://localhost:9000/hello
HTTP/1.1 200 OK
Content-Length: 36
Content-Type: application/json

(中略)

{
    "hello": "world",
    "language": "scala"
}
...

レスポンスボディがJSON形式で返ってきていることが確認できました。

ここまでの成果を、一度コミットしておきます。

$ git add .
$ git commit -m "「/hello」にGETとPOSTでアクセスできる"
[master 3202dc6] 「/hello」にGETとPOSTでアクセスできる
 3 files changed, 26 insertions(+), 4 deletions(-)

最後に

とりあえず今回までで、指定のURLにリクエストを送信し、レスポンスを取得することはできました。
とはいえ、まだ現時点ではパラメータを渡したり、DBに接続してもいないので、実用性は皆無と言えます。
今後はそういった点を一つずつ実装していこうと思います。

また、現時点のソースをGitHubに上げてあります。
こちらも今後の実装に伴い都度更新していく予定です。
https://github.com/ka2kamaboko/play-api-sample.git

最後までお読み頂きありがとうございました。質問や不備についてはコメント欄かTwitter(@ka2_kamaboko)までお願いします。


  1. 最初にPlayFrameworkをインストールした時点で既にbuild.sbtに依存性が追加されています。 

  2. DI自体の説明は やはりあなた方のDependency Injectionはまちがっている。 がわかりやすかったです。 

  3. scalaの(private[this]でない)フィールドは実際はメソッド呼び出しなので、こういった記法が可能です。(参考: Scalaのvalとvarとdef

  4. このOkオブジェクトはHelloControllerSpecに出てきた定数OKとは別物です。後者は単なる数値型で、前者は後者をフィールドに持つStatusクラスの定数オブジェクトです。 

  5. これは実際にはOk.apply("Hello World")の略記法です。 

  6. curlコマンドでも構いません。 

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away