PlayFramework(twirl)によるform
PlayFrameworkにはtwirlというテンプレートエンジンが用意されている。
@helper.form(action="/hoge/huga",Symbol("id")->"form1") {
@CSRF.formfield
@helper.inputText(nankanoForm("nanika"),Symbol("id")->"text1",Symbol("placeholder")->"field")
<input type="submit">
}
このようなテンプレートからレンダリングされたhtmlでsubmitすれば入力したデータが
GET/POSTされ用意したnankanoformのcase classなどで受け取ることが出来るが、
クライアント側でajaxリクエストを行うといくつか問題が発生する。
CSRF対策(POST時)
PlayframeworkではCSRFfilterを有効にするとRequestにCSRFTokenが設定されてないものは不正リクエストとして弾く。
上記テンプレートのように@CSRF.formfield
を
@helper.form
のブロック内部に追加することでCSRFfilterの認証を通すことができる。
しかし単純にformの値を取ってきてPOSTをすると、
CSRFTokenが見つからないためにリクエストエラーになってしまう。
PlayFrameworkではFormDataとしてcsrfTokenを含めるという方法を取っている。
上記のテンプレートをレンダリングした結果としてhoge.htmlがクライアント側で表示される。
<form action="hoge/huga" method=POST id="form1">
<input name="csrfToken" value="123...">
<input type="text" name="nanika" id="text1" placeholder="field">
<input type="submit">
</form>
このform内のcsrfTokenの値を取得してPOSTする。
const params = new URLSearchParams()
const csrfToken= document.getElementsByName("csrfToken")[0].value
const text = document.getElementById("text1").value
params.append("csrfToken",csrfToken)
params.append("text1",text)
axios.post("/hoge/huga",params)
一応こういう感じにcsrfToken
に対応させる形で送ると普通にsubmitしたのと同じようにリクエストが通る(もっといい書き方がある気もする)。
上記の方法を取らなくてもapplication.confにsession.sameSite = "lax"
と設定してCSRFfilterをfalseにしたり、
routesを以下のようにしてもいいと思う。
+nocsrf
POST /hoge/huga
他にもplay.filters.csrf.header.bypassHeadersに色々設定して認証を回避したりもできる。
https://www.playframework.com/documentation/2.8.x/ScalaCsrf
listのPOST
formの定義時にlist(number)という風にするとList[Int]みたいな感じで受け取れる。
例えばboxes -> list(number)
みたいな感じにするとhtmlではname=boxes[]
でinput要素が生成される。
これをPOSTする際には下記のようにする必要がある。
Array.from(document.getElementsByName("boxes[]"))
.filter(e => e.checked)
.map(e => e.value)
.forEach(v => params.append("boxes[]",v))
配列としてappendするのではなく、各要素の値をappendしなければformのcase classへの変換が上手くいかないので注意。