Reitit异步拦截器出现Unsupported Context on :enter错误求助
问题场景
我正在开发一款基于Reitit的HTTP应用,使用拦截器实现业务逻辑。但在拦截器的enter函数中包裹go宏后,调用接口触发错误;移除go宏则代码正常运行。
问题代码
(ns ic.reitit-interceptor-multithreading) (require '[reitit.ring :as ring]) (require '[reitit.http :as http]) (require '[reitit.interceptor.sieppari :as sieppari]) (require '[clojure.core.async :refer [go]]) (defn interceptor [number] {:enter (fn [ctx] (go (println "in enter interceptor" number (.getName (Thread/currentThread))) (update-in ctx [:request :number] (fnil + 0) number)))}) (def app (http/ring-handler (http/router ["/api" {:interceptors [(interceptor 1)]} ["/number" {:interceptors [(interceptor 10)] :get {:interceptors [(interceptor 100)] :handler (fn [req] (println "in handler") {:status 200 :body (select-keys req [:number])})}}]]) ;; the default handler (ring/create-default-handler) ;; executor {:executor sieppari/executor}))
触发错误
在REPL执行以下代码:
(require '[ic.reitit-interceptor-multithreading :refer :all]) (app {:request-method :get, :uri "/api/number"})
错误信息:
Loading src/ic/reitit_interceptor_multithreading.clj... done
(app {:request-method :get, :uri "/api/number"})
Execution error (ExceptionInfo) at sieppari.core/-invalid-context-type! (core.cljc:34).
Unsupported Context on :enter - clojure.core.async.impl.channels.ManyToManyChannel@4abe39b8
in enter interceptor 1 async-dispatch-3
问题原因
Reitit默认使用的Sieppari拦截器执行器,仅支持返回同步上下文对象或特定异步类型(如Future、Manifold Deferred),但go宏返回的是core.async的Channel对象,Sieppari无法直接识别处理这种类型,因此抛出错误。
解决方案
根据需求选择以下两种方式:
方式1:转换core.async Channel为Sieppari支持的类型
如果必须使用core.async的异步逻辑,需要将Channel转为Sieppari兼容的异步类型,推荐使用Manifold库处理:
首先添加Manifold依赖到项目,然后修改拦截器:
(ns ic.reitit-interceptor-multithreading) (require '[reitit.ring :as ring]) (require '[reitit.http :as http]) (require '[reitit.interceptor.sieppari :as sieppari]) (require '[clojure.core.async :refer [go]]) (require '[manifold.deferred :as d]) (defn interceptor [number] {:enter (fn [ctx] ;; 将core.async Channel转为Manifold Deferred (d/from-source (go (println "in enter interceptor" number (.getName (Thread/currentThread))) (update-in ctx [:request :number] (fnil + 0) number))))}) ;; 其余代码保持不变...
如果不想引入Manifold,也可以用<!!阻塞获取Channel结果(不推荐高并发场景,会阻塞线程):
(defn interceptor [number] {:enter (fn [ctx] (let [async-result (go (println "in enter interceptor" number (.getName (Thread/currentThread))) (update-in ctx [:request :number] (fnil + 0) number))] ;; 阻塞获取异步结果 (<!! async-result)))})
方式2:使用Sieppari原生支持的异步方式
如果不需要core.async的复杂异步流程,直接用future实现多线程逻辑,Sieppari可直接处理Future类型:
(defn interceptor [number] {:enter (fn [ctx] (future (println "in enter interceptor" number (.getName (Thread/currentThread))) (update-in ctx [:request :number] (fnil + 0) number)))})
内容的提问来源于stack exchange,提问作者user51

