【问题标题】:How do I bind a dynamic variable?如何绑定动态变量?
【发布时间】:2018-06-12 08:43:41
【问题描述】:

如何在 compojure 中绑定动态变量?请看我下面的例子,这里的 request-id 是为每个 api 请求生成的唯一 uuid。我希望能够在后续的日志记录方法中访问此请求 ID。我尝试使用绑定功能,但仍然无法访问 some-page/some-method 中的 request-id

handler.clj

(ns some_app.handler
  (:require
    [compojure.api.sweet :refer :all]
    [compojure.route :as route]
    [some_api.some_page :as some-page]))

(def ^:dynamic *request-id*
  nil)

(defn ^:private generate-request-id []
  (str (java.util.UUID/randomUUID)))

(def app
  (binding [*request-id* (generate-request-id)]
    (api
      (context "/api" [] (GET "/some-page" [] (some-page/some-method))))))

some-page.clj

(ns some_app.some_page
(:require
        [clojure.tools.logging :as log]))

(def some-method []
  (log/info {:request-id *request-id*}))

【问题讨论】:

    标签: clojure binding compojure compojure-api


    【解决方案1】:

    动态绑定是一种很好的方法,随着时间的推移,它会随着代码库的增长而变得异常庞大,至少与将有关请求的数据存储在自身的请求中相比是如此。

    环模型鼓励将有关请求的内容作为数据直接存储在请求中,而不是存储在元数据或绑定变量之类的环境中。

    (defn with-request-id 
      [f]
      (fn [request]
          (f (assoc request :request-id (generate-request-id)))
    

    那么您就不必担心线程绑定的保存位置或其他类似问题。

    【讨论】:

      【解决方案2】:

      在您的some_app.some_page 命名空间中,您需要require 声明*request-id* 的命名空间。比如:

      (ns some_app.some_page
        (:require
          [clojure.tools.logging :as log]
          [some_app.handler :as hndlr))
      

      那你可以参考*request-id*like:

      (def some-method []
        (log/info {:request-id hndlr/*request-id*}))
      

      【讨论】:

      • 我不认为这会起作用,因为它会创建一个循环依赖。
      • Freid001 -- 你说的很对。我读得不够仔细。但是,您仍然需要为some_app.some_page 命名空间提供一种方法来引用该变量。
      • 是的,所以我能够通过将变量存储在 some_page 和 handler 都可以访问的公共共享命名空间中来解决这个问题。
      【解决方案3】:

      这里对绑定的调用放错了地方。绑定应该在处理请求时生效,而不是在构建 app/api 时。

      您希望有一些中间件来执行此操作:

      (defn with-request-id 
        [f]
        (fn [request]
          (binding [*request-id* (generate-request-id)]
            (f request)))
      
      (def app
        (with-request-id
          (api ... ))
      

      另见Ring Concepts

      【讨论】:

      • 这仍然给了我同样的错误 线程“main”中的异常 java.lang.RuntimeException: Unable to resolve symbol: request-id
      • 这是一个编译器错误,告诉您应该在正确的命名空间中引用*request-id*