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

使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 05:15:29