Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

finished a la carte formats

  • Loading branch information...
commit 18cd265e955d4bc5f7dca6497573acfcbcdcf0cb 1 parent ffe8cd2
@ngrunwald authored
View
4 src/ring/middleware/format.clj
@@ -5,6 +5,8 @@
(defn wrap-restful-format
[handler & {:keys [formats]
+ :or {formats [:json :edn :yaml :yaml-in-html]}
:as options}]
(-> handler
- ))
+ (par/wrap-restful-params :formats formats)
+ (res/wrap-restful-response :formats formats)))
View
48 src/ring/middleware/format_params.clj
@@ -77,24 +77,22 @@
the Exception"
[handler & {:keys [predicate decoder charset handle-error]}]
(fn [{:keys [#^InputStream body] :as req}]
- (let [mod-req
- (try
- (if-let [byts (slurp-to-bytes body)]
- (if (predicate req)
- (let [body (:body req)
- #^String char-enc (if (string? charset) charset (charset (assoc req :body byts)))
- bstr (String. byts char-enc)
- fmt-params (decoder bstr)
- req* (assoc req
- :body-params fmt-params
- :params (merge (:params req)
- (when (map? fmt-params) fmt-params)))]
- req*)
- (assoc req :body (ByteArrayInputStream. byts)))
- req)
- (catch Exception e
- (handle-error e handler req)))]
- (handler mod-req))))
+ (try
+ (if-let [byts (slurp-to-bytes body)]
+ (if (predicate req)
+ (let [body (:body req)
+ #^String char-enc (if (string? charset) charset (charset (assoc req :body byts)))
+ bstr (String. byts char-enc)
+ fmt-params (decoder bstr)
+ req* (assoc req
+ :body-params fmt-params
+ :params (merge (:params req)
+ (when (map? fmt-params) fmt-params)))]
+ (handler req*))
+ (handler (assoc req :body (ByteArrayInputStream. byts))))
+ (handler req))
+ (catch Exception e
+ (handle-error e handler req)))))
(def json-request?
(make-type-request-pred #"^application/(vnd.+)?json"))
@@ -141,7 +139,7 @@
(safe-read-string s)))
(def clojure-request?
- (make-type-request-pred #"^application/(vnd.+)?(x-)?clojure"))
+ (make-type-request-pred #"^application/(vnd.+)?(x-)?(clojure|edn)"))
(defn wrap-clojure-params
"Handles body params in Clojure format. See wrap-format-params for details."
@@ -159,10 +157,7 @@
(def format-wrappers
{:json wrap-json-params
:edn wrap-clojure-params
- :clj wrap-clojure-params
- :clojure wrap-clojure-params
- :yaml wrap-yaml-params
- :yml wrap-yaml-params})
+ :yaml wrap-yaml-params})
(defn wrap-restful-params
"Wrapper that tries to do the right thing with the request :body and provide
@@ -172,8 +167,9 @@
:or {handle-error default-handle-error
formats [:json :edn :yaml]}}]
(reduce (fn [h format]
- (if-let [wrapper (format-wrappers (keyword format))]
+ (if-let [wrapper (if
+ (fn? format) format
+ (format-wrappers (keyword format)))]
(wrapper h :handle-error handle-error)
- (throw (java.util.UnknownFormatFlagsException.
- (format "wrap-restful-params does not recognize format %s" (keyword format))))))
+ h))
handler formats))
View
54 src/ring/middleware/format_response.clj
@@ -20,13 +20,11 @@
"Check whether encoder can encode to accepted-type.
Accepted-type should have keys :type and :sub-type with appropriate
values."
- [encoder accepted-type]
- (let [type (accepted-type :type)
- sub-type (accepted-type :sub-type)]
- (or (= "*" type)
- (and (= (get-in encoder [:enc-type :type]) type)
- (or (= "*" sub-type)
- (= (get-in encoder [:enc-type :sub-type]) sub-type))))))
+ [{:keys [enc-type] :as encoder} {:keys [type sub-type] :as accepted-type}]
+ (or (= "*" type)
+ (and (= (:type enc-type) type)
+ (or (= "*" sub-type)
+ (= (enc-type :sub-type) sub-type)))))
(defn sort-by-check
[by check headers]
@@ -66,7 +64,7 @@
(sort-by-check :sub-type "*")
(sort-by :q >)))
-(def parse-accept-header (memo-lu parse-accept-header* 100))
+(def parse-accept-header (memo-lu parse-accept-header* 500))
(defn preferred-encoder
"Return the encoder that encodes to the most preferred type.
@@ -218,6 +216,7 @@
(def format-encoders
{:json (make-encoder json/generate-string "application/json")
:edn (make-encoder generate-native-clojure "application/edn")
+ :clojure (make-encoder generate-native-clojure "application/clojure")
:yaml (make-encoder yaml/generate-string "application/x-yaml")
:yaml-in-html (make-encoder wrap-yaml-in-html "text/html")})
@@ -229,17 +228,38 @@
[handler & {:keys [handle-error formats charset]
:or {handle-error default-handle-error
charset "utf-8"
- formats [:json :yaml :edn :yaml-in-html]}}]
- (let [encoders (into []
- (map (fn [format]
- (if-let [encoder (get format-encoders (keyword format))]
- encoder
- (throw
- (java.util.UnknownFormatFlagsException.
- (format "wrap-restful-response does not recognize format %s" (keyword format))))))
- formats))]
+ formats [:json :yaml :edn :clojure :yaml-in-html]}}]
+ (let [encoders (for [format formats
+ :when format
+ :let [encoder (if (map? format)
+ format
+ (get format-encoders (keyword format)))]
+ :when encoder]
+ encoder)]
(wrap-format-response handler
:predicate serializable?
:encoders encoders
:charset charset
:handle-error handle-error)))
+
+(defn wrap-restful-response
+ "Wrapper that tries to do the right thing with the response :body
+ and provide a solid basis for a RESTful API. It will serialize to
+ JSON, YAML, Clojure or HTML-wrapped YAML depending on Accept header.
+ See wrap-format-response for more details."
+ [handler & {:keys [handle-error formats charset]
+ :or {handle-error default-handle-error
+ charset "utf-8"
+ formats [:json :yaml :edn :clojure :yaml-in-html]}}]
+ (let [encoders (for [format formats
+ :when format
+ :let [encoder (if (map? format)
+ format
+ (get format-encoders (keyword format)))]
+ :when encoder]
+ encoder)]
+ (wrap-format-response handler
+ :predicate serializable?
+ :encoders encoders
+ :charset charset
+ :handle-error handle-error)))
View
41 test/ring/middleware/test/format.clj
@@ -0,0 +1,41 @@
+(ns ring.middleware.test.format
+ (:use [clojure.test]
+ [ring.middleware.format])
+ (:require [cheshire.core :as json]
+ [clj-yaml.core :as yaml])
+ (:import [java.io ByteArrayInputStream]))
+
+(defn stream [s]
+ (ByteArrayInputStream. (.getBytes s "UTF-8")))
+
+(def restful-echo
+ (wrap-restful-format (fn [req] (assoc req :body (vals (:body-params req))))))
+
+(def restful-echo-json
+ (wrap-restful-format (fn [req] (assoc req :body (vals (:body-params req))))
+ :format [:json]))
+
+(deftest test-restful-round-trip
+ (let [ok-accept "application/edn"
+ msg {:test :ok}
+ ok-req {:headers {"accept" ok-accept}
+ :content-type ok-accept
+ :body (stream (pr-str msg))}
+ r-trip (restful-echo ok-req)]
+ (is (= (get-in r-trip [:headers "Content-Type"])
+ "application/edn; charset=utf-8"))
+ (is (= (read-string (slurp (:body r-trip))) (vals msg)))
+ (is (= (:params r-trip) msg))
+ (is (thrown? RuntimeException (restful-echo {:headers {"accept" "foo/bar"}}))))
+ (let [ok-accept "application/json"
+ msg {"test" "ok"}
+ ok-req {:headers {"accept" ok-accept}
+ :content-type ok-accept
+ :body (stream (json/encode msg))}
+ r-trip (restful-echo-json ok-req)]
+ (is (= (get-in r-trip [:headers "Content-Type"])
+ "application/json; charset=utf-8"))
+ (is (= (json/decode (slurp (:body r-trip))) (vals msg)))
+ (is (= (:params r-trip) msg))
+ (is (nil? (:body-params (restful-echo-json {:headers {"accept" "application/edn"}
+ :content-type "application/edn"}))))))
View
6 test/ring/middleware/test/format_response.clj
@@ -140,9 +140,9 @@
(def custom-restful-echo
(wrap-restful-response identity
- :default {:encoder (constantly "foobar")
- :enc-type {:type "text"
- :sub-type "foo"}}))
+ :formats [{:encoder (constantly "foobar")
+ :enc-type {:type "text"
+ :sub-type "foo"}}]))
(deftest format-custom-restful-hashmap
(let [req {:body {:foo "bar"} :headers {"accept" "text/foo"}}
Please sign in to comment.
Something went wrong with that request. Please try again.