文章目錄
  1. 1. 環境設定
  2. 2. 先定義一個case class
  3. 3. 然後定義一個Form
    1. 3.1. apply 和unapply 的作用
  4. 4. GET Request
    1. 4.1. Test with Curl
  5. 5. POST Request
    1. 5.1. Test with curl
  6. 6. Nested field
  7. 7. Reference

環境設定

  • PlayFramework: v2.4.6
  • scala : 2.11.6

先import 以下

1
2
3
import play.api.data._
import play.api.data.Forms._
import play.api.data.format.Formats._

先定義一個case class

case class 的作用是用來encapsulate 提交的資料為一個object, 正式名稱為Algebraic data type , 將data 定義為不同結溝和不同value,以下例子為Haskell

  • data Bool = False | True
  • data Maybe a = Nothing | Just a

an algebraic data type is a kind of composite type, i.e. a type formed by combining other types

回到play中,加入case class User(name: String, password: String)
這定義User 有兩個String field, 當中有當中有name 和password,是否很像c中的struct, 但scala 中的Algebraic data type不只case class, Option也是其中一種

然後定義一個Form

定義一個form 實現如何把form的parameter blind to the case class

1
2
3
4
5
6
val userForm = Form(
mapping(
"name" -> of[String],
"password" -> of[String]
)(User.apply)(User.unapply)

)

  • 表格中的”name” 為String
  • 表格中的”password” 為String

apply 和unapply 的作用

1
2
3
4
5
6
7
8
scala> User("jason", "password")
res6: User = User(jason,password)

scala> :t User.unapply _
User => Option[(String, String)]

scala> User.unapply(User("jason", "password"))
res8: Option[(String, String)] = Some((jason,password))

User.apply 是用作創建新instance of the User
User.unapply 是用作extract 資料,用作patten matching (match & case)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// when u is a User
// u is User(jason,password)
// User(name, password) actually calls `User.unapply(u)` which extract the data inside with return value: Some(name, password)
// therefore unapply method is called extractor pattern
u match {
case User(name, password) => println(s"u is consist of $name and $password")
case _ => println("not matched")
}

// when o is Option
// o: Option[(String, String)] = Some((jason,password))
o match {
case Some((name, password)) => println(s"u is consist of $name and $password")
case None => println("no user is found ")
}

GET Request

1
2
3
4
5
def login = Action(parse.form(userForm)) { request =>
Logger.debug("User logining")
val user = request.body;
Ok("Got: " + s"User name: ${user.name}, User password: ${user.password}");
}

request.body 裡就是User了
Result (content type: text/plain)
Got: request.body.class: class controllers.UserController$User User name: jason, User password: text

Test with Curl

1
2
curl --data http://localhost:9000/api/user/login2 \
name=jason&password=password

POST Request

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def login2 = Action(parse.urlFormEncoded) { implicit request =>
/* Approach 1
val body : Map[String, Seq[String]]= request.body;
val mapping : Map[String, String] = body.mapValues(_.mkString)

val user = userForm.bind(mapping).get;
*/


/* Approach 2
val user = userForm.bindFromRequest().get;
*/


/* Approach 3*/
val user = userForm.bindFromRequest(request.body).get

// var user = userForm..get;
Ok(s"Got User name: ${user.name}, User password: ${user.password}");
}

比較麻煩 需要把mapping 轉做form然後再map做base class

Test with curl

1
2
3
curl -H "Content-Type:application/x-www-form-urlencoded" \
-d 'name=jason&password=password' \
http://localhost:9000/api/user/login2

Nested field

可以定義一個field 作為list or seq
但需要跟隨一定規則定義html name field

例如在文件中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
val contactForm: Form[Contact] = Form(

// Defines a mapping that will handle Contact values
mapping(
"firstname" -> nonEmptyText,
"lastname" -> nonEmptyText,
"company" -> optional(text),

// Defines a repeated mapping
"informations" -> seq(
mapping(
"label" -> nonEmptyText,
"email" -> optional(email),
"phones" -> list(
text verifying pattern("""[0-9.+]+""".r, error="A valid phone number is required")
)
),
"mynickname" -> mapping(
"primaryscl" -> text,
"secondaryscl" -> text
)
(ContactInformation.apply)(ContactInformation.unapply)
)
)(Contact.apply)(Contact.unapply)
)

phones 在html 的name 為informations[0].phones

1
2
3
<input type="text" name="informations[0].phones">
(省略........)
<input type="text" name="informations[1].phones">

primaryscl 在html 的name 為mynickname.primaryscl

1
2
<input type="text" name="mynickname.primaryscl">
<input type="text" name="mynickname.secondaryscl">

Reference

文章評論

文章目錄
  1. 1. 環境設定
  2. 2. 先定義一個case class
  3. 3. 然後定義一個Form
    1. 3.1. apply 和unapply 的作用
  4. 4. GET Request
    1. 4.1. Test with Curl
  5. 5. POST Request
    1. 5.1. Test with curl
  6. 6. Nested field
  7. 7. Reference