如何将 http.kit 服务器与吊桥环处理程序一起使用?

How to use http.kit server with drawbridge ring handler?

我有以下服务器代码:

(ns tweet-sentiment.server
(:require [clojure.java.io :as io]
            [tweet-sentiment.dev :refer [is-dev? inject-devmode-html browser-repl start-figwheel start-less]]
            [compojure.core :refer [GET POST defroutes]]
            [compojure.route :refer [resources]]
            [net.cgrand.enlive-html :refer [deftemplate]]
            [net.cgrand.reload :refer [auto-reload]]
            [ring.middleware.reload :as reload]
            [ring.middleware.defaults :refer [wrap-defaults api-defaults]]
            [tweet-sentiment.utils :refer [generate-response]]
            [environ.core :refer [env]]
            [org.httpkit.server :refer [run-server]]
            [ring.middleware.edn :refer [wrap-edn-params]]
            [tweet-sentiment.tweets :refer [tweets]]
            [tweet-sentiment.dandelion :refer [dandelion-sentiment]]
            [clojure.core.async :refer [>!! <!! put! take! pipe chan]]
            [cemerick.drawbridge :as drawbridge]
            [ring.middleware.basic-authentication :refer [wrap-basic-authentication]]
            )
  (:gen-class))

(defonce server (atom nil))

(deftemplate page (io/resource "index.html") []
             [:body] (if is-dev? inject-devmode-html identity))

(defroutes routes
           (resources "/")
           (resources "/react" {:root "react"})
           (GET "/*" req (page)))


(defn wrap-drawbridge [handler]
  (fn [req]
    (if (= "/repl" (:uri req))
      (drawbridge/ring-handler req)
      (handler req))))

(def http-handler
  (if is-dev?
    (-> #'routes
        (wrap-defaults api-defaults)
        reload/wrap-reload
        wrap-edn-params
        wrap-drawbridge)

    (-> (wrap-defaults #'routes api-defaults)
        wrap-edn-params
        wrap-drawbridge)))

(defn run-web-server [& [port]]
  (let [port (Integer. (or port (env :port) 10555))]
    (println (format "Starting web server on port %d." port))
    (reset! server (run-server http-handler {:port port :join? false}))))

(defn run-auto-reload [& [port]]
  (auto-reload *ns*)
  (start-figwheel)
  (start-less))

(defn run [& [port]]
  (when is-dev?
    (run-auto-reload))
  (run-web-server port))

(defn stop-server []
  (when-not (nil? @server)
    (@server :timeout 0)
    (reset! server nil)))

(defn restart-server []
  (stop-server)
  (run-web-server))

(defn -main [& [port]]
  (run port))

当我尝试以开发或生产身份连接到吊桥 REPL 时:

lein repl :connect http://localhost:10555/repl

我收到以下错误:

ERROR - POST /repl
java.lang.IllegalArgumentException: No value supplied for key: {:remote-addr "127.0.0.1", :headers {"accept-encoding" "gzip, deflate", "connection" "close", "content-length" "48", "content-type" "application/x-www-form-urlencoded", "host" "localhost:10555", "user-agent" "Apache-HttpClient/4.3.3 (java 1.5)"}, :async-channel #<AsyncChannel /127.0.0.1:10555<->/127.0.0.1:60374>, :server-port 10555, :content-length 48, :websocket? false, :content-type "application/x-www-form-urlencoded", :character-encoding "utf8", :uri "/repl", :server-name "localhost", :query-string nil, :body #<BytesInputStream BytesInputStream[len=48]>, :scheme :http, :request-method :post}
    at clojure.lang.PersistentHashMap.create(PersistentHashMap.java:77)
    at cemerick.drawbridge$ring_handler.doInvoke(drawbridge.clj:49)
    at clojure.lang.RestFn.invoke(RestFn.java:408)
    at tweet_sentiment.server$wrap_drawbridge$fn__21884.invoke(server.clj:36)
    at org.httpkit.server.HttpHandler.run(RingHandler.java:91)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

问题显然出在 drawbridge/ring-handler 函数中。 这些是我的依赖项:

[[org.clojure/clojure "1.6.0"]
                 [org.clojure/clojurescript "0.0-3058" :scope "provided"]
                 [ring "1.3.2"]
                 [ring/ring-defaults "0.1.4"]
                 [compojure "1.3.2"]
                 [enlive "1.1.6"]
                 [org.omcljs/om "0.8.8"]
                 [om-sync "0.1.1"]
                 [environ "1.0.0"]
                 [http-kit "2.1.19"]
                 [fogus/ring-edn "0.3.0"]
                 [prismatic/om-tools "0.3.11"]
                 [secretary "1.2.3"]
                 [sablono "0.3.4"]
                 [twitter-api "0.7.8"]
                 [racehub/om-bootstrap "0.5.3"]
                 [cheshire "5.2.0"]
                 [com.cemerick/drawbridge "0.0.7"]
                 [ring-basic-authentication "1.0.1"]]

有人用 drawbrige 开始工作 http.kit 服务器吗?我错过了什么?

我终于弄明白了,服务器文件应该看起来像这样

(ns tweet-sentiment.server
  (:require [clojure.java.io :as io]
            [tweet-sentiment.dev :refer [is-dev? inject-devmode-html browser-repl start-figwheel start-less]]
            [compojure.core :refer [GET POST defroutes]]
            [compojure.route :refer [resources]]
            [net.cgrand.enlive-html :refer [deftemplate]]
            [net.cgrand.reload :refer [auto-reload]]
            [ring.middleware.reload :as reload]
            [ring.middleware.defaults :refer [wrap-defaults api-defaults]]
            [tweet-sentiment.utils :refer [generate-response]]
            [environ.core :refer [env]]
            [org.httpkit.server :refer [run-server]]
            [ring.middleware.edn :refer [wrap-edn-params]]
            [clojure.core.async :refer [>!! <!! put! take! pipe chan]]
            [cemerick.drawbridge :as drawbridge]
            [ring.middleware.basic-authentication :refer [wrap-basic-authentication]]
            [ring.middleware.params :as params]
            [ring.middleware.keyword-params :as keyword-params]
            [ring.middleware.nested-params :as nested-params]
            [ring.middleware.session :as session]
            )
  (:gen-class))

(defonce server (atom nil))

(deftemplate page (io/resource "index.html") []
             [:body] (if is-dev? inject-devmode-html identity))

(defroutes routes
           (resources "/")
           (resources "/react" {:root "react"})
           (GET "/*" req (page)))

(defn authenticated? [name pass]
  (= [name pass] [(System/getenv "AUTH_USER") (System/getenv "AUTH_PASS")]))

(def drawbridge-handler
  (-> (drawbridge/ring-handler)
      (keyword-params/wrap-keyword-params)
      (nested-params/wrap-nested-params)
      (params/wrap-params)
      (session/wrap-session)))

(defn http-handler [handler]
  (-> handler
      (wrap-defaults api-defaults)
      wrap-edn-params))

(defn wrap-http [handler]
  (fn [req]
    (let [handler (if (= "/repl" (:uri req))
                    (wrap-basic-authentication drawbridge-handler authenticated?)
                    (if is-dev?
                      (-> handler
                          http-handler
                          reload/wrap-reload)
                      (-> handler
                          http-handler)))]
      (handler req))))

(defn run-web-server [& [port]]
  (let [port (Integer. (or port (env :port) 10555))]
    (println (format "Starting web server on port %d." port))
    (reset! server
            (run-server (wrap-http #'routes) {:port port :join? false})
            )))

(defn run-auto-reload [& [port]]
  (auto-reload *ns*)
  (start-figwheel)
  (start-less))

(defn run [& [port]]
  (when is-dev?
    (run-auto-reload))
  (run-web-server port))

(defn stop-server []
  (when-not (nil? @server)
    (@server :timeout 0)
    (reset! server nil)))

(defn restart-server []
  (stop-server)
  (run-web-server))

(defn -main [& [port]]
  (run port))