如何在 clojure 环服务器中将请求正文作为键映射进行访问

问题描述 投票:0回答:1

我有一个 clojure 环服务器,我想将数据发布到其中,这样我就可以在数据库中执行操作,例如添加、更新和删除用户。我目前坚持以我可以方便使用的格式接收正文(按键图)。

我有 NodeJS 背景,您可以通过添加 body-parser 中间件轻松访问请求正文。

这是代码

(ns questhero.core
  (:require
   [ring.adapter.jetty :as jetty]
   [cheshire.core :refer [generate-string]]
   [ring.middleware.cors :refer [wrap-cors]]
   [ring.util.request]
   [ring.util.io]
   [ring.middleware.defaults :refer [wrap-defaults site-defaults]]))

(defn create-user [request]
  (let [body (slurp (:body request))]
    (println "body" body)
    {:status 200
     :headers {"Content-Type" "application/json"}
     :body (generate-string {:message "Success"})}))

(defn handler [{:keys [uri] :as request}]
  (cond
    (= uri "/create-user")
    (create-user request)
    :else
    {:status 404
     :headers {"Content-Type" "application/json"}
     :body "Not Found"}))

(def allowed-origins [#"http://localhost:3000"])

(def allowed-methods [:get :post :put :delete])

(def allowed-headers #{:accept :content-type})

(def app
  (-> #'handler
      (wrap-defaults (assoc-in site-defaults [:security :anti-forgery] false))
      (wrap-cors :access-control-allow-origin allowed-origins
                               :access-control-allow-methods allowed-methods
                               :access-control-allow-headers allowed-headers)))


(defn start-server []
  (println "listening server")
  (jetty/run-jetty app {:port 4000}))

(println "starting server")
(start-server)

当我发布到创建用户路由时,正文被记录为

["^","~:fullname","myfullname","~:username","myusername","~:email","[email protected]","~:password","mypassword1234"]

如何将其转换为对象键映射?

我还希望能够通过打印到终端来检查正文的内容,这可能是嵌套地图

有关更多信息,这就是我的 clojurescript 前端请求的样子

(defn post-save-new-user [data] 
  (ajax/POST "http://localhost:4000/create-user"
    {:handler handle-response
     :error-handler handle-error
     :headers {"Accept" "application/json"
               "Content-Type" "application/json"}
     :params data}))

我尝试添加以下环形中间件wrap-params、wrap-json-body和wrap-json-params,但没有任何帮助。

我也尝试过这个answer

的步骤
clojure middleware clojurescript ring
1个回答
0
投票

如果您不想使用框架或预先编写的中间件,您可以使用 Cheshire 来解析您在处理程序中读取的正文字符串:

(ns questhero.core
  (:require
   [cheshire.core :refer [generate-string parse-string]]
   [clojure.pprint :refer [pprint]]
   [ring.adapter.jetty :as jetty]
   [ring.middleware.cors :refer [wrap-cors]]
   [ring.middleware.defaults :refer [wrap-defaults site-defaults]]
   [ring.util.request]
   [ring.util.io]))

(defn create-user [request]
  (let [body (slurp (:body request))
        body-params (parse-string body true)]
    (println "body" body)
    (pprint body-params)
    {:status 200
     :headers {"Content-Type" "application/json"}
     :body (generate-string {:message "Success"})}))

请注意,这里我已将

parse-string
添加到 Cheshire 的引用函数中,以及来自
pprint
命名空间的
clojure.pprint
。传递给
parse-string
的布尔值将所有键转换为关键字。

需要注意的一件事是,请求的主体是一个 Java InputStream,这意味着它是有状态的。如果您

slurp
它,它将被消耗,并且无法再次读取。这是考虑使用只读取一次主体的中间件的原因之一。我将在这里给您举一个例子,但这只是最基本的内容协商。它不适用于非 JSON 编码数据,但普通 URL 编码表单参数除外,这些参数由您已使用并放入的默认中间件处理
:form-params
:

(defn wrap-json-body [handler]
  (fn [request]
    (if (= (ring.util.request.content-type request)
           "application/json"))
      (let [body-params (-> request
                            :body
                            slurp
                            (parse-string true)]
        (handler (assoc request :body-params body-params)))
      (handler request))

这将像第一个示例中那样解析正文,并将解析的参数放在请求中的 :body-params 中。如果您想以相同的方式对待查询参数和 JSON 主体,您可能需要将处理程序调用交换为:

(handler (-> request
             (update :params merge body-params)
             (assoc :body-params body-params)))

现在您可以将此中间件添加到您的应用程序中:

(def app
  (-> #'handler
      (wrap-json-body)
      (wrap-defaults (assoc-in site-defaults [:security :anti-forgery] false))
      (wrap-cors :access-control-allow-origin allowed-origins
                               :access-control-allow-methods allowed-methods
                               :access-control-allow-headers allowed-headers)))
© www.soinside.com 2019 - 2024. All rights reserved.