Clojure世界:Http Client

    使用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


时间: 2024-11-01 15:35:52

Clojure世界:Http Client的相关文章

Clojure世界:静态代码分析

    Java世界里有findbugs这样的神器,可以让你避免很多"简单愚蠢"的bug.同样,Clojure世界里也有相应的替代品,这就是今天要介绍的kibit.不过kibit现在还比较年轻,判断的规则较少,但是已经可以使用起来做clojure代码的静态检查. 项目主页:https://github.com/jonase/kibit 使用: 1.安装lein插件: lein plugin install jonase/kibit 0.0.2 2.在项目的根目录运行 lein kibi

Clojure世界:如何做性能测试

  我们经常需要在程序中测量某段代码的性能,或者某个函数的性能,在Java中,我们可能简单地循环调用某个方法多少次,然后利用System.currentTimeMillis()方法测量下时间.在Ruby中,一般都是用Benchmark module做测试,提供了更详细的报告信息.     同样,在Clojure里你可以做这些事情,你仍然可以使用System.currentTimeMillis()来测量运行时间,例如: user=> (defn sum1 [& args] (reduce + 

Clojure世界:API文档生成

    继续Clojure世界之旅,介绍下我今天的探索成果,使用clojure生成clojure项目的API文档.在java里,我们是利用javadoc生成API文档,各种build工具都提供了集成,例如maven和ant都提供了javadoc插件或者task.在Clojure世界里,同样有一系列工具帮助你从源码中自动化生成API文档.今天主要介绍三个工具.不过我不会介绍怎么在clojure里写doc,具体怎么做请看一些开源项目,或者直接看clojure.core的源码.     首先是codo

Clojure世界:使用rlwrap增强REPL

   Clojure的REPL非常方便,可以随时随地试验你的想法,REPL是read-eval-print-loop的简称.默认clojure.contrib有带一个shell脚本来启动REPL,具体看这里.你也可以用JLine来增强REPL: java -cp "%CLOJURE_DIR%\jline-VERSION.jar;%CLOJURE_JAR%" jline.ConsoleRunner clojure.main     不过,其实你还可以用rlwrap这个GNU库来增强clo

Clojure世界:文件IO

   文件读写是日常编程中最经常使用的操作之一.这篇blog将大概介绍下Clojure里对文件操作的常用类库.     首先介绍标准库clojure.java.io,这是最经常用的IO库,定义了常见的IO操作.     首先,直接看一个例子,可以熟悉下大多数常用的函数: (ns io   (:use [clojure.java.io])) ;;file函数,获取一个java.io.File对象 (def f (file "a.txt")) ;;拷贝文件使用copy (copy f (f

Clojure世界:利用HouseMD诊断clojure

  HouseMD是淘宝的聚石写的一个非常优秀的Java进程运行时诊断和调试工具,如果你接触过btrace,那么HouseMD也许你应该尝试下,它比btrace更易用,不需要写脚本,类似strace的方式attach到jvm进程做跟踪调试.     基本的安装和使用请看这篇文档<UserGuide>,恕不重复.以下内容都假设你正确安装了housemd.     本文主要介绍下怎么用housemd诊断跟踪clojure进程.Clojure的java实现也是跑在JVM里,当然也可以用housemd

Clojure世界:单元测试

    单元测试也是一个开发中最常见的需求,在Java里我们用JUnit或者TestNG,在clojure里也内置了单元测试的库.标准库的clojure.test,以及第三方框架midje.这里我将主要介绍clojure.test这个标准库,midje是个更加强大的测试框架,广告下,midje的介绍在第二次cn-clojure聚会上将有个Topic,我就不画蛇添足了.通常来说,clojure.test足够让你对付日常的测试.     首先看一个最简单的例子,定义一个函数square来计算平方,然

Clojure世界: STM的统计

   年前一篇blog提过,写了一个stm-profiler用于统计clojure STM的运行状况,放在了github上:https://github.com/killme2008/stm-profiler    STM的事务在遇到写冲突(多个事务写同一个ref的时候)就会回滚事务并重试,通过stm-profiler你可以查看事务的重试次数,重试原因,以及每个reference的使用情况.使用很简单,在lein的project.clj引用stm-profiler: [stm-profiler 

Clojure世界:日志管理——clojure.tools.logging

   处理日志是任何一个产品级的程序都需要仔细处理的模块.在Java中,我们经常使用的是log4j就是一个日志框架.在clojure里,同样有一套日志框架--clojure.tools.logging,它不仅提供了常用的日志输出功能,还屏蔽了Java各种日志框架之间的差异,如slf4j,commons-logging,log4j,java.util.logging等,让你可以透明地使用这些框架来处理日志. 名称:clojure.tools.logging 主页:https://github.co