使用Ring设置Session时POST请求无法返回JSON响应问题
问题:Ring设置Session时返回JSON响应触发错误
使用ring.util.response设置Session时,POST请求返回JSON(字典)会报错,但返回纯文本字符串响应正常,其他GET路由返回JSON负载无问题。
相关代码
Session处理函数代码
(ns tool.session (:require [tool.config :refer [env]] [clj-http.client :as client] [clojure.tools.cli :refer [parse-opts]] [clojure.tools.logging :as log] [clojure.string :as s] [cheshire.core :refer :all] [slingshot.slingshot :refer [try+, throw+]] [ring.util.codec] [ring.util.response :refer [response]] [mount.core :as mount]) ) ;; Functions for user web session related access (defn set-session-user! [business-external-id data-preferences-uuid {session :session}] (-> (response (str "Business id set to: " business-external-id) ) ;; (-> (response {} ) ;; PUTTING this line results in the error below. Whereas returning a plaintext string response works fine. (assoc :session (assoc session :business_external_id business-external-id)) (assoc :session (assoc session :data_preferences_uuid data-preferences-uuid)) (assoc :headers {"Content-Type" "text/plain"})))
路由处理代码
(defn log-in-to-business [{:keys [body-params] :as req}] (def business-uuid (:business_uuid body-params)) (def data-preferences-uuid (:data_preferences_uuid body-params)) (session/set-session-user! business-uuid data-preferences-uuid req) )
报错信息
2022-09-21 00:51:21,838 [XNIO-1 task-2] ERROR io.undertow.request - UT005071: Undertow request failed HttpServerExchange{ POST /login/session-post} java.lang.UnsupportedOperationException: Body class not supported: class clojure.lang.PersistentArrayMap at ring.adapter.undertow.response$eval20252$fn__20253.invoke(response.clj:46) at ring.adapter.undertow.response$eval20207$fn__20208$G__20198__20215.invoke(response.clj:11) at ring.adapter.undertow.response$set_exchange_response.invokeStatic(response.clj:59) at ring.adapter.undertow.response$set_exchange_response.invoke(response.clj:52) at ring.adapter.undertow$handle_request.invokeStatic(undertow.clj:23) at ring.adapter.undertow$handle_request.invoke(undertow.clj:19) at ring.adapter.undertow$undertow_handler$fn$reify__20336.handleRequest(undertow.clj:41) at io.undertow.server.session.SessionAttachmentHandler.handleRequest(SessionAttachmentHandler.java:68) at io.undertow.server.Connectors.executeRootHandler(Connectors.java:387) at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:852) at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35) at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:2019) at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1558) at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1449) at org.xnio.XnioWorker$WorkerThreadFactory$1$1.run(XnioWorker.java:1280) at java.base/java.lang.Thread.run(Thread.java:833)
问题原因及解决方法
原因分析
- Undertow适配器无法直接处理Clojure的Map作为响应体,错误提示明确指出不支持
PersistentArrayMap类型的响应体。GET路由能正常返回JSON,大概率是因为路由链中配置了ring-json这类中间件自动序列化Map,但该POST请求的响应未经过此中间件处理。 - 代码中两次调用
assoc :session会导致后一次覆盖前一次的修改,因为每次都是基于原始session对象操作,而非已经更新过的版本。 - 返回JSON时仍设置
Content-Type为text/plain,既不符合响应类型,也会导致客户端无法正确解析内容。
修复步骤
1. 正确合并Session属性
使用一次assoc同时设置多个Session属性,避免覆盖:
(assoc session :business_external_id business-external-id :data_preferences_uuid data-preferences-uuid)
2. 手动序列化JSON并设置正确Header
如果未使用自动序列化中间件,用cheshire.core/generate-string将Map转为JSON字符串,同时设置Content-Type为application/json:
(defn set-session-user! [business-external-id data-preferences-uuid {session :session}] (-> (response (generate-string {:status "success" :business_id business-external-id})) (assoc :session (assoc session :business_external_id business-external-id :data_preferences_uuid data-preferences-uuid)) (assoc :headers {"Content-Type" "application/json"})))
3. 利用中间件自动序列化(推荐)
如果应用中已配置ring-json的wrap-json-response中间件,确保该中间件作用于当前路由,直接返回Map即可,无需手动处理序列化:
(defn set-session-user! [business-external-id data-preferences-uuid {session :session}] (-> (response {:status "success" :business_id business-external-id}) (assoc :session (assoc session :business_external_id business-external-id :data_preferences_uuid data-preferences-uuid))))
4. 修正路由函数的变量定义
路由中使用def定义变量是不良实践,改用let创建局部变量:
(defn log-in-to-business [{:keys [body-params] :as req}] (let [business-uuid (:business_uuid body-params) data-preferences-uuid (:data_preferences_uuid body-params)] (session/set-session-user! business-uuid data-preferences-uuid req)))
内容的提问来源于stack exchange,提问作者rasen58
相关产品推荐
相关产品推荐

