【问题标题】:Can anybody explain why Compojure's routing macro only accepts literal vector as route description parameter?谁能解释为什么 Compojure 的路由宏只接受文字向量作为路由描述参数?
【发布时间】:2013-09-03 18:38:03
【问题描述】:

compojure.core/GET 接受描述 URI 的字符串或描述正则表达式的向量作为路由定义参数,以匹配从 URI 中提取的参数以及 URI 本身。你可以在这里找到文档Routes in Detail

来自我的 REPL(从头开始,leiningen 的 project.clj 在这个问题的末尾)

user> (use '[compojure.core :only [GET]])
nil
user> (def h (GET "/" [] "hello"))
#'user/h
user> (h {:uri "/" :request-method :get})
{:status 200, :headers {"Content-Type" "text/html; charset=utf-8"}, :body "hello"}

到目前为止一切顺利,普通的香草路线

以下是使用向量进行路由的示例:

user> (def h2 (GET ["/foo/:id" :id #"[0-9]+"] [] "hello from foo"))
#'user/h2
user> (h2 {:uri "/foo/123" :request-method :get})
{:status 200, :headers {"Content-Type" "text/html; charset=utf-8"}, :body "hello from foo"}
user> (h2 {:uri "/foo/abc" :request-method :get})
nil

一切都还好;你可以看到它只匹配数字:id的请求

现在我不明白的部分来了:

user> (def v ["/foo/:id" :id #"[0-9]+"])
#'user/v
user> (def h3 (GET v [] "hello again from foo"))
#'user/h3
user> (h3 {:uri "/foo/abc" :request-method :get})
IllegalArgumentException No implementation of method: :route-matches of protocol: #'clout.core/Route found for class: clojure.lang.PersistentVector  clojure.core/-cache-protocol-fn (core_deftype.clj:541)

当处理程序使用变量而不是文字向量定义时,任何人都可以解释处理程序在评估时失败的原因吗?

如果你想复制确切的环境,这是我的 leiningen 项目文件 project.clj:

(defproject cljlab "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.5.1"]
                 [org.clojure/clojure-contrib "1.2.0"]
                 [compojure "1.1.5"]])

【问题讨论】:

    标签: clojure compojure


    【解决方案1】:

    问题在于 compojure 的 GET 是一个宏,而不是一个函数。宏在编译时运行,所以它看不到“v”的值是什么。查看为 h2 和 h3 生成的代码的区别:

    user> (pprint (macroexpand-1 '(GET ["/foo/:id" :id #"[0-9]+"] [] "hello from foo")))
          (compojure.core/make-route
          :get
          (clout.core/route-compile "/foo/:id" {:id #"[0-9]+"})
          (clojure.core/fn
             [request__1858__auto__]
             (compojure.core/let-request
               [[] request__1858__auto__]
               "hello from foo")))
    
    user> (pprint (macroexpand-1 '(GET v [] "hello again from foo")))
          (compojure.core/make-route
          :get
          (if (clojure.core/string? v) (clout.core/route-compile v) v)
          (clojure.core/fn
            [request__1858__auto__]
            (compojure.core/let-request
              [[] request__1858__auto__]
              "hello again from foo")))
    

    在第一种情况下,宏能够将路由拆分为其组成部分并对其进行转换。在第二种情况下,宏只看到“v”符号。

    【讨论】:

    • 这并不能完全回答这个问题。在第二种情况下,您可以看到运行时代码已编译为处理作为字符串的v(具有全球范围)。那么,为什么不同样包括一个向量的运行时测试并执行(route-compile (first v) (apply hash-map (rest v))
    • 换句话说,(def s "/") 后跟 ((GET s [] "hello") {:uri "/" :request-method :get}) 的工作原理是编译的运行时代码。那么,为什么允许这个而不是向量的情况呢?
    • 我无法回答为什么 Compojure 的作者决定不在运行时进行矢量检查,但可能是对 Compojure 的疏忽或被认为是不必要的复杂性
    • 你说得对,我们无法猜测意图,只是好奇是否有明显的原因。我插入了运行时检查并没有发现任何损坏的东西(还)。坦率地说,我更惊讶于 对运行时字符串进行检查,而不是对向量没有检查。
    • 这有点晚了,但这是 Compojure 中的一个错误。它应该处理向量和字符串。
    猜你喜欢
    • 2012-10-11
    • 2017-04-17
    • 1970-01-01
    • 2018-12-04
    • 2016-06-01
    • 2021-09-30
    • 2017-11-24
    • 1970-01-01
    • 2022-07-01
    相关资源
    最近更新 更多