You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Compojure路由中更新后的Atom值未生效问题排查与解决求助

问题分析与解决方案

你的核心问题在于Compojure路由定义时的提前求值:当你写(route/not-found {:status 400 :body @my-atom})时,@my-atom会在app被定义的瞬间就被解析成当时的Atom值(也就是"foobar"),之后无论你怎么更新Atom,这个静态的响应map都不会再变化了。

要让路由响应Atom的动态更新,你需要让响应逻辑延迟到每次请求时才执行——也就是把响应变成一个函数,而不是静态的map。

修正后的基础写法

直接把not-found的响应改成一个接收请求参数的函数,每次请求过来时才去解引用Atom:

(ns mveroute
  (:require [org.httpkit.server :as srv]
            [compojure.core :as cmpj]
            [compojure.route :as route]
            [clj-http.client :as client])
  (:gen-class))

(def my-atom (atom "foobar"))

;; 把not-found的响应改成函数,每次请求时才获取Atom最新值
(def app (cmpj/routes
           (route/not-found (fn [_req] {:status 400 :body @my-atom}))))

(defn -main [& args]
  (reset! my-atom "hello world")
  (srv/run-server #'app {:port 8005})
  (println @my-atom) ; 输出"hello world"
  ;; 现在会输出"hello world"了
  (-> "http://localhost:8005"
      (client/get {:throw-exceptions? false})
      :body println))

这样修改后,每次收到404请求时,都会调用这个匿名函数,此时才会读取Atom的当前值,自然就能拿到更新后的内容。

关于你尝试的wrap-routes问题

你之前的写法里,(route/not-found (atom-body))是直接调用了atom-body函数,相当于提前把它的返回值(初始Atom值对应的map)绑定到路由里了。正确的做法是直接传递函数本身,不要加括号调用:

(defn atom-body [_req] {:status 200 :body @my-atom})
(cmpj/defroutes wrap-found (route/not-found atom-body)) ; 这里直接传函数,不要调用
(def app (cmpj/wrap-routes #'wrap-found {}))

这样路由收到请求时,才会执行atom-body函数,获取最新的Atom值。

针对你的最终目标(CLI设置资源根目录)

如果要实现动态设置静态文件根目录的功能,你可以把根目录存在Atom里,然后写一个动态读取该目录的handler:

(def root-dir (atom "./default-public")) ; 默认根目录

(defn static-file-handler [req]
  (let [file-path (str @root-dir (:uri req))
        file (clojure.java.io/file file-path)]
    (cond
      (.exists file) {:status 200 :body (slurp file)}
      :else {:status 404 :body (str "File not found in root: " @root-dir)})))

(def app (cmpj/routes
           (cmpj/GET "/*" [] static-file-handler) ; 处理所有静态文件请求
           (route/not-found (fn [_] {:status 404 :body (str "404 - Root dir: " @root-dir)}))))

;; 在CLI启动时可以通过参数更新根目录
(defn -main [& args]
  (when-let [new-root (first args)]
    (reset! root-dir new-root))
  (srv/run-server #'app {:port 8005})
  (println "Serving files from:" @root-dir))

这样启动时传参数clj -m mveroute ./my-custom-public,就能动态切换静态文件根目录,后续请求都会使用新目录。


内容的提问来源于stack exchange,提问作者Will

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 18:27:31