Permalink
Browse files

allow json coercion for exception cases based on :coerce setting, can

be either :always, :exceptional or :unexceptional.

Fixes #118
  • Loading branch information...
1 parent 99cf57c commit 426049c93eba360c6eca92fc542b70627a7d2be3 @dakrone committed Mar 6, 2013
Showing with 64 additions and 21 deletions.
  1. +13 −0 Readme.md
  2. +3 −0 changelog.org
  3. +30 −17 src/clj_http/client.clj
  4. +18 −4 test/clj_http/test/core.clj
View
@@ -217,6 +217,19 @@ The client transparently accepts and decompresses the `gzip` and
;; stream has been read
```
+JSON coercion defaults to only an "unexceptional" statuses, meaning
+status codes in the #{200 201 202 203 204 205 206 207 300 301 302 303
+307} range. If you would like to change this, you can send the
+`:coerce` option, which can be set to:
+
+```clojure
+:always ;; always json decode the body
+:unexceptional ;; only json decode when not an HTTP error response
+:exceptional ;; only json decode when it IS an HTTP error response
+```
+
+The `:coerce` setting defaults to `:unexceptional`.
+
#### Body decompression
By default, clj-http will add the `{"Accept-Encoding" "gzip, deflate"}`
header to requests, and automatically decompress the resulting gzip or
View
@@ -247,6 +247,9 @@
* Work log
Log of merges/issues/work that's gone in so I know what to put in
the changelog for the next release
+** 2013-03-06
+ - allow json coercion for exception cases based on :coerce setting,
+ can be either :always, :exceptional or :unexceptional
** 2013-03-01
- Update clojure to 1.5
- Move SingleClientConnManager shutdown into finally block
View
@@ -203,29 +203,41 @@
(decompress-body resp-c)))))
;; Multimethods for coercing body type to the :as key
-(defmulti coerce-response-body (fn [as _] as))
+(defmulti coerce-response-body (fn [req _] (:as req)))
(defmethod coerce-response-body :byte-array [_ resp] resp)
(defmethod coerce-response-body :stream [_ resp] resp)
-(defmethod coerce-response-body :json [_ {:keys [body status] :as resp}]
- (if (and json-enabled? (unexceptional-status? status))
- (assoc resp :body (json-decode (String. #^"[B" body "UTF-8") true))
- (assoc resp :body (String. #^"[B" body "UTF-8"))))
+(defn coerce-json-body
+ [{:keys [coerce]} {:keys [body status] :as resp} keyword? & [charset]]
+ (let [charset (or charset "UTF-8")]
+ (if json-enabled?
+ (cond
+ (= coerce :always)
+ (assoc resp :body (json-decode (String. #^"[B" body charset) keyword?))
+
+ (and (unexceptional-status? status)
+ (or (nil? coerce) (= coerce :unexceptional)))
+ (assoc resp :body (json-decode (String. #^"[B" body charset) keyword?))
+
+ (and (not (unexceptional-status? status)) (= coerce :exceptional))
+ (assoc resp :body (json-decode (String. #^"[B" body charset) keyword?))
-(defmethod coerce-response-body
- :json-string-keys
- [_ {:keys [body status] :as resp}]
- (if (and json-enabled? (unexceptional-status? status))
- (assoc resp :body (json-decode (String. #^"[B" body "UTF-8")))
- (assoc resp :body (String. #^"[B" body "UTF-8"))))
+ :else (assoc resp :body (String. #^"[B" body charset)))
+ (assoc resp :body (String. #^"[B" body charset)))))
+
+(defmethod coerce-response-body :json [req resp]
+ (coerce-json-body req resp true))
+
+(defmethod coerce-response-body :json-string-keys [_ resp]
+ (coerce-json-body resp false))
(defmethod coerce-response-body :clojure [_ {:keys [status body] :as resp}]
(binding [*read-eval* false]
(assoc resp :body (read-string (String. #^"[B" body "UTF-8")))))
-(defmethod coerce-response-body :auto [_ {:keys [status body] :as resp}]
+(defmethod coerce-response-body :auto [_ {:keys [body coerce status] :as resp}]
(assoc resp
:body
(let [typestring (get-in resp [:headers "content-type"])]
@@ -247,13 +259,14 @@
json-enabled?)
(if-let [charset (second (re-find #"charset=(.*)"
(str typestring)))]
- (json-decode (String. #^"[B" body ^String charset) true)
- (json-decode (String. #^"[B" body "UTF-8") true))
+ (coerce-json-body resp true charset)
+ (coerce-json-body resp true "UTF-8"))
:else
(String. #^"[B" body "UTF-8")))))
-(defmethod coerce-response-body :default [as {:keys [status body] :as resp}]
+(defmethod coerce-response-body :default
+ [{:keys [as]} {:keys [status body] :as resp}]
(cond
(string? as) (assoc resp :body (String. #^"[B" body ^String as))
:else (assoc resp :body (String. #^"[B" body "UTF-8"))))
@@ -264,10 +277,10 @@
`coerce-response-body` multimethod may be extended to add
additional coercions."
[client]
- (fn [{:keys [as] :as req}]
+ (fn [req]
(let [{:keys [body] :as resp} (client req)]
(if body
- (coerce-response-body as resp)
+ (coerce-response-body req resp)
resp))))
(defn maybe-wrap-entity
@@ -295,13 +295,27 @@
(deftest ^{:integration true} t-json-output-coercion
(run-server)
(let [resp (client/get (localhost "/json") {:as :json})
+ resp-str (client/get (localhost "/json")
+ {:as :json :coerce :exceptional})
bad-resp (client/get (localhost "/json-bad")
- {:throw-exceptions false :as :json})]
- (is (= 200 (:status resp)))
+ {:throw-exceptions false :as :json})
+ bad-resp-json (client/get (localhost "/json-bad")
+ {:throw-exceptions false :as :json
+ :coerce :always})
+ bad-resp-json2 (client/get (localhost "/json-bad")
+ {:throw-exceptions false :as :json
+ :coerce :unexceptional})]
+ (is (= 200 (:status resp) (:status resp-str)))
(is (= {:foo "bar"} (:body resp)))
- (is (= 400 (:status bad-resp)))
+ (is (= "{\"foo\":\"bar\"}" (:body resp-str)))
+ (is (= 400
+ (:status bad-resp)
+ (:status bad-resp-json)
+ (:status bad-resp-json2)))
(is (= "{\"foo\":\"bar\"}" (:body bad-resp))
- "don't coerce on bad response status")))
+ "don't coerce on bad response status by default")
+ (is (= {:foo "bar"} (:body bad-resp-json)))
+ (is (= "{\"foo\":\"bar\"}" (:body bad-resp-json2)))))
(deftest ^{:integration true} t-ipv6
(run-server)

0 comments on commit 426049c

Please sign in to comment.