Как я могу сделать http-вызов в clojure/ring?

Мой веб-клиент (написано в cljs) подключается к бэкэнду (написано в clj), который должен сделать несколько вызовов API сторонних производителей. Это должно быть сделано на сервере, а затем результат должен быть преобразован определенным образом и отправлен обратно клиенту.

Вот мой обработчик для одного из URL

(defn get-orders [req]
  (let [{:keys [sig uri]} (api-signature :get-orders)]
    (client/get uri
                {:async? true}
                (fn [response] {:body "something"})
                (fn [exception] {:body "error"}))))

Вместо возвращения {:body "something"}, он возвращает следующую ошибку:

No implementation of method: :render of protocol: #'compojure.response/Renderable found for class: org.apache.http.impl.nio.client.FutureWrapper

Что я делаю неправильно?

1 ответ

Решение

Когда вы указываете {:async? true}, clj-http.client/get вернет будущее, которое является FutureWrapper в сообщении об ошибке, которое вы получили.

Так что, если вам не нужен асинхронный режим, не используйте его. Это пример синхронного обработчика звонков, который вызывает сторонний URL и возвращает ответ, который был получен.

(defn handler [request]
  (response {:result (client/get "http://example.com")}))

Если вам действительно нужна асинхронность, используйте асинхронную версию обработчика кольца.

(defn handler [request respond raise]
  (client/get "http://example.com"
              {:async? true}
              (fn [response] (respond {:body "something"}))
              (fn [exception] (raise {:body "error"}))))

Не забудьте настроить адаптер веб-сервера для использования асинхронного обработчика. Например, для Jetty, установите :async? флаг для true вот так

(jetty/run-jetty app {:port 4000 :async? true :join? false})

Если вы хотите одновременно вызывать несколько сторонних URL-адресов и возвращаться один раз в веб-клиент, используйте обещание, чтобы помочь

(defn handler [request]
  (let [result1 (promise)
        result2 (promise)]
    (client/get "http://example.com/"
                {:async? true}
                (fn [response] (deliver result1 {:success true :body "something"}))
                (fn [exception] (deliver result1 {:success false :body "error"})))
    (client/get "http://example.com/"
                {:async? true}
                (fn [response] (deliver result2 {:success true :body "something"}))
                (fn [exception] (deliver result2 {:success false :body "error"})))
    (cond
      (and (:success @result1)
           (:success @result2))
      (response {:result1 (:body @result1)
                 :result2 (:body @result2)})

      (not (:success @result1))
      (throw (ex-info "fail1" (:body @result1)))

      (not (:success @result2))
      (throw (ex-info "fail2" (:body @result2))))))
Другие вопросы по тегам