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

Clojure中Compojure POST处理器如何接收edn、处理cljs-http传的map

全链路Map传输最佳实践(Reagent + cljs-http + Compojure)

基础概念澄清

  • EDN本质:Clojure生态原生的结构化数据序列化格式,和JSON同属跨端可传输的文本格式,但天生适配Clojure的内置数据结构,不需要额外适配转换。
  • 常用序列化函数的适用边界:
    • pr-str:将Clojure结构化数据转换为可被解析的EDN纯字符串,无额外换行
    • prn-str:和pr-str功能完全一致,仅末尾多一个换行符,不影响后续解析
    • read-string:将合规EDN字符串反向解析为Clojure结构化数据,生产环境需对输入做校验,禁止直接解析不可信来源的EDN内容
  • cljs-http的内置参数逻辑:
    • :edn-params是请求配置项的键,取值直接传原始Clojure map即可,库会自动完成“map→EDN字符串”的转换、自动设置正确的Content-Type: application/edn请求头,完全不需要手动调用pr-str/prn-str提前转字符串。
    • 对应同逻辑的参数还有:json-params、:transit-params,都是接收原始map,自动完成对应格式的序列化。
  • 常见语法错误纠正:(http/post "/test" (:edn-params (prn-str {:hey "there"})))存在两层问题:一是语法结构错误,http/post的第二个参数是完整的请求配置map,而非单独的参数键;二是逻辑错误,手动将map转为字符串再传给:edn-params会造成双重序列化,最终请求发出的内容会是嵌套转义的字符串,无法被后端正确解析。

可直接落地的实现代码

前端(Reagent环境)正确请求写法

(ns your-app.core
  (:require [cljs-http.client :as http]
            [cljs.core.async :refer [go <!]]))

;; 向后端发送map的正确流程
(go
  (let [response (<! (http/post "/test"
                        {:edn-params {:hey "there"
                                      :user-id 123
                                      :tags #{:cljs :reagent}}}))]
    ;; 因为后端返回的是EDN格式,cljs-http会自动将body解析为Clojure map
    (println "后端响应:" (:body response))))

注:之前遇到的“请求中:data字段是对象而非字符串”的问题,本质是手动提前做了序列化,导致cljs-http的自动序列化逻辑重复执行,把本该是EDN字符串的内容当成普通对象参数包了一层。

后端(Compojure环境)正确处理器实现

原有代码的核心问题是:Compojure默认只会自动解析JSON、表单格式的请求body,EDN格式的请求需要手动读取并解析,之前用的read (:body req)是Java IO流的读取方法,完全不是EDN解析逻辑。
正确实现:

(ns your-app.handler
  (:require [compojure.core :refer :all]
            [compojure.route :as route]
            [clojure.edn :as edn]))

(defroutes handler
  (GET "/about" [] "<h1>Hello World</h1>")
  (POST "/test" req
    (let [;; 读取请求体中的EDN原始字符串
          edn-str (slurp (:body req))
          ;; 解析为Clojure map,生产环境需限定白名单reader避免安全风险
          request-map (edn/read-string {:readers {'object identity}} edn-str)]
      ;; 此处可直接将request-map传入数据库存储逻辑,结构和前端发送的map完全一致
      (prn "收到的前端数据:" request-map)
      ;; 返回EDN格式的响应,供前端自动解析
      {:status 200
       :headers {"Content-Type" "application/edn"}
       :body (pr-str {:success true :stored-data request-map})}))
  (route/not-found "<h1>Page not found</h1>"))

EDN对比JSON的全栈优势

在纯Clojure/script全栈场景下,EDN相比JSON的实际收益非常明确:

  • 零结构转换损耗:Clojure原生的keyword、set、symbol等数据类型可以直接序列化传输,使用JSON时keyword会被转为普通字符串、set会被转为数组,接收端需要额外写适配逻辑才能还原结构。
  • 零样板代码:前端传什么结构的map,后端解析后就是完全一致的map,不需要编写DTO类、不需要处理键名格式的转换逻辑。
  • 传输性能相当:EDN和JSON的文本体积、传输速率在同类场景下差异极小,没有可感知的性能损耗。

高频踩坑排查

  • 不要手动调用pr-str/prn-str传给:edn-params:该参数仅接收原始Clojure map,序列化动作由库自动完成,手动转换必然造成双重序列化问题。
  • 不要用clojure.core/read解析EDN:该函数是用于解析Clojure源码的,存在任意代码执行风险,生产环境必须使用clojure.edn/read-string,且提前配置允许的reader白名单。
  • 响应头必须匹配内容格式:后端返回EDN内容时,必须设置Content-Type: application/edn,否则cljs-http会默认按JSON逻辑解析响应体,拿到乱码字符串。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.22 17:36:04