原文:https://github.com/weavejester/compojure/wiki
compojure
Compojure是一个在Ring基础上开发出的小型路由库,可使得web程序由小而独立的部分组成。使用时,在project.clj文件添加依赖:
[compojure "1.1.5"]
Getting Started
使用compojure最简单的方式是使用clojure构建工具leiningen。如果你还没有,那就先去下载安装leiningen。为了使得我们下面的说明能正常运行,需要使用leiningen 2.0.0或者最近版本。
那么,我们使用"compojure"模板创建一个新工程:
lein new compojure hello-world
这创建了一个基本的包含小型web程序的工程骨架。
现在可以cd工程目录,使用leiningen启动开发服务器:
cd hello-world
lein ring server
这个服务器将使用一个没有被占用的端口并打开一个浏览器窗口。如果改动了其中的源文件(src目录下),服务器将为你自动重载。
Routes In Detail
Compojure的路由类似这样:
(GET "/user/:id" [id]
(str "<h1>Hello user " id "</h1>"))
Routes(路由)返回Ring handler函数。撇除语法来看,没有什么神奇之处。他们(Routes)仅仅提供了定义函数的简洁方式,以处理HTTP请求。
Matching the HTTP method
我们拆解这个语法。第一个符号:
GET
这是Compojure提供的几个路由宏之一。这个宏检测HTTP请求方法,如果方法不是”GET“,函数返回nil。
其他可用宏有POST、PUT、DELETE、OPTIONS、PATCH和HEAD。如果想匹配任意的HTTP方法,使用 ANY 宏。
Matching the URI
下一符号:
"/user/:id"
这是Clout定义的使用路由语法的一个字符串。这在Ruby on Rails和Sinatra更常见一些。
它匹配请求的URI。”:id“将匹配”/“或者”.“下的任意子路径,并且将结果置于"id"参数中。
如果我们想更具体一些,还可以为这个参数定义一个自定义的正则表达式:
["/user/:id", :id #"[0-9]+"]
对于HTTP方法,如果URI匹配不到任何自定义路径,那么路由函数将返回nil。
Destructuring the request
在HTTP方法和URI匹配之后:
[id]
宏的第二个参数提供了从请求map中检索信息的方法。它可能是一个由参数组成的向量,也或者其他完整的clojure结构形式。
换言之,上述语法绑定符号”id“到请求map中的参数”id“,这种情形更受Clout路由串欢迎。我们也可以使用一个标准得Clojure结构形式:
{{id :id} :params}
这能提供更多控制权,但不如向量语法简洁。
Returning a response
一旦HTTP请求被匹配和结构,路由的其他部分包装成隐式的行为就会被阻止,而仅仅像正常的函数:
(译者注:此处翻译牵强,不过大概意思应该能明白)
(str "<h1>Hello user " id "</h1>"))
返回值被很智能地处理。这种情况下返回字符串,它被转换成标准的response:
{:status 200
:headers {"Content-Type" "text/html; charset=utf-8"}
:body "<h1>Hello user 1</h1>"}
compojure.response/render 将任意一种类型(String、map、File等)的返回值处理成合适的response。它也支持你使用自定义类型重写。
Destructuring Syntax
Compojure提供了俩种解构:
1、clojure类型的,可能使用let 等特殊形式(special form) 。
2、compojure特定类型的,被设计用于解析请求map:
;查询字符串(和形式)参数
;URL路径的部分
*注意,为清楚起见,下面提供了一个REPL示范。
Regular Clojure Destrucuring
如果你应用一个map或者符号,Clojure的解构语法将会用在Ring请求map上面。例如,此处使用Clojure语法绑定一个特定的参数到一个变量:
(GET "/:foo" {{foo :foo} :params}
(str "Foo = " foo))
Compojure-specific Destructuring
鉴于通常的解构相当繁琐,Compojure提供了一个专门的解构形式。若你应用一个向量,Compojure将使用这种定制的解构语法。上面的例子能更加简洁:
(GET "/:foo" [foo]
(str "Foo = " foo))
Compojure参数解构语法有三类功能。第一直接绑定参数到相同名字的符号。例如,假设我们有如下请求map:
{:request-method :get
:uri "/foobar"
:headers {}
:params {:x "foo", :y "bar", :z "baz", :w "qux"}}
然后,我们可以使用一个符号组成的向量绑定每个参数:
[x y z]
x -> "foo"
y -> "bar"
z -> "baz"
要绑定所有未分配参数的map,可以使用符号 & ,&后面跟一个变量名:
[x y & z]
x -> "foo"
y -> "bar"
z -> {:z "baz", :w "qux"}
这种行为跟在正常的clojure绑定中的 & 类似。不同之处在于,对获得未绑定部分,我们用map代替了list。
最后,你可以使用 :as 关键字将整个请求map分配给一个符号:
[x y :as r]
x -> "foo"
y -> "bar"
r -> {:request-method :get
:uri "/foobar"
:headers {}
:params {:x "foo", :y "bar", :z "baz", :w "qux"}}
你也可以绑定一个clojure解构map到 :as 关键字:
[x y :as {u :uri}]
x -> "foo"
y -> "bar"
u -> "/foobar"}
REPL Demonstration
注释标签 ;;->后面是路由呼叫响应:
user> (use 'compojure.core)
nil
user> (require '[compojure.handler :as handler])
nil
user> (require '[compojure.route :as route])
nil
user> (def my-request
{:request-method :get
:uri "/my-uri"
:headers []
:params {:x "foo" :y "bar" :z "baz" :w "qux"}})
#'user/my-request
user> (defroutes my-3-parameter-route
(GET "/:my" [x y z]
(str "x -> " x "; "
"y -> " y "; "
"z -> " z)))
#'user/my-3-parameter-route
user> (my-3-parameter-route my-request)
;;-> {:status 200
;;-> :headers {"Content-Type" "text/html; charset=utf-8"},
;;-> :body "x -> foo; y -> bar; z -> baz"}
user> (defroutes my-2-parameter-and-remainder-route
(GET "/:my" [x y & z] ; & binds remainder of request map to z
(str "x -> " x "; "
"y -> " y "; "
"z -> " z)))
#'user/my-2-parameter-and-remainder-route
user> (my-2-parameter-and-remainder-route my-request)
;;-> {:status 200,
;;-> :headers {"Content-Type" "text/html; charset=utf-8"},
;;-> :body "x -> foo; y -> bar; z -> {:my \"my-uri\", :z \"baz\", :w \"qux\"}"}
user> (defroutes my-remainder-symbol-route
(GET "/:my" [x y :as r] ; :as keyword assigns entire request map to symbol
(str "x -> " x "; "
"y -> " y "; "
"r -> " r)))
#'user/my-remainder-symbol-route
user> (my-remainder-symbol-route my-request)
;;-> {:status 200,
;;->:headers {"Content-Type" "text/html; charset=utf-8"},
;;->:body "x -> foo; y -> bar;
;;-> r -> {:route-params {:my \"my-uri\"},
;;-> :request-method :get,
;;-> :uri \"/my-uri\",
;;-> :headers [],
;;-> :params {:my \"my-uri\",
;;-> :z \"baz\",
;;-> :y \"bar\",
;;-> :x \"foo\",
;;-> :w \"qux\"}}"}
user> (defroutes my-destructuring-map-route
(GET "/:my" [x y :as {u :uri}] ; :as keyword and destructuring map
; to bind :uri value to u
(str "x -> " x "; "
"y -> " y "; "
"u -> " u)))
#'user/my-destructuring-map-route
user> (my-destructuring-map-route my-request)
;;-> {:status 200,
;;-> :headers {"Content-Type" "text/html; charset=utf-8"},
;;-> :body "x -> foo; y -> bar; u -> /my-uri"}
Nesting routes
Routes可以嵌套使用context宏:
(defroutes api-routes
(GET "/something" [] ...)) ; matches /api/something
(defroutes main-routes
(context "/api" [] api-routes)
other-routes)