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
相关产品推荐
相关产品推荐

