通过Http与twitter服务器通信:
如何构建 Scitter 客户机来处理经过验证和未经过验证的调用。目前,我将采用典型的 Scala 方式,假定验证是 “按对象” 执行的,因此将需要验证的调用放在类定义中,并在未验证的调用放在对象定义中,测试代码(apache common httpclient包):
package com.tedneward.scitter { /** * Object for consuming "non-specific" Twitter feeds, such as the public timeline. * Use this to do non-authenticated requests of Twitter feeds. */ object Scitter { import org.apache.commons.httpclient._, methods._, params._, cookie._ /** * Ping the server to see if it's up and running. * * Twitter docs say: * test * Returns the string "ok" in the requested format with a 200 OK HTTP status code. * URL: http://twitter.com/help/test.format * Formats: xml, json * Method(s): GET */ def test : Boolean = { val client = new HttpClient() val method = new GetMethod("http://twitter.com/help/test.xml") method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler(3, false)) client.executeMethod(method) val statusLine = method.getStatusLine() statusLine.getStatusCode() == 200 } } /** * Class for consuming "authenticated user" Twitter APIs. Each instance is * thus "tied" to a particular authenticated user on Twitter, and will * behave accordingly (according to the Twitter API documentation). */ class Scitter(username : String, password : String) { } }
实现用户验证的api实现:
package com.tedneward.scitter { import org.apache.commons.httpclient._, auth._, methods._, params._ // ... /** * Class for consuming "authenticated user" Twitter APIs. Each instance is * thus "tied" to a particular authenticated user on Twitter, and will * behave accordingly (according to the Twitter API documentation). */ class Scitter(username : String, password : String) { /** * Verify the user credentials against Twitter. * * Twitter docs say: * verify_credentials * Returns an HTTP 200 OK response code and a representation of the * requesting user if authentication was successful; returns a 401 status * code and an error message if not. Use this method to test if supplied * user credentials are valid. * URL: http://twitter.com/account/verify_credentials.format * Formats: xml, json * Method(s): GET */ def verifyCredentials : Boolean = { val client = new HttpClient() val method = new GetMethod("http://twitter.com/help/test.xml") method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler(3, false)) client.getParams().setAuthenticationPreemptive(true) val creds = new UsernamePasswordCredentials(username, password) client.getState().setCredentials( new AuthScope("twitter.com", 80, AuthScope.ANY_REALM), creds) client.executeMethod(method) val statusLine = method.getStatusLine() statusLine.getStatusCode() == 200 } } }
现在可以添加的最简单的 API 是 public_timeline,它收集 Twitter 从所有用户处接收到的最新的 n 更新,并返回它们以便于进行使用。与之前讨论的另外两个
API 不同,public_timeline API 返回一个响应主体(而不是仅依赖于状态码),因此我们需要分解生成的 XML/RSS/ATOM/,然后将它们返回给 Scitter 客户机。
package com.tedneward.scitter.test { class ExplorationTests { // ... @Test def callTwitterPublicTimeline = { val publicFeedURL = "http://twitter.com/statuses/public_timeline.xml" // HttpClient API 101 val client = new HttpClient() val method = new GetMethod(publicFeedURL) method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler(3, false)) client.executeMethod(method) val statusLine = method.getStatusLine() assertEquals(statusLine.getStatusCode(), 200) assertEquals(statusLine.getReasonPhrase(), "OK") val responseBody = method.getResponseBodyAsString() System.out.println("callTwitterPublicTimeline got... ") System.out.println(responseBody) } } }
这样就获得了返回的主体,接下来,我们要实现通过客户端完成twitter的更新
package com.tedneward.scitter { class Scitter { // ... def update(message : String, options : OptionalParam*) : Option[Status] = { def optionsToMap(options : List[OptionalParam]) : Map[String, String]= { options match { case hd :: tl => hd match { case InReplyToStatusId(id) => Map("in_reply_to_status_id" -> id.toString) ++ optionsToMap(tl) case _ => optionsToMap(tl) } case List() => Map() } } val paramsMap = Map("status" -> message) ++ optionsToMap(options.toList) val (statusCode, body) = Scitter.execute("http://twitter.com/statuses/update.xml", paramsMap, username, password) if (statusCode == 200) { Some(Status.fromXml(XML.loadString(body))) } else { None } } } }