【问题标题】:Unable to parse out EDN in this server request无法解析此服务器请求中的 EDN
【发布时间】:2015-07-24 10:37:10
【问题描述】:

我认为我做对了,但我无法将我的 EDN 从 :body 输入流中取出。 Ring 和 Compojure 处理程序是这样的:

依赖:

 [ring.middleware.params :as params]
 [ring.middleware.edn :as edn]; https://github.com/tailrecursion/ring-edn

处理程序:

(defroutes ajax-example
  (PUT "/ajax-example" r
       (println r)
       {:status 200
        :headers {"Content-Type" "application/edn"}
        :body "yo"}))

我把它包装成这样:

  (def ajax-wrapped
   (-> ajax-example
      edn/wrap-edn-params
      params/wrap-params))

println'd 响应正确地显示它是 EDN,并且根据我发送的简单测试地图,内容长度是正确的,但地图本身无处可寻,它永远被困在输入流中:body... 怎么弄?

这是响应 println:

{:ssl-client-cert nil, :remote-addr 0:0:0:0:0:0:0:1, :params {}, :route-params {}, :headers {origin @987654321 @, 主机 localhost:6542, 用户代理 Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36, content-type application/edn, cookie _ga= GA1.1.1354518981.1429622648; content-length 20, referer http://localhost:6542/, connection keep-alive, accept /, accept-language en-US,en;q=0.8,sq;q=0.6, accept-encoding gzip, deflate , sdch, 缓存控制 max-age=0}, :server-port 6542, :content-length 20, :form-params {}, :query-params {}, :content-type application/edn, :character- encoding nil, :uri /ajax-example, :server-name localhost, :query-string nil, :body #, :edn-params nil, :scheme :http, :request-method :put}

:body 上面没有正确粘贴,看起来像这样:

 [open corner bracket] HttpInput org.eclipse.jetty.server.HttpInput@5d969109 [end corner bracket]

使用cljs-ajax lib 从浏览器发送的客户端代码是:

(defn ajax-send
  []
  (let [push {:adder1 2 :add2 3}]
    (PUT "/ajax-example" 
         {:format :edn 
          :params push
          :handler ajax-result
          :error-handler error-handler})))

以下是其中一个答案建议的测试输出:

hf.examples> ((-> ajax-example  params/wrap-params edn/wrap-edn-params) (-> (mock/request :put "/ajax-example")
                                                             (mock/content-type "application/edn")
                                                             (mock/body "{:hello \"there\"}"))) 
{:status 200,
 :headers {"Content-Type" "application/edn"},
 :body
 "{:remote-addr \"localhost\", :params {:hello \"there\"}, :route-params {}, :headers {\"content-length\" \"16\", \"content-type\" \"application/edn\", \"host\" \"localhost\"}, :server-port 80, :content-length 16, :form-params {}, :query-params {}, :content-type \"application/edn\", :uri \"/ajax-example\", :server-name \"localhost\", :query-string nil, :edn-params {:hello \"there\"}, :scheme :http, :request-method :put}"}
hf.examples> 

我也试过这个:

(defroutes ajax-example
  (PUT "/ajax-example" r
       {:status 200
        :headers {"Content-Type" "application/edn"}
        :body (pr-str (dissoc r :body))}))

卷曲结果独立于前端:

curl -X PUT -H "Content-Type: application/edn" -d '{:name :barnabas}' http://localhost:6542/ajax-example
{:ssl-client-cert nil, :remote-addr "0:0:0:0:0:0:0:1", :params {}, :route-params {}, :headers {"host" "localhost:6542", "content-length" "17", "content-type" "application/edn", "user-agent" "curl/7.37.1", "accept" "*/*"}, :server-port 6542, :content-length 17, :content-type "application/edn", :character-encoding nil, :uri "/ajax-example", :server-name "localhost", :query-string nil, :edn-params nil, :scheme :http, :request-method

content-length(共 17 个)与通过 Curl 传递的映射中的字符数匹配。但是edn-params 是零!内容在哪里?

【问题讨论】:

    标签: clojure ring


    【解决方案1】:

    编辑:作为更新问题的答案,wrap-edn-params 函数通过完全读取 :body InputStream 来消耗请求的正文。 Compojure 路由将请求传递给每个参数处理程序,直到返回非 nil 值。在这种情况下,作为第一个处理程序传递给路由的任何处理程序都将消耗 :body 并且将没有 :body 值供第二个处理程序使用,从而导致 wrap-edn-params 读取的主体值为零。

    传递给环处理程序的请求可能没有将其内容类型设置为 edn。 wrap-edn-params function 只会在请求内容类型设置为 edn 时解析 edn。

    此外,解析后的edn参数只会被wrap-edn-params函数放在请求映射的:params和:edn-params键中,因此:body不应该用于访问解析后的edn。

    (require '[ring.mock.request :as mock])
    (require '[ring.middleware.edn :as edn]) 
    
    ((-> ajax-example  params/wrap-params edn/wrap-edn-params) (-> (mock/request :put "/ajax-example")
                                                                 (mock/content-type "application/edn")
                                                                 (mock/body "{:hello \"there\"}"))) 
    
    {:remote-addr "localhost",
     :params {:hello "there"},
     :route-params {},
     :headers
     {"content-length" "16",
      "content-type" "application/edn",
      "host" "localhost"},
     :server-port 80,
     :content-length 16,
     :form-params {},
     :query-params {},
     :content-type "application/edn",
     :uri "/ajax-example",
     :server-name "localhost",
     :query-string nil,
     :body #<ByteArrayInputStream java.io.ByteArrayInputStream@171788d8>,
     :edn-params {:hello "there"},
     :scheme :http,
     :request-method :put}
    

    【讨论】:

    • 请求显示正确的 EDN 内容类型,我在前端使用 cljs-ajax 使其成为 EDN。关于正文,我没有直接使用它,我正在显示整个请求图。其中 edn 和 params 为 nil
    • 删除包装函数并打印正文(slurp (:body r))时的输出是什么?
    • 当我这样做时它什么也不打印。这个怎么可能?表示身体是空的。但是同一请求中的内容长度是正确的,显示的长度与我发送的 EDN 映射中的字符数相匹配。
    • 您能否将相关的客户端代码添加到您的问题中?
    • 使用客户端提供的 ajax-send 方法对我来说工作正常。您是否尝试过像我的回答中所做的那样使用模拟环请求调用 ajax-wrapped?
    【解决方案2】:

    我已经解决了这个问题,但我不知道为什么会出现问题。我有另一条独立的路线,也包裹了 EDN 中间件。这是可重现的示例:

    (defroutes example-routes2
      (PUT "/test-edn" r
           (println r)
           {:status 200
            :headers {"Content-Type" "application/edn"}
            :body (pr-str (str "request is: " r))}))
    
    (defroutes ajax-example
      (PUT "/ajax-example" r
           (println r)
           {:status 200
            :headers {"Content-Type" "application/edn"}
            :body (pr-str (str "request is: " r))}))
    
    (def edn-routes 
      (-> example-routes2
          edn/wrap-edn-params))
    
    (def ajax-wrapped
      (-> ajax-example
          edn/wrap-edn-params))
    
    ;;combining all routes on this page into a single handler
    (def example-routes (routes example-routes1  ajax-wrapped edn-routes))
    

    请注意,这两条路线本质上是相同的,并且包装相同。 未能将 EDN 解析为 edn-params 的是example-routes def! 中列出的第二个!

    curl -X PUT -H "Content-Type: application/edn" -d '{:name :barnabasss}' http://localhost:6542/test-edn

    返回:

    "请求是:{:ssl-client-cert nil, :remote-addr \"0:0:0:0:0:0:0:1\", :params {}, :route-params { }, :headers {\"host\" \"localhost:6542\", \"content-length\" \"19\", \"content-type\" \"application/edn\", \"user-代理\" \"curl/7.37.1\", \"accept\" \"/\"}, :server-port 6542, :content-length 19, :content-type \" application/edn\", :character-encoding nil, :uri \"/test-edn\", :server-name \"localhost\", :query-string nil, :body #, :edn-params无, :scheme :http, :request-method :put}"

    curl -X PUT -H "Content-Type: application/edn" -d '{:name :barnabasss}' http://localhost:6542/ajax-example

    返回:

    “请求是:{:ssl-client-cert nil, :remote-addr \"0:0:0:0:0:0:0:1\", :params {:name :barnabasss}, :路由参数 {}, :headers {\"host\" \"localhost:6542\", \"content-length\" \"19\", \"content-type\" \"application/edn\", \"user-agent\" \"curl/7.37.1\", \"accept\" \"/\"}, :server-port 6542, :content-length 19, :content -type \"application/edn\", :character-encoding nil, :uri \"/ajax-example\", :server-name \"localhost\", :query-string nil, :body #, :edn-params {:name :barnabasss}, :scheme :http, :request-method :put}"

    example-routes 中切换它们的顺序,切换哪个有效。谁能解释一下?

    【讨论】:

    • wrap-edn-params 通过完全读取 :body InputStream 来消耗正文。 Compojure 路由将请求传递给每个参数处理程序,直到返回非 nil 值。在这种情况下,作为第一个处理程序传递给路由的任何处理程序都将消耗 :body 并且将没有 :body 值供第二个处理程序消耗,从而导致 wrap-edn-params 读取的主体值为零。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-03
    • 2016-04-10
    • 1970-01-01
    • 2018-06-21
    • 2013-11-01
    • 2016-06-09
    相关资源
    最近更新 更多