使用http client提交表单或者下载网页也是非常常见的任务,比如使用Java的时候可以用标准库的HttpURLConnection,也可以选择
Apache Http Client。在clojure里也有这样的类库,这里我将介绍三个各有特色的http client实现。
首先,我最先推荐使用clj-http这个类库,它是Apache HttpClient的clojure wrapper,是一个提供同步API的简单易用的Http Client。
名称: clj-http
主页:
https://github.com/dakrone/clj-http
依赖:
[clj
-
http
"
0.3.1
"
]
例子:
(require
'
[clj-http.client :as client])
(client
/
get
"
http://google.com
"
)
结果:
=>
{:cookies {"NID" {:domain ".google.com.hk", :expires #<Date Tue Aug 14 18:20:38 CST 2012>, :path "/", :value "56=qn2OWtODE2D3fUKi_vbi44jZepOeLI9xC4Ta1JQLEicqUvIZAqr7TCmft_hq8i_FRwnFXdTK1jV2S5IrSZFyYhlAN2KcQEXgWX1iK36gM2iYPaKPihuUZDCqgiAamDOl", :version 0}, "PREF" {:domain ".google.com.hk", :expires #<Date Wed Feb 12 18:20:38 CST 2014>, :path "/", :value "ID=8b73a654ff0a2783:FF=0:NW=1:TM=1329128438:LM=1329128438:S=uEM4SsFuHlkqtVhp", :version 0}}, :status
200
:headers {
"
date
"
"
Sun, 01 Aug 2010 07:03:49 GMT
"
"
cache-control
"
"
private, max-age=0
"
"
content-type
"
"
text/html; charset=ISO-8859-1
"
} :body
"
<!doctype html>
"
:trace
-
redirects [
"
http://google.com
"
"
http://www.google.com/
"
"
http://www.google.fr/
"
]}
更多例子:
(client
/
get
"
http://site.com/resources/3
"
{:accept :json}) ;; Various options: (client
/
post
"
http://site.com/api
"
{:basic
-
auth [
"
user
"
"
pass
"
] :body
"
{\
"
json\
"
: \
"
input\
"
}
"
:headers {
"
X-Api-Version
"
"
2
"
} :content
-
type :json :socket
-
timeout
1000
:conn
-
timeout
1000
:accept :json}) ;; Need to contact a server with an untrusted SSL cert
?
(client
/
get
"
https://alioth.debian.org
"
{:insecure
?
true
}) ;; If you don
'
t want to follow-redirects automatically:
(client
/
get
"
http://site.come/redirects-somewhere
"
{:follow
-
redirects
false
}) ;; Only follow a certain number of redirects: (client
/
get
"
http://site.come/redirects-somewhere
"
{:max
-
redirects
5
}) ;; Throw an exception
if
redirected too many times: (client
/
get
"
http://site.come/redirects-somewhere
"
{:max
-
redirects
5
:
throw
-
exceptions
true
}) ;; Send form params as a urlencoded body (client
/
post
"
http//site.com
"
{:form
-
params {:foo
"
bar
"
}}) ;; Multipart form uploads
/
posts ;; a map or vector works as the multipart object. Use a vector of ;; vectors
if
you need to preserve order, a map otherwise. (client
/
post
"
http//example.org
"
{:multipart [[
"
title
"
"
My Awesome Picture
"
] [
"
Content/type
"
"
image/jpeg
"
] [
"
file
"
(clojure.java.io
/
file
"
pic.jpg
"
)]]}) ;; Multipart values can be one of the following: ;; String, InputStream, File, or a
byte
-
array ;; Basic authentication (client
/
get
"
http://site.com/protected
"
{:basic
-
auth [
"
user
"
"
pass
"
]}) (client
/
get
"
http://site.com/protected
"
{:basic
-
auth
"
user:pass
"
}) ;; Query parameters (client
/
get
"
http://site.com/search
"
{:query
-
params {
"
q
"
"
foo, bar
"
}})
clj-http的API相当的简洁漂亮,使用起来非常便利,强烈推荐。题外,学习clojure的一个好方法就是为现有的java类库实现一些方便的clojure wrapper。
如果你需要异步的http client,我会推荐http.async.client这个类库,它的API是异步形式的类似 Java的Future模式,对于clojure程序员来说应该更像是agent。
名称:http.async.client
主页:
https://github.com/neotyk/http.async.client
依赖:
[http.async.client
"
0.4.1
"
]
例子:
(require
'
[http.async.client :as c])
(with
-
open [client (c
/
create
-
client)] (let [response (c
/
GET client
"
http://neotyk.github.com/http.async.client/
"
)] (prn (c
/
done
?
response)) (c
/
await response) (prn (c
/
string response)) (prn (c
/
status response)) (prn (c
/
done
?
response))))
输出:
false
<!
DOCTYPE html {:code
200
, :msg
"
OK
"
, :protocol
"
HTTP/1.1
"
, :major
1
, :minor
1
}
true
更多例子:
(c
/
POST client
"
http://example.com
"
:body
"
hello world
"
:timeout
3000
) (c
/
DELETE client
"
http://example.com
"
) (c
/
POST client
"
http://example.com
"
:body
"
hello world
"
:auth {:type :basic :user
"
admin
"
:password
"
admin
"
})
请注意,这些方法都是异步调用的,你需要通过await来等待调用完成,或者通过done?来判断调用是否完成。
http.async.client有个比较重要的特性就是对Http Chunked编码的支持,分别通过LazySeq和callback的方式支持,首先看将Http chunked变成一个lazy seq:
(with
-
open [client (client
/
create
-
client)] ; Create client (let [resp (client
/
stream
-
seq client :get url)] (doseq [s (s
/
string resp)] (println s))))
这里非常关键的一点是stream-seq返回的chunk序列,每取一个就少一个(通过first函数),也就是说每次调用first取到的chunk都不一样,是顺序递增,不可重复获取的。
通过callback方式处理:
(with
-
open [client (client
/
create
-
client)] ; Create client (let [parts (ref #{}) resp (client
/
request
-
stream client :get url (fn [state body] (dosync (alter parts conj (string body))) [body :
continue
]))] ;;
do
something to @parts ))
自己传入一个callback函数接收chunk,比如这里用一个ref累积。 http.async.client的详细文档看这里:http://neotyk.github.com/http.async.client/docs.html 最后,有兴趣还可以看下aleph这个异步通讯的框架,它支持Http协议,也提供了http server和client的实现。不过它的API就没有那么简单明了,它的模型是类似go语言里利用channel做异步通讯的模型,http只是它的一个模块罢了,这是另一个话题了。
文章转自庄周梦蝶 ,原文发布时间2012-02-13
相关资源:敏捷开发V1.0.pptx