翻译的是clojure的ring库文档,原文来自git:https://github.com/ring-clojure/ring/wiki。不知道这个之前是不是有人翻译过。初试牛刀,纰漏错误之处难免,请指正。
Ring 是一个Clojure编程语言构建web应用程序的底层接口和库。它类似于Rack之于Ruby,WSGI之于Python,或者Java的Servlet规范。
$ lein new hello-world
$ cd hello-world
(defproject hello-world "1.0.0-SNAPSHOT"
:description "FIXME: write"
:dependencies [[org.clojure/clojure "1.5.1"]
[ring/ring-core "1.2.0"]
[ring/ring-jetty-adapter "1.2.0"]])
(ns hello-world.core)
(defn handler [request]
{:status 200
:headers {"Content-Type" "text/html"}
:body "Hello World"})
$ lein repl
=> (use 'ring.adapter.jetty)
=> (use 'hello-world.core)
=> (run-jetty handler {:port 3000})
(defn what-is-my-ip [request]
{:status 200
:headers {"Content-Type" "text/plain"}
:body (:remote-addr request)})
(defn wrap-content-type [handler content-type]
(fn [request]
(let [response (handler request)]
(assoc-in response [:headers "Content-Type"] content-type))))
(def app
(wrap-content-type handler "text/html"))
(def app
(-> handler
(wrap-content-type "text/html")
(wrap-keyword-params)
(wrap-params)))
你可以手动创建ring响应maps(参照 Concepts),但是同样 ring.util.response 命名空间包含了一些有用的函数使得整个任务变得更简单。
这个 response 函数 创建了一个基础的“200 OK”响应:
(response "Hello World")
=> {:status 200
:headers {}
:body "Hello World"}
(-> (response "Hello World")
(content-type "text/plain"))
=> {:status 200
:headers {"Content-Type" "text/plain"}
:body "Hello World"}
也存在创建重定向这样特殊的函数:
(redirect "http://example.com")
=> {:status 302
:headers {"Location" "http://example.com"}
:body ""}
也或者返回静态文件或者资源:
(file-response "readme.html" {:root "public"})
=> {:status 200
:headers {}
:body (io/file "public/readme.html")}
(resource-response "readme.html" {:root "public"})
=> {:status 200
:headers {}
:body (io/input-stream (io/resource "public/readme.html"))}
这些函数的更多信息和其他内容可以点击这里 ring.util.response API documentation 。
web应用程序经常需要提供静态内容,例如图片或者CSS样式。ring提供了俩个中间件函数来应付这些。
一个 是 wrap-file 。它提供本地文件系统某个目录的静态内容:
(use 'ring.middleware.file)
(def app
(wrap-file your-handler "/var/www/public"))
(use 'ring.middleware.resource)
(def app
(wrap-resource your-handler "public"))
(use 'ring.middleware.resource
'ring.middleware.file-info)
(def app
(-> your-handler
(wrap-resource "public")
(wrap-file-info)))
(use 'ring.middleware.content-type)
(def app
(wrap-content-type your-handler))
http://example.com/style/screen.css
Content-Type: text/css
(use 'ring.middleware.content-type)
(def app
(wrap-content-type
your-handler
{:mime-types {"foo" "text/x-foo"}}))
(use 'ring.middleware.params)
(def app
(wrap-params your-handler))
{:http-method :get
:uri "/search"
:query-string "q=clojure"}
{:http-method :get
:uri "/search"
:query-string "q=clojure"
:query-params {"q" "clojure"}
:form-params {}
:params {"q" "clojure"}}
http://example.com/demo?x=hello
{"x" "hello"}
http://example.com/demo?x=hello&x=world
{"x" ["hello", "world"]}
(use 'ring.middleware.cookies)
(def app
(wrap-cookies your-handler))
{"username" {:value "alice"}}
{:status 200
:headers {}
:cookies {"username" {:value "alice"}}
:body "Setting a cookie."}
{"secret" {:value "foobar", :secure true, :max-age 3600}}
(use 'ring.middleware.session
'ring.util.response)
(defn handler [{session :session}]
(response (str "Hello " (:username session))))
(def app
(wrap-session handler))
(defn handler [{session :session}]
(let [count (:count session 0)
session (assoc session :count (inc count))]
(-> (response (str "You accessed this page " count " times."))
(assoc :session session))))
(defn handler [request]
(-> (response "Session deleted.")
(assoc :session nil)))
(def app
(wrap-session handler {:cookie-attrs {:max-age 3600}}))
(def app
(wrap-session handler {:cookie-attrs {:secure true}}))
(use 'ring.middleware.session.cookie)
(def app
(wrap-session handler {:store (cookie-store {:key "a 16-byte secret"})})
(use 'ring.middleware.session.store)
(deftype CustomStore []
SessionStore
(read-session [_ key]
(read-data key))
(write-session [_ key data]
(let [key (or key (generate-new-random-key))]
(save-data key data)
key))
(delete-session [_ key]
(delete-data key)
nil))
(use 'ring.middleware.params
'ring.middleware.multipart-params)
(def app
(-> your-handler
wrap-params
wrap-multipart-params))
用Ring开发时,你可能发现自己需要不重启开发服务器的情况下重载源文件。
有三种方式:
:plugins [[lein-ring "0.8.7"]]
lein deps
:ring {:handler your-app.core/handler}
lein ring server
:dev-dependencies [[ring-serve "0.1.2"]]
lein deps
user> (require 'your-app.core/handler)
nil
user> (use 'ring.util.serve)
nil
user> (serve your-app.core/handler)
Started web server on port 3000
user> (defonce server (run-jetty #'handler {:port 8080 :join? false}))
http://mmcgrana.github.io/ring/
https://github.com/ring-clojure/ring/wiki/Third-Party-Libraries
https://github.com/ptaoussanis/clojure-web-server-benchmarks
;; When executed, this file will run a basic web server
;; on http://localhost:8080 that will display the text
;; 'Hello World'.
(ns ring.example.hello-world
(:use ring.adapter.jetty))
(defn handler [request]
{:status 200
:headers {"Content-Type" "text/plain"}
:body "Hello World"})
(run-jetty handler {:port 8080})
;; When executed, this file will run a basic web server
;; on http://localhost:8080 that will display the text
;; 'Hello World'.
(ns ring.example.hello-world-2
(:use ring.util.response
ring.adapter.jetty))
(defn handler [request]
(-> (response "Hello World")
(content-type "text/plain")))
(run-jetty handler {:port 8080})
;; When executed, this file will run a basic web server
;; on http://localhost:8080.
(ns ring.example.params
(:use ring.middleware.params
ring.util.response
ring.adapter.jetty))
(defn page [name]
(str ""
(if name
(str "Nice to meet you, " name "!")
(str ""))
""))
(defn handler [{{name "name"} :params}]
(-> (response (page name))
(content-type "text/html")))
(def app
(-> handler wrap-params))
(run-jetty app {:port 8080})
;; When executed, this file will run a basic web server
;; on http://localhost:8080, which will tell you how many
;; times you have visited the page.
(ns ring.example.session
(:use ring.middleware.session
ring.util.response
ring.adapter.jetty))
(defn handler [{session :session, uri :uri}]
(let [n (session :n 1)]
(if (= uri "/")
(-> (response (str "You have visited " n " times"))
(content-type "text/plain")
(assoc-in [:session :n] (inc n)))
(-> (response "Page not found")
(status 404)))))
(def app
(-> handler wrap-session))
(run-jetty app {:port 8080})