HTTPBuilder:使用Groovy操做HTTP資源

現在的Web,孤立的應用已經再也不吃香,隨之而來的是與其餘應用(如Twitter)或服務(如S3)交互的意願愈來愈強烈。對於Groovy而言,HTTPBuilder絕對是應對這一需求的不二之選。html

若是熟悉HttpClient,那麼你對HTTPBuilder就不會感到陌生,它是對前者的封裝,使之更符合Groovy的使用慣例。下面的例子摘自HTTPBuilder的文檔,它充分展現了本身的特色:java

import groovyx.net.http.HTTPBuilder
import static groovyx.net.http.Method.GET
import static groovyx.net.http.ContentType.TEXT
 
def http = new HTTPBuilder( 'http://www.google.com/search' )
 
http.request(GET,TEXT) { req ->
  uri.path = '/mail/help/tasks/'
  headers.'User-Agent' = 'Mozilla/5.0'
  
  //請求成功
  response.success = { resp, reader ->
    assert resp.statusLine.statusCode == 200
    println "My response handler got response: ${resp.statusLine}"
    println "Response length: ${resp.headers.'Content-Length'}"
    System.out << reader // print response stream
  }

  //404
  response.'404' = { resp ->  
    println 'Not found'
  }
  
  // 401
  http.handler.'401' = { resp ->
    println "Access denied"
  }

  //其餘錯誤,不實現則採用缺省的:拋出異常。
  http.handler.failure = { resp ->
    println "Unexpected failure: ${resp.statusLine}"
  }
}

無需過多的講解,上述的例子已經很是明白地說明了HTTPBuilder的基本使用。儘管如此,對於上例中的內容類型(即request括號中的TEXT),仍是有必要囉嗦幾句。HTTPBuilder支持對響應內容的自動解析,解析的方式可在請求中指定,缺省除了TEXT以外,還支持XML、HTML和JSON。若是在請求中不指定解析方式,那麼它會根據響應的內容類型選擇最合適的方式進行解析。對於每一種內容類型:apache

  • TEXT,純文本
  • XML,採用XmlSlurper解析內容
  • HTML,先採用NekoHTML使HTML規範化,以後採用XmlSlurper解析DOM
  • JSON,採用JSON-lib解析內容

要想按照本身的意願解析內容,你能夠建立本身的內容解析器:json

import au.com.bytecode.opencsv.CSVReader
import groovyx.net.http.ParserRegistry

//註冊本身的內容類型和解析器
http.parser.'text/csv' = { resp ->
   return new CSVReader( new InputStreamReader( resp.entity.content
            , ParserRegistry.getCharset( resp ) ) )
}

//驗證使用
http.get( uri : 'http://somehost.com/contacts.csv'
        , contentType : 'text/csv' ) { resp, csv ->
   assert csv instanceof CSVReader
   // parse the csv stream here.
}

除了展現如何支持新的內容類型,上例還展現另外一種GET請求方法:直接使用HTTPBuilder的get方法。該方法簡化了GET請求的操做,很是適合簡單的場景。提到了GET,就不能不提POST,使用HTTPBuilder完成POST請求的方法以下:安全

import groovyx.net.http.HTTPBuilder
 
def http = new HTTPBuilder('http://twitter.com/statuses/')

http.request( POST ) { 
  uri.path = 'update.xml'
  body =  [ status : 'update!' , source : 'httpbuilder' ] 
  requestContentType = ContentType.URLENC 
 
  response.success = { resp ->
    println "Tweet response status: ${resp.statusLine}"
    assert resp.statusLine.statusCode == 200
  }
}

一樣很是簡單,不一樣則在於POST中須要指定body和requestContentType,使用它徹底能夠模擬窗體的提交。在GET請求中咱們談到了對於響應內容的解析,與之對應的則是如何在POST中提交不一樣的內容類型:異步

  • XML:使用StreamingMarkupBuilder。
    http.request( POST, XML ) {
      body = {
        auth {
          user 'Bob'
          password 'pass'
        }
      }
    }
  • JSON:藉助Json-Lib的JsonGroovyBuilder動態構造。
    http.request( POST, JSON ) { req ->
        body = [
          first : 'Bob',
          last : 'Builder',
          address : [
            street : '123 Some St',
            town : 'Boston',
            state : 'MA',
            zip : 12345
          ]
        ]
        
        response.success = { resp, json ->...}
    }

一樣,HTTPBuilder對於POST也提供了便利的post方法,關於它的使用也請參見文檔。工具

REST是現在Web的寵兒,許多Web 2.0 API都宣稱本身是RESTful的。且不論其中的真僞,做爲給HTTP操做提供DSL的工具,HTTPBuilder天然沒有錯過這個潮流。RESTClient即是它對於這種趨勢的迴應,其自己是HTTPBuilder的子類,雖然損失了部分靈活性,但簡化了爲GET、PUT、POST、DELETE和HEAD操做:post

twitter = new RESTClient( 'https://twitter.com/statuses/' )

//HEAD
twitter.head( path : 'public_timeline.json' ).status == 200

//GET
def resp = twitter.get( path : 'friends_timeline.json' )

//POST
def msg = "I'm using HTTPBuilder's RESTClient on ${new Date()}"
resp = twitter.post( path : 'update.xml', 
                     body : [ status:msg, source:'httpbuilder' ]
                            , requestContentType : URLENC )

//DELETE
resp = twitter.delete( path : "destroy/${postID}.json" )

AsyncHTTPBuilder是該工具的另外一個類,看名字就知道,它主要用於異步請求。它的使用方式相似HTTPBuilder,只是返回結果是一個java.util.concurrent.Future類型,關於它的使用詳情,可參見文檔。ui

想找臺免費的機器嗎?如今已徹底不是天方夜譚,Google GAE就是你要找的目標。雖說它是免費的並不徹底對,它在必定配額內免費,但這個配額對於我的實驗或小規模的應用,應該夠用了。開發GAE應用對於Grails來說並不是難事,但若是你想在應用裏使用HTTPBuilder去發起請求,那麼就不會那麼順利。因爲GAE的安全限制,你不能直接去打開Socket,這正是HTTPBuilder底層(HttpClient)的機制。這時,就須要使用HTTPURLConnection完成這一任務。以它爲基礎,HTTPBuilder工具包內提供了另外一個兼容GAE的「HTTPBuilder」:HttpURLClient。使用其餘並不複雜:google

import groovyx.net.http.*

def http = new HttpURLClient( url: 'http://twitter.com/statuses/' )
def resp = http.request( path: 'user_timeline.json'
                       , query: [id:'httpbuilder', count:5] )
println "JSON response: ${resp.status}"
resp.data.each {
    println it.created_at
    println '  ' + it.text
}

本文最後要介紹的一個組件是URIBuilder,它並不直接面對HTTP請求,而是輔助HTTPBuilder構造複雜的URL,在其內部使用。它的基本使用以下:

import groovyx.net.http.URIBuilder
 
def uri = new URIBuilder( 'http://www.google.com/one/two?a=1#frag' )

uri.scheme = 'https'
assert uri.toString() == 'https://www.google.com:80/one/two?a=1#frag' 

uri.host = 'localhost'
assert uri.toString() == 'https://localhost:80/one/two?a=1#frag' 

uri.port = 8080
assert uri.toString() == 'https://localhost:8080/one/two?a=1#frag' 

uri.fragment = 'asdf2'
assert uri.toString() == 'https://localhost:8080/one/two?a=1#asdf2' 

// relative paths:
uri.path = 'three/four.html'
assert uri.toString() == 'https://localhost:8080/one/three/four.html?a=1#asdf2' 
uri.path = '../four/five'
assert uri.toString() == 'https://localhost:8080/one/four/five?a=1#asdf2' 

// control the entire path with leading '/' :
uri.path = '/six'
assert uri.toString() == 'https://localhost:8080/six?a=1#asdf2'

同時也提供了對查詢字符串的處理:

def uri = new groovyx.net.http.URIBuilder( 'http://localhost?a=1&b=2' )

assert uri.query instanceof Map
assert uri.query.a == '1'
assert uri.query.b == '2'
 
uri.addQueryParam 'd', '4'
uri.removeQueryParam 'b'
 
assert uri.toString() == 'http://localhost?d=4&a=1'
 
uri.query = [z:0,y:9,x:8]
assert uri.toString() == 'http://localhost?z=0&y=9&x=8'

uri.query = null
assert uri.toString() == 'http://localhost'
 
// parameters are also properly escaped as well:
uri.query = [q:'a:b',z:'war & peace']
assert uri.toString() == 'http://localhost?q=a%3Ab&z=war+%26+peace'

但願經過本文的浮光掠影式的介紹,能讓你們對於HTTPBuilder有個簡單的瞭解;)

相關文章
相關標籤/搜索