简介
现在我们在很多地方使用REST API。很多时候,这需要软件开发工具包的特定语言的实现。如果你打算写一个SDK,或者,你需要调用没有SDK的可用性REST后台,你需要一个框架来发送Http请求。而在Scala中,它原生的使用Future来支持异步处理。
通过使用Future,可以简化你的工作:
- 应用不在阻塞
- 应用可以处理更多的并行请求
- 不再需要一个复杂的线程模型
在Scala中,Dispatch是一个异步http包,让我们使用Future完成一些例子。
普通的http请求
import dispatch._, Defaults._
import scala.util.{Success, Failure}
object DispatchTest {
def main (args: Array[String]) {
val svc = url("http://www.wikipedia.org/");
val response : Future[String] = Http(svc OK as.String)
response onComplete {
case Success(content) => {
println("Successful response" + content)
}
case Failure(t) => {
println("An error has occurred: " + t.getMessage)
}
}
}
}
重定向http请求
这是常见的http端点使用重定向。默认情况下Dispatch不遵循这些重定向,你需要配置HTTP,例如通过使用 Http.configure(_ setFollowRedirects true)(svc OK as.String)。
下面是一个包含重定向的例子:
import dispatch._, Defaults._
import scala.util.{Success, Failure}
object DispatchTest {
def main (args: Array[String]) {
val svc = url("http://www.wikipedia.com");
val response : Future[String] = Http.configure(_ setFollowRedirects true)(svc OK as.String)
response onComplete {
case Success(content) => {
println("Successful response" + content)
}
case Failure(t) => {
println("An error has occured: " + t.getMessage)
}
}
}
}
用户验证的http请求
如果你的http请求中需要基本的用户验证,使用 .as_!()来处理:
val svc = url("http://www.wikipedia.com").as_!("user", "password")
解析Json格式的Response
如今几乎所有的REST端点使用JSON响应。Argonaut 是一个处理http的工具包,它使用许多scala的特性,并与scala结合的非常好。
为了快速解析或者预定义的数据结构不可用时,可以只解析具体的字段:
import scalaz._, Scalaz._
import argonaut._, Argonaut._
val json = """
{ "name" : "Toddler", "age" : 2, "greeting": "gurgle!" }
"""
// extract a simple field "greeting"
val greeting1: String =
Parse.parseWith(jsonString, _.field("greeting").flatMap(_.string).getOrElse(null), msg => msg)
如果你要应对不断变化的数据结构时,这种处理方式很不错。通常REST API都会定义一个固定的数据结构,既可以用于解析。假设你会得到如下用户数据:
{
"dn" :"uid=chris,ou=Users,dc=lollyrock,dc=com",
"controls" :[],
"cn" :"Chris Rock",
"givenName" :"Chris",
"l" :"Berlin",
"mail" :"chris@lollyrock.com",
"uid" : "chris" ,
"displayName" :"ch.hartmann",
"o" :"Rock Inc."
}
首先我们需要一个结构来存储解析后的结果,在scala中使用case class非常合适:
case class User(
dn : String,
cn : String,
givenName : String,
l : String,
mail : String,
displayName : String,
o : String
)
下面的例子演示了JSON数据解析到一个预定义的样例类:
import scalaz._, Scalaz._
import argonaut._, Argonaut._
object UserParser {
// use the implicit json conversion of Argonaut
// more information at http://argonaut.io/doc/parsing/
implicit def UserCodecJson: CodecJson[User] =
// the 9 represents the amount of arguments
casecodec9(User.apply, User.unapply)("dn", "cn", "givenName","l", "mail", "uid", "displayName", "o", "plan")
// method to use argonaut parse
def parse(data: String) : Option[User] = {
Parse.decodeOption[User](data)
}
// simple app to test JSON parsing
def main(args: Array[String]) {
// json input data
var jsonString =
"""
| {"dn":"uid=chris,ou=Users,dc=lollyrock,dc=com","controls":[],"cn":"Chris Rock","givenName":"Chris","l":"Berlin","mail":"chris@lollyrock.com","uid": "chris" ,"displayName":"ch.hartmann","o":"Rock Inc."}
""".stripMargin
// parse json content
val userdata: Option[User] = parse(jsonString)
// print specific values
val usr = userdata.get
println (usr.dn)
println (usr.displayName)
}
}
Dispatch与Argonout的组合提供了一种有效的方法来处理HTTP调用和Response。由于我们使用的是Future,相对一个复杂的线程系统,可以在同一时间处理更多的请求,同时代码更易于阅读。
参考
Asynchronous HTTP requests with Scala and Dispatch - Christoph Hartmann