当前位置: 首页 > 工具软件 > Compojure > 使用案例 >

compojure-clojure

顾宏朗
2023-12-01

原文: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)


 类似资料: