• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    迪恩网络公众号

dakrone/clj-http: An idiomatic clojure http client wrapping the apache client. O ...

原作者: [db:作者] 来自: 网络 收藏 邀请

开源软件名称:

dakrone/clj-http

开源软件地址:

https://github.com/dakrone/clj-http

开源编程语言:

Clojure 99.3%

开源软件介绍:

clj-http documentation

https://img.shields.io/clojars/v/clj-http.svg https://github.com/dakrone/clj-http/workflows/Clojure%20CI/badge.svg https://badges.gitter.im/clj-http/Lobby.svg

Table of Contents

Branches

There are branches for the major version numbers:

  • 2.x (no longer maintained except for security issues)
  • 3.x (current stable releases and the main Github branch)
  • master (which is 4.x, unreleased, based on version 5 of the apache http client)

Introduction

Overview

clj-http is an HTTP library wrapping the Apache HttpComponents client. This library has taken over from mmcgrana’s clj-http.

Philosophy

The design of clj-http is inspired by the Ring protocol for Clojure HTTP server applications.

The client in clj-http.core makes HTTP requests according to a given Ring request map and returns Ring response maps corresponding to the resulting HTTP response. The function clj-http.client/request uses Ring-style middleware to layer functionality over the core HTTP request/response implementation. Methods like clj-http.client/get are sugar over this clj-http.client/request function.

Installation

clj-http is available as a Maven artifact from Clojars.

With Leiningen/Boot:

[clj-http "3.12.3"]

If you need an older version, a 2.x release is also available.

[clj-http "2.3.0"]

clj-http 3.x supports clojure 1.6.0 and higher. clj-http 4.x will support clojure 1.7.0 and higher.

Quickstart

The main HTTP client functionality is provided by the clj-http.client namespace.

First, require it in the REPL:

(require '[clj-http.client :as client])

Or in your application:

(ns my-app.core
  (:require [clj-http.client :as client]))

The client supports simple get, head, put, post, delete, copy, move, patch, and options requests. Response are returned as Ring-style response maps:

HEAD

(client/head "http://example.com/resource")

(client/head "http://example.com/resource" {:accept :json})

GET

Example requests:

(client/get "http://example.com/resources/id")

;; Setting options
(client/get "http://example.com/resources/3" {:accept :json})
(client/get "http://example.com/resources/3" {:accept :json :query-params {"q" "foo, bar"}})

;; Specifying headers as either a string or collection:
(client/get "http://example.com"
            {:headers {"foo" ["bar" "baz"], "eggplant" "quux"}})

;; Using either string or keyword header names:
(client/get "http://example.com"
            {:headers {:foo ["bar" "baz"], :eggplant "quux"}})

;; Completely ignore cookies:
(client/post "http://example.com" {:cookie-policy :none})
;; There are also multiple ways to handle cookies
(client/post "http://example.com" {:cookie-policy :default})
(client/post "http://example.com" {:cookie-policy :netscape})
(client/post "http://example.com" {:cookie-policy :standard})
(client/post "http://example.com" {:cookie-policy :standard-strict})

;; Cookies can be completely configurable with a custom spec by adding a
;; function to return a cookie spec for parsing the cookie. For example, if you
;; wanted to configure a spec provider to have a certain compatibility level:
(client/post "http://example.com"
             {:cookie-spec
              (fn [http-context]
                (println "generating a new cookie spec")
                (.create
                 (org.apache.http.impl.cookie.RFC6265CookieSpecProvider.
                  org.apache.http.impl.cookie.RFC6265CookieSpecProvider$CompatibilityLevel/IE_MEDIUM_SECURITY
                  (PublicSuffixMatcherLoader/getDefault))
                 http-context))})
;; Or a version with relaxed compatibility
(client/post "http://example.com"
             {:cookie-spec
              (fn [http-context]
                (println "generating a new cookie spec")
                (.create
                 (org.apache.http.impl.cookie.RFC6265CookieSpecProvider.
                  org.apache.http.impl.cookie.RFC6265CookieSpecProvider$CompatibilityLevel/RELAXED
                  (PublicSuffixMatcherLoader/getDefault))
                 http-context))})

;; Sometimes you want to do your own validation or something, which you can do
;; by proxying the CookieSpecBase. Note that this doesn't actually return the
;; cookies, because clj-http does its own cookie parsing. If you want to store
;; the cookies from these methods you'll need to use a cookie store or put it in
;; some datastructure yourself.
(client/post "http://example.com"
             {:cookie-spec
              (fn [http-context]
                (proxy [org.apache.http.impl.cookie.CookieSpecBase] []
                  ;; Version and version header
                  (getVersion [] 0)
                  (getVersionHeader [] nil)
                  ;; parse headers into cookie objects
                  (parse [header cookie-origin] (java.util.ArrayList.))
                  ;; Validate a cookie, throwing MalformedCookieException if the
                  ;; cookies isn't valid
                  (validate [cookie cookie-origin]
                    (println "validating:" cookie))
                  ;; Determine if a cookie matches the target location
                  (match [cookie cookie-origin] true)
                  ;; Format a list of cookies into a list of headers
                  (formatCookies [cookies] (java.util.ArrayList.))))})

;; If you have created your own registry for cookie policies, you can provide
;; :cookie-policy-registry to use it. See
;; clj-http.core/create-custom-cookie-policy-registry for an example of a custom
;; registry
(client/post "http://example.com"
             {:cookie-policy-registry my-custom-policy-registry
              :cookie-policy "my-policy"})

;; 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://example.com/redirects-somewhere" {:redirect-strategy :none})

;; Only follow a certain number of redirects:
(client/get "http://example.com/redirects-somewhere" {:max-redirects 5})

;; Avoid throwing exceptions if redirected too many times:
(client/get "http://example.com/redirects-somewhere" {:max-redirects 5 :redirect-strategy :graceful})

;; Throw an exception if the get takes too long. Timeouts in milliseconds.
(client/get "http://example.com/redirects-somewhere" {:socket-timeout 1000 :connection-timeout 1000})

;; Query parameters
(client/get "http://example.com/search" {:query-params {"q" "foo, bar"}})

;; "Nested" query parameters
;; (this yields a query string of `a[e][f]=6&a[b][c]=5`)
(client/get "http://example.com/search" {:query-params {:a {:b {:c 5} :e {:f 6}}}})

;; Provide cookies — uses same schema as :cookies returned in responses
;; (see the cookie store option for easy cross-request maintenance of cookies)
(client/get "http://example.com"
            {:cookies {"ring-session" {:discard true, :path "/", :value "", :version 0}}})

;; Tell clj-http not to decode cookies from the response header
(client/get "http://example.com" {:decode-cookies false})

;; Support for IPv6!
(client/get "http://[2001:62f5:9006:e472:cabd:c8ff:fee3:8ddf]")

;; Super advanced, your own http-client-context and request-config
(client/get "http://example.com/get"
            {:http-client-context my-http-client-context
             :http-request-config my-request-config})

The client will also follow redirects on the appropriate 30* status codes.

The client transparently accepts and decompresses the gzip and deflate content encodings.

:trace-redirects will contain the chain of the redirections followed.

PUT

(client/put "http://example.com/api" {:body "my PUT body"})

POST

;; Various options:
(client/post "http://example.com/api"
             {:basic-auth ["user" "pass"]
              :body "{\"json\": \"input\"}"
              :headers {"X-Api-Version" "2"}
              :content-type :json
              :socket-timeout 1000      ;; in milliseconds
              :connection-timeout 1000  ;; in milliseconds
              :accept :json})

;; Send form params as a urlencoded body (POST or PUT)
(client/post "http://example.com" {:form-params {:foo "bar"}})

;; Send form params as a json encoded body (POST or PUT)
(client/post "http://example.com" {:form-params {:foo "bar"} :content-type :json})

;; Send form params as a json encoded body (POST or PUT) with options
(client/post "http://example.com" {:form-params {:foo "bar"}
                                   :content-type :json
                                   :json-opts {:date-format "yyyy-MM-dd"}})

;; You can also specify the encoding of form parameters
(client/post "http://example.com" {:form-params {:foo "bar"}
                                   :form-param-encoding "ISO-8859-1"})

;; Send form params as a Transit encoded JSON body (POST or PUT) with options
(client/post "http://example.com" {:form-params {:foo "bar"}
                                   :content-type :transit+json
                                   :transit-opts
                                   {:encode {:handlers {}}
                                    :decode {:handlers {}}}})

;; Send form params as a Transit encoded MessagePack body (POST or PUT) with options
(client/post "http://example.com" {:form-params {:foo "bar"}
                                   :content-type :transit+msgpack
                                   :transit-opts
                                   {:encode {:handlers {}}
                                    :decode {:handlers {}}}})

;; Multipart form uploads/posts
;; takes a vector of maps, to preserve the order of entities, :name
;; will be used as the part name unless :part-name is specified
(client/post "http://example.org" {:multipart [{:name "title" :content "My Awesome Picture"}
                                               {:name "Content/type" :content "image/jpeg"}
                                               {:name "foo.txt" :part-name "eggplant" :content "Eggplants"}
                                               {:name "file" :content (clojure.java.io/file "pic.jpg")}]
                                   ;; You can also optionally pass a :mime-subtype
                                   :mime-subtype "foo"})

;; Multipart :content values can be one of the following:
;; String, InputStream, File, a byte-array, or an instance of org.apache.http.entity.mime.content.ContentBody
;; Some Multipart bodies can also support more keys (like :encoding
;; and :mime-type), check src/clj-http/multipart.clj to see all flags

;; Apache's http client automatically retries on IOExceptions, if you
;; would like to handle these retries yourself, you can specify a
;; :retry-handler. Return true to retry, false to stop trying:
(client/post "http://example.org" {:multipart [["title" "Foo"]
                                               ["Content/type" "text/plain"]
                                               ["file" (clojure.java.io/file "/tmp/missing-file")]]
                                   :retry-handler (fn [ex try-count http-context]
                                                    (println "Got:" ex)
                                                    (if (> try-count 4) false true))})

;; to handle a file with non-ascii filename, try :multipart-charset "UTF-8" and :multipart-mode BROWSER_COMPATIBLE
;; see also: https://stackoverflow.com/questions/3393445/international-characters-in-filename-in-mutipart-formdata
(import (org.apache.http.entity.mime HttpMultipartMode))

(client/post "http://example.org" {:multipart [{:content (clojure.java.io/file "日本語.txt")}]
                                   :multipart-mode HttpMultipartMode/BROWSER_COMPATIBLE
                                   :multipart-charset "UTF-8"} )

A word about flattening nested :query-params and :form-params maps. There are essentially three different ways to handle flattening them:

:ignore-nested-query-string
Do not handle nested query parameters specially, treat them as the exact text they come in as. Defaults to false.
:flatten-nested-form-params
Flatten nested (map within a map) :form-params before encoding it as the body. Defaults to false, meaning form params are encoded only x-www-form-urlencoded.
:flatten-nested-keys
An advanced way of specifying which keys having nested maps should be flattened. A middleware function checks the previous two options (:ignore-nested-query-string and :flatten-nested-form-params) and modifies this to be the list that will be flattened.

DELETE

(client/delete "http://example.com/resource")

Async HTTP Request

The new async HTTP request API is a Ring-style async API. All options for synchronous requests can be used in asynchronous requests. Starting an async request is easy, for example:
;; :async? in options map need to be true
(client/get "http://example.com"
            {:async? true}
            ;; respond callback
            (fn [response] (println "response is:" response))
            ;; raise callback
            (fn [exception] (println "exception message is: " (.getMessage exception))))

All exceptions thrown during the request will be passed to the raise callback.

Cancelling Requests

Calls to the http methods with :async true return an Apache BasicFuture that you can call .get or .cancel on. See the Javadocs for BasicFuture here. For instance:

(import '(java.util.concurrent TimeoutException TimeUnit))

(let [future (client/get "http://example.com/slow-url"
                         {:async true :oncancel #(println "request was cancelled")}
                         #(println :got %) #(println :err %))]
  (try
    (.get future 1 TimeUnit/SECONDS)
    (catch TimeoutException e
      ;; Cancel the request, it's taken too long
      (.cancel future true))))

Coercions

clj-http allows coercing the body of the request either before it is sent (input coercion), or after it’s received (output coercion) from the server.

Input coercion

;; body as a byte-array
(client/post "http://example.com/resources" {:body my-byte-array})

;; body as a string
(client/post "http://example.com/resources" {:body "string"})

;; :body-encoding is optional and defaults to "UTF-8"
(client/post "http://example.com/resources"
             {:body "string" :body-encoding "UTF-8"})

;; body as a file
(client/post "http://example.com/resources"
             {:body (clojure.java.io/file "/tmp/foo") :body-encoding "UTF-8"})

;; :length is optional for passing in an InputStream; if not

                      

鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap