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

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

问题原因及解决方法

原因分析

  1. Undertow适配器无法直接处理Clojure的Map作为响应体,错误提示明确指出不支持PersistentArrayMap类型的响应体。GET路由能正常返回JSON,大概率是因为路由链中配置了ring-json这类中间件自动序列化Map,但该POST请求的响应未经过此中间件处理。
  2. 代码中两次调用assoc :session会导致后一次覆盖前一次的修改,因为每次都是基于原始session对象操作,而非已经更新过的版本。
  3. 返回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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 01:20:40