使用clj-http测试本地POST请求时遭遇403错误求助
问题描述
我在HttpKit服务器中定义了POST路由,但使用clj-http.client测试时返回403(禁止访问)错误,GET路由却能正常工作。
最小复现示例
server.clj
(ns api.server (:require [org.httpkit.server :as server] [compojure.core :refer [defroutes GET POST]] [ring.middleware.defaults :as default]) (defn auth-start [req] {:status 200 :headers {"Content-Type" "text/html"} :body "<h1> logged-in </h1>"}) (defroutes app-routes (GET "/" [] "<h1> Hello </h1>") (POST "/login" [req] auth-start) (route/not-found "Error, page not found!")) (defn -main "This is our main entry point" [& args] (let [port (Integer/parseInt (or (env :PORT) "3000"))] ; Run the server with Ring.defaults middleware (server/run-server (default/wrap-defaults #'app-routes default/site-defaults) {:port port}) ; Run the server without ring defaults ;(server/run-server #'app-routes {:port port}) (println (str "Running webserver at http://127.0.0.1:" port "/"))))
test.clj
(ns api.test (:require [clj-http.client :as client])) (client/post "http://localhost:3000/login" {})
project.clj依赖
:dependencies [[org.clojure/clojure "1.11.1"] ; Compojure - A basic routing library [compojure "1.6.1"] ; Our Http library for client/server [http-kit "2.3.0"] ; Ring defaults - for query params etc [ring/ring-defaults "0.3.2"] ; For testing api routes [clj-http "3.12.3"]]
解决办法
1. 处理CSRF防护问题
ring-defaults的site-defaults默认开启了CSRF防护,这是POST请求返回403的核心原因:
- 测试环境临时关闭:修改服务器启动代码,关闭anti-forgery机制:
(server/run-server (default/wrap-defaults #'app-routes (assoc default/site-defaults :security {:anti-forgery false})) {:port port}) - 生产环境适配:先通过GET请求获取CSRF令牌(通常在响应头
X-CSRF-Token或页面表单的_csrf字段),再在POST请求中携带:; 获取令牌 (def csrf-token (get-in (client/get "http://localhost:3000/") [:headers "X-CSRF-Token"])) ; 携带令牌发起请求 (client/post "http://localhost:3000/login" {:headers {"X-CSRF-Token" csrf-token}})
2. 修正路由参数定义
你的POST路由参数写法错误,应该接收整个请求对象而非提取名为req的参数:
(POST "/login" req (auth-start req))
如果auth-start不需要使用请求对象,也可以简化为:
(POST "/login" [] (auth-start))
3. 补全缺失的依赖与函数
- 添加
compojure.route命名空间引用,否则route/not-found会报错:(:require ... [compojure.route :as route]) - 替换未定义的
env函数,改用标准方式获取环境变量:(let [port (Integer/parseInt (or (System/getenv "PORT") "3000"))]
内容的提问来源于stack exchange,提问作者Jack Gee
相关产品推荐
相关产品推荐

