内容简介:JSON with HTTP
Play通过HTTP API与JSON库共同支持内容类型为JSON的HTTP请求和应答。
关于 Controllers, Actions, 和routing的详细资料看 HTTP Programming
我们将通过设计一个简单的RESTful Web服务来解释必要的概念,这个服务可以GET 到实体集合,并接收POSTs 创建新的实体。并且这个服务将对所有的数据使用JSON类型。
这是我们在我们的服务中将使用的模型:
case class Location(lat: Double, long: Double)
case class Place(name: String, location: Location)
object Place {
var list: List[Place] = {
List(
Place(
"Sandleford",
Location(51.377797, -1.318965)
),
Place(
"Watership Down",
Location(51.235685, -1.309197)
)
)
}
def save(place: Place) = {
list = list ::: List(place)
}
}
以Json形式提供实体列表
我们将以给我们的Controller添加必须的导入开始:
import play.api.mvc._
import play.api.libs.json._
import play.api.libs.functional.syntax._
object Application extends Controller {
}
在我们写我们的Action之前,我们将需要探究一下从我们的模型转换为 JsValue
形式。这是通过定义隐式 Writes[Place]
实现.
implicit val locationWrites: Writes[Location] = ( (JsPath \ "lat").write[Double] and (JsPath \ "long").write[Double] )(unlift(Location.unapply)) implicit val placeWrites: Writes[Place] = ( (JsPath \ "name").write[String] and (JsPath \ "location").write[Location] )(unlift(Place.unapply))
接下来我们写我们的Action
def listPlaces = Action {
val json = Json.toJson(Place.list)
Ok(json)
}
Action 取到Place 对象列表,使用Json.toJson和我们的隐式 Writes[Place]
把他们转换成JsValue ,然后再把它作为结果Body返回。Play将会认出JSON结果并相应的为应答设置 Content-Type头和Body值。最后一步是把我们的Action 路由添加到 conf/routes
:
GET /places controllers.Application.listPlaces
我们可以使用浏览器或HTTP工具生成一个请求测试Action,这个例子使用了Unix的命令行工具 cURL.
curl --include http://localhost:9000/places
应答:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 141
[{"name":"Sandleford","location":{"lat":51.377797,"long":-1.318965}},{"name":"Watership Down","location":{"lat":51.235685,"long":-1.309197}}]
通过JSON创建一个新的实体引用
对于这个Action ,我们将需要定义一个隐式的 Reads[Place]
来把 JsValue
转换成我们的模型。
implicit val locationReads: Reads[Location] = ( (JsPath \ "lat").read[Double] and (JsPath \ "long").read[Double] )(Location.apply _) implicit val placeReads: Reads[Place] = ( (JsPath \ "name").read[String] and (JsPath \ "location").read[Location] )(Place.apply _)
这个Action 比我们的列表例子复杂些,一些事情需要注意:
-
这个Action 要求有一个Content-Type头为
text/json或application/json,body 包含需要创建的实体的JSON形式。 -
它使用JSON指定的BodyParser ,它将 解析请求 并提供做为
JsValue的request.body -
我们使用依赖我们的隐式
Reads[Place]的validate 方法进行转换。 - 为了处理验证结果,我们使用有错误和成功流的fold。这个模式也类似于使用 表单提交.
- Action 也发送JSON应答
Body 解析器可以用Case类,隐式的Reads 或接受函数的方式被输入。因此我们可以省掉更多的工作,让Play自动把Json解析成Case类并在调用我们的Action之前进行[验证]。( https://www.playframework.com/documentation/2.5.x/ScalaJsonCombinators#Validation-with-Reads )
import play.api.libs.json._
import play.api.libs.json.Reads._
import play.api.libs.functional.syntax._
implicit val locationReads: Reads[Location] = (
(JsPath \ "lat").read[Double](min(-90.0) keepAnd max(90.0)) and
(JsPath \ "long").read[Double](min(-180.0) keepAnd max(180.0))
)(Location.apply _)
implicit val placeReads: Reads[Place] = (
(JsPath \ "name").read[String](minLength[String](2)) and
(JsPath \ "location").read[Location]
)(Place.apply _)
// This helper parses and validates JSON using the implicit `placeReads`
// above, returning errors if the parsed json fails validation.
def validateJson[A : Reads] = BodyParsers.parse.json.validate(
_.validate[A].asEither.left.map(e => BadRequest(JsError.toJson(e)))
)
// if we don't care about validation we could replace `validateJson[Place]`
// with `BodyParsers.parse.json[Place]` to get an unvalidated case class
// in `request.body` instead.
def savePlaceConcise = Action(validateJson[Place]) { request =>
// `request.body` contains a fully validated `Place` instance.
val place = request.body
Place.save(place)
Ok(Json.obj("status" ->"OK", "message" -> ("Place '"+place.name+"' saved.") ))
}
最后,我们将在 conf/routes
文件里添加一个路由绑定:
POST /places controllers.Application.savePlace
我们将使用有效的和无效的请求测试Action,以验证我们成功和错误流。 使用有效的数据测试Action:
curl --include
--request POST
--header "Content-type: application/json"
--data '{"name":"Nuthanger Farm","location":{"lat" : 51.244031,"long" : -1.263224}}'
http://localhost:9000/places
应答:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 57
{"status":"OK","message":"Place 'Nuthanger Farm' saved."}
使用没有“name” 字段的无效数据测试Action:
curl --include
--request POST
--header "Content-type: application/json"
--data '{"location":{"lat" : 51.244031,"long" : -1.263224}}'
http://localhost:9000/places
应答:
HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=utf-8
Content-Length: 79
{"status":"KO","message":{"obj.name":[{"msg":"error.path.missing","args":[]}]}}
使用错误数据类型“lat”的无效数据测试 Action
:
curl --include
--request POST
--header "Content-type: application/json"
--data '{"name":"Nuthanger Farm","location":{"lat" : "xxx","long" : -1.263224}}'
http://localhost:9000/places
应答:
HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=utf-8
Content-Length: 92
{"status":"KO","message":{"obj.location.lat":[{"msg":"error.expected.jsnumber","args":[]}]}}
总结
Play是被设计来支持REST使用JSON并且开发这些服务应该是简单的。大部分的工作是给你的模型写 Reads
和 Writes
,这将会在下一节中详细介绍。
以上所述就是小编给大家介绍的《JSON with HTTP》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Game Engine Architecture, Second Edition
Jason Gregory / A K Peters/CRC Press / 2014-8-15 / USD 69.95
A 2010 CHOICE outstanding academic title, this updated book covers the theory and practice of game engine software development. It explains practical concepts and techniques used by real game studios,......一起来看看 《Game Engine Architecture, Second Edition》 这本书的介绍吧!