質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.92%

Scala(Play Framework)でJSONパラメータをつけたPOSTリクエストを送る

受付中

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 164

Nawta

score 4

https://hexx.github.io/scala_text/introduction-of-web-application-4th-day.html

このサイトを真似して、ScalaでWebアプリケーションを試しに動かそうとしているのですが、GETリクエストを送って表示まではできるもののPOSTリクエストからDBへの登録がどうもできません。
現在送っているリクエストは以下のようなものです。何個か試しましたがどうもうまく行きません。エラーが表示されないのと、ログの残し方も試してみてうまくいかなかった(理解が足りてなくてうまく実装できてないかも?)ので途方に暮れています。どこかおかしな点はありますでしょうか。

curl -X POST -H "Content-Type: application/json" -d '{"post": "have a good night!"}' localhost:9000/posts  
curl -X POST -d 'post:"have a good night!"' localhost:9000/posts                                                                                                
curl -X POST -d '{post:"have a good night!"}' localhost:9000/posts                                                                                              
curl -X POST -d '{post:["have a good night!"]}' localhost:9000/posts                                                                                            
curl -d '{post:["have a good night!"]}' localhost:9000/posts                                                                                                    
curl -d '{post:["have a good night!"]}' localhost:9000/posts                                                                                                    
curl -d '{post:["have a good night!"]}' localhost:9000/posts                                                                                                  

上記のようなリクエストを送ると、

GET    /posts   controllers.PostsController.get()
POST   /posts   controllers.PostsController.post()

から

package controllers

import java.time.OffsetDateTime
import javax.inject.Inject
import play.api.mvc.Action
import play.api.mvc.Controller
import play.api.data.Form
import play.api.data.Forms._
import play.api.i18n.I18nSupport
import play.api.i18n.Messages
import play.api.i18n.MessagesApi
import play.api.libs.json.Json

//case class PostRequest(body: String)
//これを定義していると、エラーが出る。多分、postの中で引数として用いているから二重宣言になる?

class PostsController @Inject()(val messagesApi: MessagesApi) extends Controller with I18nSupport {

  private[this] val form = Form(
    mapping(
      "post" -> text(minLength = 1, maxLength = 10)
    )(PostRequest.apply)(PostRequest.unapply))

  def get = Action { implicit request =>
    Ok(
      Json.toJson(
        Response(Meta(200), Some(Json.obj("posts" -> Json.toJson(PostRepository.findAll))))))
  }

  def post = Action { implicit request =>
    form.bindFromRequest.fold(
      error => {
        val errorMessage = Messages(error.errors("post")(0).message)
        BadRequest(Json.toJson(Response(Meta(400, Some(errorMessage)))))
      },
      postRequest => {
        val post = Post(postRequest.body, OffsetDateTime.now)
        PostRepository.add(post)
        Ok(Json.toJson(Response(Meta(200))))
      }
    )
  }
}

のコントローラーに飛び、postメソッドを通じてDBにデータが追加されるようにします。

PostRepositoryの中身は以下のようなものです。

package controllers

import scalikejdbc._
import scalikejdbc.jsr310._

object PostRepository {

  def findAll: Seq[Post] = DB readOnly { implicit session =>
    sql"SELECT id, body, date FROM posts"
      .map { rs =>
        Post(rs.long("id"), rs.string("body"), rs.offsetDateTime("date"))
      }
      .list()
      .apply()
  }

  def add(post: Post): Unit = DB localTx { implicit session =>
    sql"INSERT INTO posts (body, date) VALUES (${post.body}, ${post.date})".update().apply()
  }
}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

+1

POSTしたリクエストのボディが期待したものと違うのではないでしょうか。
(実装自体は問題ないとおもわれます。)

curl -X POST -v http://localhost:9000/posts \
  -H "content-type: application/json" \
  -d '{"post": "anything"}'


もしくは

curl -X POST -v http://localhost:9000/posts \
  -H "content-type: application/x-www-form-urlencoded" \
  -d 'post=anything'


とすれば 200 で登録可能となりそうです。
(リクエスト例は post の文字列をダブルクォートで囲っておらず、
かつ contents-type 指定がないので curl に json と認識されてないと思われます。)

PostController の以下の部分が key: post の value を受け取るようになっているので、
その組み合わせで解釈できるフォーマットでリクエストを送るとよさそうです。

  private[this] val form = Form(
    mapping(
      "post" -> text(minLength = 1, maxLength = 10)
    )(PostRequest.apply)(PostRequest.unapply))

捕捉: 

これを定義していると、エラーが出る。

これはたぶん以前のサンプルコードの TextboardController 内でも
同じクラス PostRequest を定義しているからではないでしょうか
form.bindFromRequest.fold の中は match 式で case class をマッチさせているので、
どこかで事前に定義が必要になります。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.92%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る