Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Liberator Free your data with RFC 2616
Search
Philipp Meier
November 16, 2013
Programming
0
420
Liberator Free your data with RFC 2616
My talk on liberator given at clojure/conj 2013.
Philipp Meier
November 16, 2013
Tweet
Share
More Decks by Philipp Meier
See All by Philipp Meier
Creating Web Services with Liberator - f(by) 2016
ordnungswidrig
0
92
Liberator Free your data with RFC 2616
ordnungswidrig
0
190
Other Decks in Programming
See All in Programming
What Spring Developers Should Know About Jakarta EE
ivargrimstad
0
180
CSC305 Lecture 08
javiergs
PRO
0
220
「ちょっと古いから」って避けてた技術書、今だからこそ読もう
mottyzzz
11
6.8k
NixOS + Kubernetesで構築する自宅サーバーのすべて
ichi_h3
0
970
Devvox Belgium - Agentic AI Patterns
kdubois
1
120
Flutterで分数(Fraction)を表示する方法
koukimiura
0
130
2分台で1500examples完走!爆速CIを支える環境構築術 - Kaigi on Rails 2025
falcon8823
3
3.7k
bootcamp2025_バックエンド研修_WebAPIサーバ作成.pdf
geniee_inc
0
110
Software Architecture
hschwentner
6
2.3k
組込みだけじゃない!TinyGo で始める無料クラウド開発入門
otakakot
0
290
理論と実務のギャップを超える
eycjur
0
140
(Extension DC 2025) Actor境界を越える技術
teamhimeh
1
260
Featured
See All Featured
Facilitating Awesome Meetings
lara
56
6.6k
Testing 201, or: Great Expectations
jmmastey
45
7.7k
Agile that works and the tools we love
rasmusluckow
331
21k
Intergalactic Javascript Robots from Outer Space
tanoku
273
27k
Site-Speed That Sticks
csswizardry
13
900
Java REST API Framework Comparison - PWX 2021
mraible
34
8.9k
Leading Effective Engineering Teams in the AI Era
addyosmani
7
450
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
162
15k
Building Applications with DynamoDB
mza
96
6.7k
Side Projects
sachag
455
43k
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
132
19k
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
230
22k
Transcript
Liberator Free your data with RFC 2616 Philipp Meier Software
Developer http://philipp.meier.name/ @ordnungswprog clojure/conj 2013 Alexandria, VA http://www.fickr.com/photos/aidanmorgan/3352417491/
None
Liberator A clojure library to implement REST resources
Liberator A clojure library to implement REST resources ...on the
server side.
What is HTTP?
What is HTTP? • Request and response • Headers and
body • Methods • Status codes • Content negotiation • Conditional request • And more... • Request and response • Headers and body • Methods • Status codes • Content negotiation • Conditional request • And more...
HTTP Methods Methods
HTTP Methods 8 Methods 8
HTTP Methods 8 RFC 2616 GET, PUT, POST, DELETE, HEAD,
OPTIONS, TRACE, CONNECT Methods 8 RFC 2616 GET, PUT, POST, DELETE, HEAD, OPTIONS, TRACE, CONNECT
HTTP Methods 8 RFC 2616 GET, PUT, POST, DELETE, HEAD,
OPTIONS, TRACE, CONNECT Methods 8 RFC 2616 GET, PUT, POST, DELETE, HEAD, OPTIONS, TRACE, CONNECT
HTTP Methods 8+1 RFC 2616 PATCH (RFC 5789) Methods 8+1
RFC 2616 PATCH (RFC 5789)
HTTP Methods 8+1+7 RFC 2616 PATCH (RFC 5789) WEBDAV (RFC
4918) Methods 8+1+7 RFC 2616 PATCH (RFC 5789) WEBDAV (RFC 4918)
HTTP Header Fields Header Fields
HTTP Header Fields 47 Header Fields 47
HTTP Header Fields 47 Accept … WWW-Authenticate Header Fields 47
Accept … WWW-Authenticate
HTTP Status codes Status codes
HTTP Status codes 5 Classes Status codes 5 Classes
HTTP Status codes 1xx Informational 2xx Successful 3xx Redirection 4xx
Client Error 5xx Server error Status codes 1xx Informational 2xx Successful 3xx Redirection 4xx Client Error 5xx Server error
HTTP Status codes Status codes
HTTP Status codes 41 Status codes 41
HTTP Status codes 41 (RFC 2616) Status codes 41 (RFC
2616)
HTTP Status codes 41+1 (RFC 2616) WEBDAV (RFC 5842) Status
codes 41+1 (RFC 2616) WEBDAV (RFC 5842)
HTTP Status codes 41+1+1 (RFC 2616) WEBDAV (RFC 5842) HTCPCP
(RFC 2324) Status codes 41+1+1 (RFC 2616) WEBDAV (RFC 5842) HTCPCP (RFC 2324)
HTTP Status codes 41+1+1 (RFC 2616) WEBDAV (RFC 5842) HTCPCP
(RFC 2324) Status codes 41+1+1 (RFC 2616) WEBDAV (RFC 5842) HTCPCP (RFC 2324) I‘m a Teapot
HTTP Status codes 41 Status codes 41
HTTP Status codes (< 41 26) Maximum # of decisions
Still 40 branching points Status codes (< 41 26) Maximum # of decisions Still 40 branching points
Ring {:request-method … :headers … :body … …} {:status …
:headers … :body …} {:request-method … :headers … :body … …} {:status … :headers … :body …}
Ring (→ {:request-method … :headers … :body … …} (ring-handler))
=> {:status … :headers … :body …} (→ {:request-method … :headers … :body … …} (ring-handler)) => {:status … :headers … :body …}
Ring (fn [req] ???) ;; => {:status … ;; :headers
… ;; :body … } (fn [req] ???) ;; => {:status … ;; :headers … ;; :body … }
Ring (fn [req] (cond … {:status 503} … {:status 501}
… {:status 403} … {:status 405} …)) (fn [req] (cond … {:status 503} … {:status 501} … {:status 403} … {:status 405} …))
Ring (fn [req] (cond ? {:status 503} … {:status 501}
… {:status 403} … {:status 405} …)) (fn [req] (cond ? {:status 503} … {:status 501} … {:status 403} … {:status 405} …))
Ring (fn [req] (cond ? {:status 503} ? {:status 501}
… {:status 403} … {:status 405} …)) (fn [req] (cond ? {:status 503} ? {:status 501} … {:status 403} … {:status 405} …))
Ring (fn [req] (cond ? {:status 503} ? {:status 501}
? {:status 403} ? {:status 405} …)) (fn [req] (cond ? {:status 503} ? {:status 501} ? {:status 403} ? {:status 405} …))
Ring (defn handler [request] {:status 200 :headers {"Content-Type" "text/html"} :body
"a event"}) (defn handler [request] {:status 200 :headers {"Content-Type" "text/html"} :body "a event"})
Ring (defn handler [{m :request-method {id "id"} :params}] (if (=
:get m) {:status 200 :headers {"Content-Type" "text/plain; charset=UTF-8"} :body (pr-str (find-session id))} {:status 405 :body "Method not allowed."})) (defn handler [{m :request-method {id "id"} :params}] (if (= :get m) {:status 200 :headers {"Content-Type" "text/plain; charset=UTF-8"} :body (pr-str (find-session id))} {:status 405 :body "Method not allowed."}))
Ring (defn handler [{m :request-method {id "id"} :params}] (if (=
:get m) (if-let [e (find-session id)] {:status 200 :headers {"Content-Type" "text/plain; charset=UTF-8"} :body (pr-str (find-session id))} {:status 404 :body "Not found."}) {:status 405 :body "Method not allowed."})) (defn handler [{m :request-method {id "id"} :params}] (if (= :get m) (if-let [e (find-session id)] {:status 200 :headers {"Content-Type" "text/plain; charset=UTF-8"} :body (pr-str (find-session id))} {:status 404 :body "Not found."}) {:status 405 :body "Method not allowed."}))
Ring (defn handler [{m :request-method {id "id"} :params {accept "accept"}
:headers}] (if (= :get m) (if-let [e (find-session id)] (if-let [mt (some #(when (or (nil? accept) (= accept "*/*") (.contains accept %)) %) ["application/clojure" "text/html"])] (condp = mt "application/clojure" {:status 200 :headers {"Content-Type" "application/clojure; charset=UTF-8"} :body (pr-str e)} "text/html" {:status 200 :headers {"Content-Type" "text/html; charset=UTF-8"} :body (apply format "<html><h1>%s</h1><p>%s</p><p><small>%s</small></p></html>" ((juxt :title :time :speaker) e)) }) {:status 406 :body "Requested media type not acceptable."}) {:status 404 :body "Not found."}) {:status 505 :body "Method not allowed."})) (defn handler [{m :request-method {id "id"} :params {accept "accept"} :headers}] (if (= :get m) (if-let [e (find-session id)] (if-let [mt (some #(when (or (nil? accept) (= accept "*/*") (.contains accept %)) %) ["application/clojure" "text/html"])] (condp = mt "application/clojure" {:status 200 :headers {"Content-Type" "application/clojure; charset=UTF-8"} :body (pr-str e)} "text/html" {:status 200 :headers {"Content-Type" "text/html; charset=UTF-8"} :body (apply format "<html><h1>%s</h1><p>%s</p><p><small>%s</small></p></html>" ((juxt :title :time :speaker) e)) }) {:status 406 :body "Requested media type not acceptable."}) {:status 404 :body "Not found."}) {:status 505 :body "Method not allowed."}))
Ring (defn handler [{m :request-method {id "id"} :params {accept "accept"
ims "if-modified-since"} :headers}] (if (#{:get :post :put :delete :head :options :trace} m) (if (= :get m) (if-let [e (find-session id)] (if-let [mt (some #(when (or (nil? accept) (= accept "*/*") (.contains accept %)) %) ["application/clojure" "text/html"])] (let [last-modified (begin-of-last-minute) cache-headers {"Last-Modified" (.format (http-date-format) last-modified) "Vary" "Accept"}] (if (and ims (let [imsd (.parse (http-date-format) ims)] (= last-modified imsd))) {:status 304 :headers cache-headers} (condp = mt "application/clojure" {:status 200 :headers (assoc cache-headers "Content-Type" "application/clojure; charset=UTF-8") :body (pr-str e)} "text/html" {:status 200 :headers (assoc cache-headers "Content-Type" "text/html; charset=UTF-8") :body (apply format "<html><h1>%s</h1><p>%s</p><p><small>%s</small></p></html>" ((juxt :title :time :speaker) e)) }))) {:status 406 :body "Requested media type not acceptable."}) {:status 404 :body "Not found."}) {:status 501 :body "Not implemented."}) {:status 405 :body "Method not allowed."})) (defn handler [{m :request-method {id "id"} :params {accept "accept" ims "if-modified-since"} :headers}] (if (#{:get :post :put :delete :head :options :trace} m) (if (= :get m) (if-let [e (find-session id)] (if-let [mt (some #(when (or (nil? accept) (= accept "*/*") (.contains accept %)) %) ["application/clojure" "text/html"])] (let [last-modified (begin-of-last-minute) cache-headers {"Last-Modified" (.format (http-date-format) last-modified) "Vary" "Accept"}] (if (and ims (let [imsd (.parse (http-date-format) ims)] (= last-modified imsd))) {:status 304 :headers cache-headers} (condp = mt "application/clojure" {:status 200 :headers (assoc cache-headers "Content-Type" "application/clojure; charset=UTF-8") :body (pr-str e)} "text/html" {:status 200 :headers (assoc cache-headers "Content-Type" "text/html; charset=UTF-8") :body (apply format "<html><h1>%s</h1><p>%s</p><p><small>%s</small></p></html>" ((juxt :title :time :speaker) e)) }))) {:status 406 :body "Requested media type not acceptable."}) {:status 404 :body "Not found."}) {:status 501 :body "Not implemented."}) {:status 405 :body "Method not allowed."}))
Ring (defn handler [{m :request-method {id "id"} :params {accept "accept"
ims "if-modified-since"} :headers}] (if (#{:get :post :put :delete :head :options :trace} m) (if (= :get m) (if-let [e (find-session id)] (if-let [mt (some #(when (or (nil? accept) (= accept "*/*") (.contains accept %)) %) ["application/clojure" "text/html"])] (let [last-modified (begin-of-last-minute) cache-headers {"Last-Modified" (.format (http-date-format) last-modified) "Vary" "Accept"}] (if (and ims (let [imsd (.parse (http-date-format) ims)] (= last-modified imsd))) {:status 304 :headers cache-headers} (condp = mt "application/clojure" {:status 200 :headers (assoc cache-headers "Content-Type" "application/clojure; charset=UTF-8") :body (pr-str e)} "text/html" {:status 200 :headers (assoc cache-headers "Content-Type" "text/html; charset=UTF-8") :body (apply format "<html><h1>%s</h1><p>%s</p><p><small>%s</small></p></html>" ((juxt :title :time :speaker) e)) }))) {:status 406 :body "Requested media type not acceptable."}) {:status 404 :body "Not found."}) {:status 501 :body "Not implemented."}) {:status 405 :body "Method not allowed."})) (defn handler [{m :request-method {id "id"} :params {accept "accept" ims "if-modified-since"} :headers}] (if (#{:get :post :put :delete :head :options :trace} m) (if (= :get m) (if-let [e (find-session id)] (if-let [mt (some #(when (or (nil? accept) (= accept "*/*") (.contains accept %)) %) ["application/clojure" "text/html"])] (let [last-modified (begin-of-last-minute) cache-headers {"Last-Modified" (.format (http-date-format) last-modified) "Vary" "Accept"}] (if (and ims (let [imsd (.parse (http-date-format) ims)] (= last-modified imsd))) {:status 304 :headers cache-headers} (condp = mt "application/clojure" {:status 200 :headers (assoc cache-headers "Content-Type" "application/clojure; charset=UTF-8") :body (pr-str e)} "text/html" {:status 200 :headers (assoc cache-headers "Content-Type" "text/html; charset=UTF-8") :body (apply format "<html><h1>%s</h1><p>%s</p><p><small>%s</small></p></html>" ((juxt :title :time :speaker) e)) }))) {:status 406 :body "Requested media type not acceptable."}) {:status 404 :body "Not found."}) {:status 501 :body "Not implemented."}) {:status 405 :body "Method not allowed."}))
Ring (defn handler [{m :request-method {id "id"} :params {accept "accept"
ims "if-modified-since"} :headers}] (if (#{:get :post :put :delete :head :options :trace} m) (if (= :get m) (if-let [e (find-session id)] (if-let [mt (some #(when (or (nil? accept) (= accept "*/*") (.contains accept %)) %) ["application/clojure" "text/html"])] (let [last-modified (begin-of-last-minute) cache-headers {"Last-Modified" (.format (http-date-format) last-modified) "Vary" "Accept"}] (if (and ims (let [imsd (.parse (http-date-format) ims)] (= last-modified imsd))) {:status 304 :headers cache-headers} (condp = mt "application/clojure" {:status 200 :headers (assoc cache-headers "Content-Type" "application/clojure; charset=UTF-8") :body (pr-str e)} "text/html" {:status 200 :headers (assoc cache-headers "Content-Type" "text/html; charset=UTF-8") :body (apply format "<html><h1>%s</h1><p>%s</p><p><small>%s</small></p></html>" ((juxt :title :time :speaker) e)) }))) {:status 406 :body "Requested media type not acceptable."}) {:status 404 :body "Not found."}) {:status 501 :body "Not implemented."}) {:status 405 :body "Method not allowed."})) (defn handler [{m :request-method {id "id"} :params {accept "accept" ims "if-modified-since"} :headers}] (if (#{:get :post :put :delete :head :options :trace} m) (if (= :get m) (if-let [e (find-session id)] (if-let [mt (some #(when (or (nil? accept) (= accept "*/*") (.contains accept %)) %) ["application/clojure" "text/html"])] (let [last-modified (begin-of-last-minute) cache-headers {"Last-Modified" (.format (http-date-format) last-modified) "Vary" "Accept"}] (if (and ims (let [imsd (.parse (http-date-format) ims)] (= last-modified imsd))) {:status 304 :headers cache-headers} (condp = mt "application/clojure" {:status 200 :headers (assoc cache-headers "Content-Type" "application/clojure; charset=UTF-8") :body (pr-str e)} "text/html" {:status 200 :headers (assoc cache-headers "Content-Type" "text/html; charset=UTF-8") :body (apply format "<html><h1>%s</h1><p>%s</p><p><small>%s</small></p></html>" ((juxt :title :time :speaker) e)) }))) {:status 406 :body "Requested media type not acceptable."}) {:status 404 :body "Not found."}) {:status 501 :body "Not implemented."}) {:status 405 :body "Method not allowed."}))
Liberate all the data!
Liberator is extracted decision fow
Liberator is extracted decision fow
Liberator is extracted decision fow
Demo
Liberator (defresource event :available-media-types ["text/html" "application/json" "application/edn"] :exists? (fn [{{{id
"id"} :params} :request}] (if-let [talk (find-talk id)] {::talk talk})) :handle-ok ::talk :last-modified (fn [_] (begin-of-last-minute)) :etag (fn [{{{id "id"} :params} :request}] (apply str (reverse (str id id))))) (defresource event :available-media-types ["text/html" "application/json" "application/edn"] :exists? (fn [{{{id "id"} :params} :request}] (if-let [talk (find-talk id)] {::talk talk})) :handle-ok ::talk :last-modified (fn [_] (begin-of-last-minute)) :etag (fn [{{{id "id"} :params} :request}] (apply str (reverse (str id id)))))
Remember? (defn handler [{m :request-method {id "id"} :params {accept "accept"
ims "if-modified-since"} :headers}] (if (#{:get :post :put :delete :head :options :trace} m) (if (= :get m) (if-let [e (find-session id)] (if-let [mt (some #(when (or (nil? accept) (= accept "*/*") (.contains accept %)) %) ["application/clojure" "text/html"])] (let [last-modified (begin-of-last-minute) cache-headers {"Last-Modified" (.format (http-date-format) last-modified) "Vary" "Accept"}] (if (and ims (let [imsd (.parse (http-date-format) ims)] (= last-modified imsd))) {:status 304 :headers cache-headers} (condp = mt "application/clojure" {:status 200 :headers (assoc cache-headers "Content-Type" "application/clojure; charset=UTF-8") :body (pr-str e)} "text/html" {:status 200 :headers (assoc cache-headers "Content-Type" "text/html; charset=UTF-8") :body (apply format "<html><h1>%s</h1><p>%s</p><p><small>%s</small></p></html>" ((juxt :title :time :speaker) e)) }))) {:status 406 :body "Requested media type not acceptable."}) {:status 404 :body "Not found."}) {:status 501 :body "Not implemented."}) {:status 405 :body "Method not allowed."})) (defn handler [{m :request-method {id "id"} :params {accept "accept" ims "if-modified-since"} :headers}] (if (#{:get :post :put :delete :head :options :trace} m) (if (= :get m) (if-let [e (find-session id)] (if-let [mt (some #(when (or (nil? accept) (= accept "*/*") (.contains accept %)) %) ["application/clojure" "text/html"])] (let [last-modified (begin-of-last-minute) cache-headers {"Last-Modified" (.format (http-date-format) last-modified) "Vary" "Accept"}] (if (and ims (let [imsd (.parse (http-date-format) ims)] (= last-modified imsd))) {:status 304 :headers cache-headers} (condp = mt "application/clojure" {:status 200 :headers (assoc cache-headers "Content-Type" "application/clojure; charset=UTF-8") :body (pr-str e)} "text/html" {:status 200 :headers (assoc cache-headers "Content-Type" "text/html; charset=UTF-8") :body (apply format "<html><h1>%s</h1><p>%s</p><p><small>%s</small></p></html>" ((juxt :title :time :speaker) e)) }))) {:status 406 :body "Requested media type not acceptable."}) {:status 404 :body "Not found."}) {:status 501 :body "Not implemented."}) {:status 405 :body "Method not allowed."}))
Liberator (defresource event :available-media-types ["text/html" "application/json" "application/edn"] :exists? (fn [{{{id
"id"} :params} :request}] (if-let [talk (find-talk id)] {::talk talk})) :handle-ok ::talk :last-modified (fn [_] (begin-of-last-minute)) :etag (fn [{{{id "id"} :params} :request}] (apply str (reverse (str id id))))) (defresource event :available-media-types ["text/html" "application/json" "application/edn"] :exists? (fn [{{{id "id"} :params} :request}] (if-let [talk (find-talk id)] {::talk talk})) :handle-ok ::talk :last-modified (fn [_] (begin-of-last-minute)) :etag (fn [{{{id "id"} :params} :request}] (apply str (reverse (str id id)))))
Liberator (defresource event :available-media-types (fn [ctx] …) :exists? (fn [ctx]
…) :handle-ok (fn [ctx] …) :last-modified (fn [ctx] …) :etag (fn [ctx] …)) (defresource event :available-media-types (fn [ctx] …) :exists? (fn [ctx] …) :handle-ok (fn [ctx] …) :last-modified (fn [ctx] …) :etag (fn [ctx] …))
Liberator (defresource event :available-media-types (fn [ctx] …) :exists? (fn [ctx]
…) :handle-ok (fn [ctx] …) :last-modified (fn [ctx] …) :etag (fn [ctx] …)) (defresource event :available-media-types (fn [ctx] …) :exists? (fn [ctx] …) :handle-ok (fn [ctx] …) :last-modified (fn [ctx] …) :etag (fn [ctx] …))
Liberator (defresource event :available-media-types (fn [ctx] …) :exists? (fn [ctx]
…) :handle-ok (fn [ctx] …) :last-modified (fn [ctx] …) :etag (fn [ctx] …)) (defresource event :available-media-types (fn [ctx] …) :exists? (fn [ctx] …) :handle-ok (fn [ctx] …) :last-modified (fn [ctx] …) :etag (fn [ctx] …)) Essential Complexity Essential Complexity
Liberator (defresource event :available-media-types ["text/html" "application/json" "application/edn"] :exists? (fn [{{{id
"id"} :params} :request}] (if-let [talk (find-talk id)] {::talk talk})) :handle-ok ::talk :last-modified (fn [_] (begin-of-last-minute)) :etag (fn [{{{id "id"} :params} :request}] (apply str (reverse (str id id))))) (defresource event :available-media-types ["text/html" "application/json" "application/edn"] :exists? (fn [{{{id "id"} :params} :request}] (if-let [talk (find-talk id)] {::talk talk})) :handle-ok ::talk :last-modified (fn [_] (begin-of-last-minute)) :etag (fn [{{{id "id"} :params} :request}] (apply str (reverse (str id id)))))
Liberator’s execution model
Context
Context State of the computation State of the computation
Context (defresource event :available-media-types (fn [ctx] …) :exists? (fn [ctx]
…) :handle-ok (fn [ctx] …) :last-modified (fn [ctx] …) :etag (fn [ctx] …)) (defresource event :available-media-types (fn [ctx] …) :exists? (fn [ctx] …) :handle-ok (fn [ctx] …) :last-modified (fn [ctx] …) :etag (fn [ctx] …))
Context {:request {:request-method :get :params …}}} {:request {:request-method :get :params
…}}}
Context {:request {:request-method :get :params …}} ::talk {:speaker „Philipp Meier“…}}
{:request {:request-method :get :params …}} ::talk {:speaker „Philipp Meier“…}}
Context {:request {:request-method :get :params …}} ::talk {:speaker „Philipp Meier“…}
:representation {:media-type „application/edn“ :language „de“ :charset „UTF-8“ :encoding „deflate“}} {:request {:request-method :get :params …}} ::talk {:speaker „Philipp Meier“…} :representation {:media-type „application/edn“ :language „de“ :charset „UTF-8“ :encoding „deflate“}}
Context {:request {:request-method :get :params …}} ::talk {:speaker „Philipp Meier“…}
:representation {:media-type „application/edn“ :language „de“ :charset „UTF-8“ :encoding „deflate“}} {:request {:request-method :get :params …}} ::talk {:speaker „Philipp Meier“…} :representation {:media-type „application/edn“ :language „de“ :charset „UTF-8“ :encoding „deflate“}}
Representation {:media-type „application/edn“ :language „de“ :charset „UTF-8“ :encoding „deflate“} {:media-type
„application/edn“ :language „de“ :charset „UTF-8“ :encoding „deflate“}
Representation {:media-type „application/edn“ :language „de“ :charset „UTF-8“ :encoding „deflate“} {:media-type
„application/edn“ :language „de“ :charset „UTF-8“ :encoding „deflate“}
Representation {:media-type „application/edn“ :language „de“ :charset „UTF-8“ :encoding „deflate“} {:media-type
„application/edn“ :language „de“ :charset „UTF-8“ :encoding „deflate“}
Representation {:media-type „application/edn“ :language „de“ :charset „UTF-8“ :encoding „deflate“} {:media-type
„application/edn“ :language „de“ :charset „UTF-8“ :encoding „deflate“}
Representation {:media-type „application/edn“ :language „de“ :charset „UTF-8“ :encoding „deflate“} {:media-type
„application/edn“ :language „de“ :charset „UTF-8“ :encoding „deflate“}
Representation {:media-type „application/edn“ :language „de“ :charset „UTF-8“ :encoding „deflate“} {:media-type
„application/edn“ :language „de“ :charset „UTF-8“ :encoding „deflate“}
Representation {:media-type „application/edn“ :language „de“ :charset „UTF-8“ :encoding „deflate“} (defprotocol
Representation (as-response [x ctx]) {:media-type „application/edn“ :language „de“ :charset „UTF-8“ :encoding „deflate“} (defprotocol Representation (as-response [x ctx])
Representation (defprotocol Representation (as-response [x ctx]) String File InputStream map
seq (defprotocol Representation (as-response [x ctx]) String File InputStream map seq
Representation (defprotocol Representation (as-response [x ctx]) String File {:body x}
InputStream map seq (defprotocol Representation (as-response [x ctx]) String File {:body x} InputStream map seq
Representation (defprotocol Representation (as-response [x ctx]) String File InputStream map
seq (defprotocol Representation (as-response [x ctx]) String File InputStream map seq
Representation map seq edn json text/plain (html) (text/csv) ... map
seq edn json text/plain (html) (text/csv) ...
Representation Do your own implementation Do your own implementation
Representation (defmethod render-map-generic "foo/bar" [foo _] (str „my foo is
„ (:color foo)) (defmethod render-map-generic "foo/bar" [foo _] (str „my foo is „ (:color foo))
Representation (defresource custom-representation :available-media-types [„application/json“ „foo/bar“] :exists? (fn [_] {::foo
{:color :green}}) :handle-ok ::foo) (defresource custom-representation :available-media-types [„application/json“ „foo/bar“] :exists? (fn [_] {::foo {:color :green}}) :handle-ok ::foo)
More methods GET POST PUT DELETE
Representation Escape Hatch Escape Hatch
Representation Escape Hatch Fall back to plain ring response Escape
Hatch Fall back to plain ring response
Representation Escape Hatch Fall back to plain ring response (defresource
custom-representation :available-media-types [„foo/bar“] :exists? (fn [_] {::foo {:color :green}}) :handle-ok (fn [{foo ::foo}] (ring-response (str „foo is (:color foo))))) Escape Hatch Fall back to plain ring response (defresource custom-representation :available-media-types [„foo/bar“] :exists? (fn [_] {::foo {:color :green}}) :handle-ok (fn [{foo ::foo}] (ring-response (str „foo is (:color foo)))))
Representation Escape Hatch Extend multimethod as-response Escape Hatch Extend multimethod
as-response
Representation cheshire data.fressian clj-ical image/png enlive, hiccup, selmer … cheshire
data.fressian clj-ical image/png enlive, hiccup, selmer …
More methods
POST :exists? :existed? :respond-with-entity? :new? :post-redirect? :can-post-to-missing? :processable? :post! :handle-ok
:handle-created :exists? :existed? :respond-with-entity? :new? :post-redirect? :can-post-to-missing? :processable? :post! :handle-ok :handle-created
POST :exists? :not-found :existed? :gone :respond-with-entity? :no-content :new? :created :post-redirect?
:see-other :can-post-to-missing? :not-found :processable? 422 :post! side-effect! :handle-ok :handle-created :exists? :not-found :existed? :gone :respond-with-entity? :no-content :new? :created :post-redirect? :see-other :can-post-to-missing? :not-found :processable? 422 :post! side-effect! :handle-ok :handle-created
POST :exists? :not-found :existed? :gone :respond-with-entity? :no-content :new? :created :post-redirect?
:see-other :can-post-to-missing? :not-found :processable? 422 :post! side-effect! :handle-ok :handle-created ... :exists? :not-found :existed? :gone :respond-with-entity? :no-content :new? :created :post-redirect? :see-other :can-post-to-missing? :not-found :processable? 422 :post! side-effect! :handle-ok :handle-created ...
PUT :exists? :not-found :existed? :gone :respond-with-entity? :no-content :new? :created :can-put-to-missing?
:not-found :conflict? :conflict :processable? 422 :put! side-effect! :handle-ok :handle-created ... :exists? :not-found :existed? :gone :respond-with-entity? :no-content :new? :created :can-put-to-missing? :not-found :conflict? :conflict :processable? 422 :put! side-effect! :handle-ok :handle-created ...
Integration
Integration It’s a ring handler It’s a ring handler
Integration It’s a ring handler Ring routing libraries compojure, clout
It’s a ring handler Ring routing libraries compojure, clout
Integration It’s a ring handler Ring routing libraries compojure, clout
Middleware It’s a ring handler Ring routing libraries compojure, clout Middleware
Integration (defresource foo… ) (defresource bar… ) (→ (routes (ANY
„/foo“ [] foo) (ANY „/bar/:id“ [id] (bar id)) wrap-dosync wrap-keyword-params wrap-params (wrap-trace :ui) wrap-stacktrace) (defresource foo… ) (defresource bar… ) (→ (routes (ANY „/foo“ [] foo) (ANY „/bar/:id“ [id] (bar id)) wrap-dosync wrap-keyword-params wrap-params (wrap-trace :ui) wrap-stacktrace)
Future Patch Request body processing Custom decision fows Whatever Patch
Request body processing Custom decision fows Whatever
Questions?
Liberator Free your data with RFC 2616 Philipp Meier Software
Developer http://philipp.meier.name/ @ordnungswprog clojure/conj 2013 Alexandria, VA http://www.fickr.com/photos/aidanmorgan/3352417491/
http://clojure-liberator.github.io/liberator https://github.com/ordnungswidrig/liberator-presentation http://clojure-liberator.github.io/liberator https://github.com/ordnungswidrig/liberator-presentation Thank you Philipp Meier Software Developer
http://philipp.meier.name/ @ordnungswprog clojure/conj 2013 Alexandria, VA