国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

從Java走進Scala:Twitter API與Scala的交互

開發(fā) 后端
Scitter 客戶機庫即將發(fā)布,但是還差最后一步。在這一期 面向 Java 開發(fā)人員的 Scala 指南 中,Ted Neward 展示如何將更新、顯示和刪除功能添加到用于訪問 Twitter 的基于 Scala 的庫中。

本文是IBMDW上Ted Neward的Scala教學(xué)系列,本文是第16篇,標題為《用 Scitter 更新 Twitter》。

51CTO編輯推薦:Scala編程語言專題

在撰寫本文時,夏季即將結(jié)束,新的學(xué)年就要開始,Twitter 的服務(wù)器上不斷涌現(xiàn)出世界各地的網(wǎng)蟲和非網(wǎng)蟲們發(fā)布的更新。對于我們很多身在北美的人來說,從海灘聚會到足球,從室外娛樂到室內(nèi)項目,各種各樣的想法紛至沓來。為了跟上這種形勢,是時候重訪 Scitter 這個用于訪問 Twitter 的 Scala 客戶機庫了。

如果 到目前為止 您一直緊隨 Scitter 的開發(fā),就會知道,這個庫現(xiàn)在能夠利用各種不同的 Twitter API 查看用戶的好友、追隨者和時間線,以及其他內(nèi)容。但是,這個庫還不具備發(fā)布狀態(tài)更新的能力。在這最后一篇關(guān)于 Scitter 的文章中,我們將豐富這個庫的功能,增加一些有趣的內(nèi)容(終止和評價)功能和重要方法 update()、show() 和 destroy()。在此過程中,您將了解更多關(guān)于 Twitter API 的知識,它與 Scala 之間的交互如何,您還將了解如何克服兩者之間不可避免的編程挑戰(zhàn)。

注意,當您看到本文的時候,Scitter 庫將位于一個 公共源代碼控制庫 中。當然,我還將在本文中包括 源代碼,但是要知道,源代碼庫可能發(fā)生改變。換句話說,項目庫中的代碼與您在這里看到的代碼可能略有不同,或者有較大的不同。

POST 到 Twitter

到目前為止,我們的 Scitter 開發(fā)主要集中于一些基于 HTTP GET 的操作,這主要是因為這些調(diào)用非常容易,而我想輕松切入 Twitter API。將 POSTDELETE 操作添加到庫中對于可見性來說邁出了重要一步。到目前為止,可以在個人 Twitter 帳戶上運行單元測試,而其他人并不知道您要干什么。但是,一旦開始發(fā)送更新消息,那么全世界都將知道您要運行 Scitter 單元測試。

如果繼續(xù)測試 Scitter,那么需要在 Twitter 上創(chuàng)建自己的 “測試” 帳戶。(也許用 Twitter API 編程的最大缺點是沒有任何合適的測試或模擬工具。)

目前的進展

在開始著手這個庫的新的 UPDATE 功能之前,我們來回顧一下到目前為止我們已經(jīng)創(chuàng)建的東西。(我不會提供完整的源代碼清單,因為 Scitter 已經(jīng)開始變得過長,不便于全部顯示。但是,可以在閱讀本文時,從另一個窗口查看 代碼。)

大致來說,Scitter 庫分為 4 個部分:

  • 來回發(fā)送的請求和響應(yīng)類型(UserStatus 等),包含在 API 中;它們被建模為 case 類。
  • OptionalParam 類型,同樣在 API 中的某些地方;也被建模為 case 類,這些 case 類繼承基本的 OptionalParam 類型。
  • Scitter 對象,用于通信基礎(chǔ)和對 Twitter 的匿名(無身份驗證)訪問。
  • Scitter 類,存放一個用戶名和密碼,用于訪問給定 Twitter 帳戶時進行驗證。

注意,在這最后一篇文章中,為了使文件大小保持在相對合理的范圍內(nèi),我將請求/響應(yīng)類型分開放到不同的文件中。

終止和評價

那么,現(xiàn)在我們清楚了目標。我們將通過實現(xiàn)兩個 “只讀” Twitter API 來達到目標:end_session API(結(jié)束用戶會話)和 rate_limit_status API(描述在某一特定時段內(nèi)用戶帳戶還剩下多少可用的 post)。

end_session API 與它的同胞 verify_credentials 相似,也是一個非常簡單的 API:只需用一個經(jīng)過驗證的請求調(diào)用它,它將 “結(jié)束” 當前正在運行的會話。在 Scitter 類上實現(xiàn)它非常容易,如清單 1 所示:

清單 1. 在 Scitter 上實現(xiàn) end_session

				
package com.tedneward.scitter

{

  import org.apache.commons.httpclient._, auth._, methods._, params._

  import scala.xml._



  // ...

  class Scitter

  {

    /**

     *

     */

    def endSession : Boolean =

    {

      val (statusCode, statusBody) =

        Scitter.execute("http://twitter.com/account/end_session.xml",

          username, password)



      statusCode == 200

    }

  }

}

好吧,我失言了。也不是那么容易。

POST

和我們到目前為止用過的 Twitter API 中的其他 API 不一樣,end_session 要求傳入的消息是用 HTTP POST 語義發(fā)送的。現(xiàn)在,Scitter.execute 方法做任何事情都是通過 GET,這意味著需要將那些期望 GET 的 API 與那些期望 POST 的 API 區(qū)分開來。

現(xiàn)在暫不考慮這一點,另外還有一個明顯的變化:POST 的 API 調(diào)用還需將名稱/值對傳遞到 execute() 方法中。(記住,在其他 API 調(diào)用中,若使用 GET,則所有參數(shù)可以作為查詢參數(shù)出現(xiàn)在 URL 行;若使用 POST,則參數(shù)出現(xiàn)在 HTTP 請求的主體中。)在 Scala 中,每當提到名稱/值對,自然會想到 Scala Map 類型,所以在考慮建模作為 POST 一部分發(fā)送的數(shù)據(jù)元素時,最容易的方法是將它們放入到一個 Map[String,String] 中并傳遞。

例如,如果將一個新的狀態(tài)消息傳遞給 Twitter,需要將這個不超過 140 個字符的消息放在一個名稱/值對 status 中,那么應(yīng)該如清單 2 所示:

清單 2. 基本 map 語法

				
val map = Map("status" -> message)

在此情況下,我們可以重構(gòu) Scitter.execute() 方法,使之用 一個 Map 作為參數(shù)。如果 Map 為空,那么可以認為應(yīng)該使用 GET 而不是 POST,如清單 3 所示:

清單 3. 重構(gòu) execute()

				
  private[scitter] def execute(url : String) : (Int, String) =

      execute(url, Map(), "", "")

    private[scitter] def execute(url : String, username : String,

	                             password : String) : (Int, String) =

      execute(url, Map(), username, password)

    private[scitter] def execute(url : String,

	                             dataMap : Map[String,String]) : (Int, String) =

      execute(url, dataMap, "", "")

    private[scitter] def execute(url : String, dataMap : Map[String,String],

                                 username : String, password : String) =

    {

      val client = new HttpClient()

      val method = 

        if (dataMap.size == 0)

        {

          new GetMethod(url)

        }

        else

        {

          var m = new PostMethod(url)


          val array = new Array[NameValuePair](dataMap.size)

          var pos = 0

          dataMap.elements.foreach { (pr) =>

            pr match {

              case (k, v) => array(pos) = new NameValuePair(k, v)

            }

            pos += 1

          }

          m.setRequestBody(array)

          

          m

        }


      method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, 

        new DefaultHttpMethodRetryHandler(3, false))

        

      if ((username != "") && (password != ""))

      {

        client.getParams().setAuthenticationPreemptive(true)

        client.getState().setCredentials(

          new AuthScope("twitter.com", 80, AuthScope.ANY_REALM),

            new UsernamePasswordCredentials(username, password))

      }

      

      client.executeMethod(method)

      

      (method.getStatusLine().getStatusCode(), method.getResponseBodyAsString())

    }

execute() 方法最大的變化是引入了 Map[String,String] 參數(shù),以及與它的大小有關(guān)的 “if” 測試。該測試決定是處理 GET 請求還是 POST 請求。由于 Apache Commons HttpClient 要求 POST 請求的主體放在 NameValuePairs 中,因此我們使用 foreach() 調(diào)用遍歷 map 的元素。我們以二元組 pr 的形式傳入 map 的鍵和值,并將它們分別提取到本地綁定變量 kv,然后使用這些值作為 NameValuePair 構(gòu)造函數(shù)的構(gòu)造函數(shù)參數(shù)。

我們還可以使用 PostMethod 上的 setParameter(name, value) API 更輕松地做這些事情。出于教學(xué)的目的,我選擇了清單 3 中的方法:以表明 Scala 數(shù)組和 Java 數(shù)組一樣,仍然是可變的,即使數(shù)組引用被標記為 val 仍是如此。記住,在實際代碼中,對于每個 (k,v) 元組,使用 PostMethod 上的 setParameter(name, value) 方法要好得多。

還需注意,對于 if/else 返回的 “method” 對象的類型,Scala 編譯器會進行 does the right thing 類型推斷。由于 Scala 可以看到 if/else 返回的是 GetMethod 還是 PostMethod 對象,它會選擇最接近的基本類型 HttpMethodBase 作為 “method” 的返回類型。這也意味著,在 execute() 方法的其余部分中,HttpMethodBase 中的任何不可用方法都是不可訪問的。幸運的是,我們不需要它們,所以至少現(xiàn)在沒有問題。

清單 3 中的實現(xiàn)的背后還潛藏著最后一個問題,這個問題是由這樣一個事實引起的:我選擇了使用 Map 來區(qū)分 execute() 方法是處理 GET 操作,還是處理 POST 操作。如果還需要使用其他 HTTP 動作(例如 PUTDELETE),那么將不得不再次重構(gòu) execute()。到目前為止,還沒有這樣的問題,但是今后要記住這一點。

測試

在實施這樣的重構(gòu)之前,先運行 ant test,以確保原有的所有基于 GET 的請求 API 仍可使用 — 事實確實如此。(這里假設(shè)生產(chǎn) Twitter API 或 Twitter 服務(wù)器的可用性沒有變化)。一切正常(至少在我的計算機上是這樣),所以實現(xiàn)新的 execute() 方法就非常容易:

清單 4. Scitter v0.3: endSession

				
  def endSession : Boolean =

    {

      val (statusCode, statusBody) =

        Scitter.execute("http://twitter.com/account/end_session.xml",

          Map("" -> ""), username, password)


      statusCode == 200

    }

這實在是再簡單不過了。

接下來要做的是實現(xiàn) rate_limit_status API,它有兩個版本,一個是經(jīng)過驗證的版本,另一個是沒有經(jīng)過驗證的版本。我們將該方法實現(xiàn)為 Scitter 對象和 Scitter 類上的 rateLimitStatus,如清單 5 所示:

清單 5. Scitter v0.3: rateLimitStatus

				
package com.tedneward.scitter

{

  object Scitter

  {

    // ...

	

    def rateLimitStatus : Option[RateLimits] =

    {

      val url = "http://twitter.com/account/rate_limit_status.xml"

      val (statusCode, statusBody) =

        Scitter.execute(url)

      if (statusCode == 200)

      {

        Some(RateLimits.fromXml(XML.loadString(statusBody)))

      }

      else

      {

        None

      }

    }

  }

  

  class Scitter

  {

    // ...

	

    def rateLimitStatus : Option[RateLimits] =

    {

      val url = "http://twitter.com/account/rate_limit_status.xml"

      val (statusCode, statusBody) =

        Scitter.execute(url, username, password)

      if (statusCode == 200)

      {

        Some(RateLimits.fromXml(XML.loadString(statusBody)))

      }

      else

      {

        None

      }

    }

  }

}

我覺得還是很簡單。

更新

現(xiàn)在,有了新的 POST 版本的 HTTP 通信層,我們可以來處理 Twitter API 的中心:update 調(diào)用。毫不奇怪,需要一個 POST,并且至少有一個參數(shù),即 status

status 參數(shù)包含要發(fā)布到認證用戶的 Twitter 提要的不超過 140 個字符的消息。另外還有一個可選參數(shù):in_reply_to_status_id,該參數(shù)提供另一個更新的 id,執(zhí)行了 POST 的更新將回復(fù)該更新。

update 調(diào)用差不多就是這樣了,如清單 6 所示:

清單 6. Scitter v0.3: update

				
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

      }

    }

  }

}

也許這個方法中最 “不同” 的部分就是其中定義的嵌套函數(shù) — 與使用 GET 的其他 Twitter API 調(diào)用不同,Twitter 期望傳給 POST 的參數(shù)出現(xiàn)在執(zhí)行 POST 的主體中,這意味著在調(diào)用 Scitter.execute() 之前需要將它們轉(zhuǎn)換成 Map 條目。但是,默認的 Map(來自 scala.collections.immutable)是不可變的,這意味著可以組合 Map,但是不能將條目添加到已有的 Map 中。

解決這個小難題的最容易的方法是遞歸地處理傳入的 OptionalParam 元素的列表(實際上是一個 Array[])。我們將每個元素拆開,將它轉(zhuǎn)換成各自的 Map 條目。然后,將一個新的 Map(由新創(chuàng)建的 Map 和從遞歸調(diào)用返回的 Map 組成)返回到 optionsToMap

然后,將 OptionalParamArray[] 傳遞到 optionsToMap 嵌套函數(shù)。然后,將返回的 Map 與我們構(gòu)建的包含 status 消息的 Map 連接起來。最后,將新的 Map 和用戶名、密碼一起傳遞給 Scitter.execute() 方法,以傳送到 Twitter 服務(wù)器。

隨便說一句,所有這些任務(wù)需要的代碼并不多,但是需要更多的解釋,這是比較優(yōu)雅的編程方式。

潛在的重構(gòu)

理論上,傳給 update 的可選參數(shù)與傳給其他基于 GET 的 API 調(diào)用的可選參數(shù)將受到同等對待;只是結(jié)果的格式有所不同(結(jié)果是用于 POST 的名稱/值對,而不是用于 URL 的名稱/值對)。

如果 Twitter API 需要其他 HTTP 動作支持(PUT 和/或 DELETE 就是可能需要的動作),那么總是可以將 HTTP 參數(shù)作為特定參數(shù) — 也許又是一組 case 類 — 并讓 execute() 以一個 HTTP 動作、URL、名稱/值對的 map 以及(可選)用戶名/密碼作為 5 個參數(shù)。然后,必要時可以將可選參數(shù)轉(zhuǎn)換成一個字符串或一組 POST 參數(shù)。這些內(nèi)容只需記在腦中就行了。

顯示

show 調(diào)用接受要檢索的 Twitter 狀態(tài)的 id,并顯示 Twitter 狀態(tài)。和 update 一樣,這個方法非常簡單,無需再作說明,如清單 7 所示:

清單 7. Scitter v0.3: show

				
package com.tedneward.scitter

{

  class Scitter

  {

    // ...

	

    def show(id : Long) : Option[Status] =

    {

      val (statusCode, body) =

        Scitter.execute("http://twitter.com/statuses/show/" + id + ".xml",

		  username, password)

      if (statusCode == 200)

      {

        Some(Status.fromXml(XML.loadString(body)))

      }

      else

      {

        None

      }

    }

  }

}

還有問題嗎?

另一種顯示方法

如果想再試一下模式匹配,那么可以看看清單 8 中是如何以另一種方式編寫 show() 方法的:

清單 8. Scitter v0.3: show redux

				
package com.tedneward.scitter

{

  class Scitter

  {

    // ...

	

    def show(id : Long) : Option[Status] =

    {

      Scitter.execute("http://twitter.com/statuses/show/" + id + ".xml", 
          username, password) match

      {

        case (200, body) =>

          Some(Status.fromXml(XML.loadString(body)))

        case (_, _) =>

          None

      }

    }

  }

}

這個版本比起 if/else 版本是否更加清晰,這很大程度上屬于審美的問題,但公平而論,這個版本也許更加簡潔。(很可能查看代碼的人看到 Scala 的 “函數(shù)” 部分越多,就認為這個版本越吸引人。)

但是,相對于 if/else 版本,模式匹配版本有一個優(yōu)勢:如果 Twitter 返回新的條件(例如不同的錯誤條件或來自 HTTP 的響應(yīng)代碼),那么模式匹配版本在區(qū)分這些條件時可能更清晰。例如,如果某天 Twitter 決定返回 400 響應(yīng)代碼和一條錯誤消息(在主體中),以表明某種格式錯誤(也許是沒有正確地重新 Tweet),那么與 if/else 方法相比,模式匹配版本可以更輕松(清晰)地同時測試響應(yīng)代碼和主體的內(nèi)容。

還應(yīng)注意,我們還可以使用清單 8 中的方式創(chuàng)建一些局部應(yīng)用的函數(shù),這些函數(shù)只需要 URL 和參數(shù)。但是,坦白說,這是一種自找麻煩的解放方案,所以我不會采用。

撤銷

我們還想讓 Scitter 用戶可以撤銷剛才執(zhí)行的動作。為此,需要一個 destroy 調(diào)用,它將刪除已發(fā)布的 Twitter 狀態(tài),如清單 9 所示:

清單 9. Scitter v0.3: destroy

				
package com.tedneward.scitter

{

  class Scitter

  {

    // ...

	

    def destroy(id : Long) : Option[Status] =

    {

      val paramsMap = Map("id" -> id.toString())

    

      val (statusCode, body) =

        Scitter.execute("http://twitter.com/statuses/destroy/" + id.toString() + ".xml",

          paramsMap, username, password)

      if (statusCode == 200)

      {

        Some(Status.fromXml(XML.loadString(body)))

      }

      else

      {

        None

      }

    }

    def destroy(id : Id) : Option[Status] =

      destroy(id.id.toLong)

  }

}

有了這些東西,我們可以考慮將這個 Scitter 客戶機庫作為 “alpha” 版,至少實現(xiàn)一個簡單的 Scitter 客戶機。(按照慣例,這個任務(wù)就留給您來完成,作為一項 “讀者練習(xí)”。)

結(jié)束語

編寫 Scitter 客戶機庫是一項有趣的工作。雖然不能說 Scitter 已經(jīng)可以完全用于生產(chǎn),但是它絕對足以用于實現(xiàn)簡單的、基于文本的 Twitter 客戶機,這意味著它已經(jīng)可以投入使用了。要發(fā)現(xiàn)什么人可以使用它,哪些特性是需要的,從而使之變得更有用,最好的方法就是將它向公眾發(fā)布。

我已經(jīng)將本文和之前關(guān)于 Scitter 的文章中的代碼作為第一個修訂版提交到 Google Code 上的 Scitter 項目主頁。歡迎下載和試用這個庫,并告訴我您的想法。同時也歡迎提供 bug 報告、修復(fù)和建議。

您也無需受我的代碼庫的束縛。見證了之前三篇文章中進行的 Scitter 開發(fā),您應(yīng)該對 Twitter API 的使用有很好的理解。如果對于使用該 API 有不同的想法,那么盡管去做:拋開 Scitter,構(gòu)建自己的 Scala 客戶機庫。畢竟,做做這些內(nèi)部項目也是挺有樂趣的。

現(xiàn)在,我們要向 Scitter 揮手告別,開始尋找新的用 Scala 解決的項目。愿您從中找到樂趣,如果發(fā)現(xiàn)了用 Scala 編程的工作,別忘了告訴我!

責(zé)任編輯:yangsai 來源: IBMDW
相關(guān)推薦

2009-08-21 16:17:25

ScalaTwitter API

2009-09-28 11:01:39

從Java走進Scal

2009-02-04 17:32:03

ibmdwJavaScala

2009-06-17 11:44:22

Scala控制結(jié)構(gòu)

2009-06-16 17:54:38

Scala類語法語義

2009-07-15 10:14:25

Scala并發(fā)性

2009-10-14 11:14:38

ScitterScalaTwitter

2009-09-09 10:50:55

Scala例子Scala與Java

2009-06-16 17:09:17

Scala面向?qū)ο?/a>函數(shù)編程

2009-08-14 11:35:01

Scala Actor

2009-06-17 13:57:25

Scala元組數(shù)組

2009-06-17 13:26:06

scala繼承模型

2009-06-19 10:51:39

Scalapackage訪問修飾符

2009-06-15 15:33:13

ScalaTwitter

2009-08-27 12:00:40

ibmdwJava

2009-06-19 11:13:47

Scalacase類模式匹配

2009-07-08 12:43:59

Scala ServlScala語言

2009-06-19 11:42:09

Scala計算器解析

2009-09-15 18:27:59

equals實現(xiàn)canEqualScala

2010-09-14 15:34:41

Scala
點贊
收藏

51CTO技術(shù)棧公眾號

亚洲人免费短视频| 欧美精品羞羞答答| 91丨porny丨在线| 成人免费在线视频网址| bl视频在线免费观看| 中文字幕在线一区二区三区| 精品无人区一区二区三区竹菊| 午夜av成人| 欧美亚一区二区| 久久久久久三级| 久久 天天综合| av在线亚洲男人的天堂| 北条麻妃一区二区三区在线观看 | 精品国产三级a在线观看| 麻豆福利视频| aaa欧美日韩| 亚洲欧美精品| 国产精品视区| 亚洲精品免费网站| 亚洲精品无吗| 97精品视频在线观看| 欧美综合社区国产| 亚洲精品资源美女情侣酒店| 1769在线观看| 色婷婷精品久久二区二区蜜臀av| 黄色av地址| 亚洲精品国产无天堂网2021| 婷婷六月天在线| 成人av午夜电影| 国产一区二区片| 国内精品在线播放| 亚洲午夜激情| 日本成人在线不卡视频| 欧美日韩国产综合视频在线| 欧美在线三区| 北条麻妃高清一区| 欧美日韩国产在线一区| 成人写真视频福利网| 国产精品99一区二区三| 国产精品自拍视频| 日韩在线观看| 成人有码视频在线播放| 天天影视欧美综合在线观看| 91久久嫩草影院一区二区| 99久久www免费| 147欧美人体大胆444| 午夜精品久久久久99热蜜桃导演 | 神马午夜dy888| 亚洲午夜久久久久久久久电影院 | 欧美二区乱c黑人| 日本精品视频| 全亚洲最色的网站在线观看| 久久av资源| 成人国产在线激情| 禁久久精品乱码| 日韩精品无码一区二区三区| 久热成人在线视频| www插插插无码视频网站| 91亚洲精品一区二区乱码| 男女激情免费视频| 久久精品一区四区| 天堂影视av| 欧美日韩亚洲不卡| 免费高潮视频95在线观看网站| 色偷偷av一区二区三区| 丝袜美腿综合| 亚洲伊人久久综合| 免费看黄裸体一级大秀欧美| 热这里只有精品| 久久久99精品免费观看| 黄色高清在线观看| 精品奇米国产一区二区三区| 欧美爱爱视频| 成人福利在线观看| 美女视频黄a大片欧美| 十八禁视频网站在线观看| 亚洲色图制服丝袜| 成人影院在线看| 久久天天躁狠狠躁夜夜躁2014| 国产成人精品一区二区免费看京 | 求av网址在线观看| 伊人久久久久久久久久久| 好吊妞国产欧美日韩免费观看网站| 国产日本欧美一区二区三区在线| 亚洲一区二区免费看| 97国产在线播放| 欧美午夜精品伦理| 日韩中文影院| 成人有码视频在线播放| 国产一区二区不卡| 蜜桃av成人| 在线观看不卡av| 欧美在线不卡| 免费看a级黄色片| 欧美精品精品一区| 成人高潮a毛片免费观看网站| 国产一区二区高清不卡 | 天堂精品中文字幕在线| 欧美久久久久久久久久久久久| 一区二区三区四区av| 综合久久2o19| 国内精品国产三级国产在线专| 亚洲精品女人| 99热99在线| 日韩成人黄色av| 色综合久久网| 免费毛片小视频| 日韩欧美成人激情| 日韩免费视频| 国产又粗又长又大的视频| 精品国内二区三区| 久久亚洲国产| 99草草国产熟女视频在线| 精品国产免费人成在线观看| 精品国产99| 国产精品少妇在线视频| 亚洲成年人在线播放| 午夜久久tv| 99精产国品一二三产品香蕉| 亚洲一区第一页| 翔田千里一区二区| 污网站在线看| 91国产美女视频| 成人av电影在线网| 欧美xxxx少妇| 精品国产乱码久久久久| 亚洲成av人片一区二区| 第四色在线一区二区| 97在线国产视频| 亚洲精品二三区| 国产一区二区你懂的| 奇米影视888狠狠狠777不卡| 2020国产精品视频| 久久久精品人体av艺术| 三上悠亚国产精品一区二区三区| 麻豆成人av| 欧美日韩国产色站一区二区三区| 成人婷婷网色偷偷亚洲男人的天堂| 午夜视频你懂的| 欧美大片免费看| 久久精品欧美一区二区三区不卡 | 国产成人精品av在线| 91一区一区三区| 日韩不卡免费高清视频| 亚洲精品成人久久久998| 9191久久久久久久久久久| 天天插综合网| 免费看男男www网站入口在线| 日韩av电影院| 一区二区三区精品在线| 久久精品论坛| 国产美女视频黄a视频免费| 97国产精品免费视频| 日本一区二区三区在线不卡| 综合激情网...| 3d黄动漫网站| 国产成人亚洲综合91精品| 亚洲日本丝袜连裤袜办公室| 外国成人在线视频| 写真片福利在线播放| 自拍偷拍欧美专区| 免费看污久久久| 欧美日韩的一区二区| 欧美涩涩网站| h网站在线免费观看| 懂色一区二区三区av片| 欧美中文字幕久久| 午夜一区不卡| 毛片大全在线观看| 亚洲一区美女| 国产一区二区三区在线观看视频 | 丰满人妻中伦妇伦精品app| 欧美日韩你懂的| 在线免费国产视频| 国产成人拍精品视频午夜网站| 综合久久久久久| 老司机凹凸av亚洲导航| 黄网视频在线观看| 国产日韩欧美在线看| 欧美午夜视频一区二区| 激情综合久久| a国产在线视频| 拔插拔插海外华人免费| 色综合天天综合网国产成人网| 最新中文字幕一区二区三区| 久久伦理在线| av官网在线播放| 91看片淫黄大片91| 欧美国产日韩中文字幕在线| 国产精品久线在线观看| 欧洲杯足球赛直播| 日本精品在线| 青青草视频国产| 欧美一区二区影院| 欧美日韩国产乱码电影| 国产主播一区二区| 国产欧美自拍一区| 午夜在线免费观看视频| 国产一二三在线视频| 国产精品视频资源|