Post Request in Clojure with clj-http - body not accepted?

1k views Asked by At

With my post request the API documentation for the CRM I wish to post too requires that I post a JSON file.

The JSON file is a multi-level file which is seen in clojure as a persistent array map.

My code to post is:

(def contacts (http/post "https://api.close.com/api/v1/data/search" 
           {:basic-auth [api ""]
            :body closeFilter 
            })) 

CloseFilter represents the mutli-level JSON I wish to post.

However, I get the following error:

class clojure.lang.PersistentArrayMap cannot be cast to class [B (clojure.lang.PersistentArrayMap is in unnamed module of loader 'app'; [B is in module java.base of loader 'bootstrap')

What mistake am I making here?

Update

I am recreating a program I have in Javascript. Posting the same file works perfectly.

Update 2 - MRE

I am still struggling with this so here is an example of my code.

My code starts off by requiring the packages I need:

(ns schedule-emails.core
  (:require [clj-http.client :as http]
            [clojure.data.json :as json]
            [cheshire.core :refer :all]))

Then, I parse a local JSON file from my file system into the app. The JSON. This returns a map of maps with embedded vectors.

(def closeFilter
  (json/read-str
   (slurp "URL TO LOCAL FILE")))

Finally, I want to post this information from the local file to the software:

def contacts (http/post "API URL HERE"
           {:accept :json
            :as :json
            :content-type :json
            :basic-auth [api ""]
            :body closeFilter}))

However, I get the following error:

class clojure.lang.PersistentArrayMap cannot be cast to class [B (clojure.lang.PersistentArrayMap is in unnamed module of loader 'app'; [B is in module java.base of loader 'bootstrap')

I also tried the suggested solution below but i'm getting the same issue.

3

There are 3 answers

4
cfrick On BEST ANSWER

clj-http does not on it's own negotiate with some backend what it expects and coerce the transferred data "automatically". You can however configure, that in the case of JSON, so some data with the proper content-type will be transformed both from the body into the request, and back to data from the response using JSON.

  1. So you usually want the following things in the request:

    {:as :auto
     :coerce :always
     :content-type :application/json
     :body ...
     ; your own additional stuff...
     }
    

    So add a mime-type, so both clj-http knows what to do and the backend knows what it gets.

    See input coercion and output coercion

  2. and you have to make sure, that the means to make it actually working are there. This means, that you have added cheshire as dependency. See optional dependencies

Another option of course is do deal with this yourself. So you would have add a library, that can create JSON from/to a string or stream, the the content-type and transform the body/response.

0
ctpenrose On

This error is coming from clj-http/post probably because the type of closeFilter is neither a HttpEntity instance, a Java byte-array ([B), or a java.lang.String. From 3.12.3: https://github.com/dakrone/clj-http/blob/d92be158230e8094436f415324d96f2bd7cf95f7/src/clj_http/core.clj#L605C1-L611C54

The accepted answer assumes you want to automate coercion. I tend to perform JSON serialization manually to a body value when using clj-http:

(client/post
 "http://example.com"
 {:content-type :json
  :body (-> form ch/generate-string})
0
Alastair On

To use the built-in JSON coercion for a request body you need to set :form-params instead of :body, along with :content-type :json:

;; Send form params as a json encoded body (POST or PUT)
(client/post "http://example.com" {:form-params {:foo "bar"} :content-type :json})

Details: https://github.com/dakrone/clj-http#post