Fork me on GitHub
#ring
<
2022-02-23
>
fadrian13:02:52

I've set up a primitive ring server with the following routes:

(defn translate
  [json]
  "Translated JSON")

(defn process-translate
  [req]
  (println req)
  (if-let [body (:body req)]
    {:status 200 :headers {"Content-Type" "application/text" "Character-Encoding" "utf8"} :body (translate body)}
    (default-malformed-response)))

(defroutes mercator-server
  (GET "/" [] "mercator-server version 0.0.1")
  (POST "/translate" req (process-translate req))
  (POST "/translate/" req (process-translate req)))
When I pass in a well-formed JSON body, the request returns 200 and the expected body text. However, when I pass in a malformed JSON body, rather than returning the expected 400 status, I keep getting a 404 from the server. I'm using the wrap-json-body middleware, so a malformed json body should return a nil from (:body req), but somehow, ring seems to bounce the request back with a 404 before it even gets to the process-translate call. How do I get the handler to return a 400 status?

jumar14:02:32

If you return nil from a route handler it gets translated into 404 status

fadrian14:02:11

Thanks for that, good to know. I've now also added the :malformed-response option to my wrap-json-body middleware and made sure the handler is NOT returning nil for a bad string:

(defn wrap-json-body [handler]
  (fn [request]
    (handler (json-body-request request {:keywords? true :malformed-response default-malformed-response}))))

(defn translate
  [json]
  "Translated JSON")

(defn process-translate
  [req]
  (println "In process-translate:" req)
  (if-let [body (:body req)]
    {:status 200 :headers {"Content-Type" "application/text" "Character-Encoding" "utf8"} :body (translate body)}
    "Bad body"))

(defroutes mercator-server
  (GET "/" [] "mercator-server version 0.0.1")
  (POST "/translate" req (process-translate req))
  (POST "/translate/" req (process-translate req)))
However, the middleware still seems to be returning a 404 status before even getting to the process-translate function.

fadrian15:02:52

I've also read the code of wrap-json-body, and I shouldn't even have to add the :malformed-response default-malformed-response option, because the code supposedly defaults to returning that. But for some reason, it's returning a 404.

jumar15:02:13

@U0DTU8J83 I'm not sure what is this wrap-json-body in your code. We are using this standard middleware like this: https://github.com/jumarko/clojure-experiments/blob/master/src/clojure_experiments/ring/server.clj#L9-L12

(defn wrap [handler]
  (-> handler
      wrap-json-response
      (wrap-json-body {:keywords? true})))
When you try to run this code it should work as expected:
curl -i -H 'Content-Type: application/json' -d '{a}' 

HTTP/1.1 400 Bad Request
Date: Wed, 23 Feb 2022 15:16:58 GMT
Content-Type: text/plain
Transfer-Encoding: chunked
Server: Jetty(9.4.36.v20210114)

Malformed JSON in request body.%                                                                                                                                                                                                             


curl -i -H 'Content-Type: application/json' -d '{}' 

HTTP/1.1 200 OK
Date: Wed, 23 Feb 2022 15:18:31 GMT
Content-Type: application/text
Character-Encoding: utf8
Transfer-Encoding: chunked
Server: Jetty(9.4.36.v20210114)

Translated JSON% 

fadrian15:02:52

Doing it that way works. I have no idea why it works when the other code didn't, but thank you for that suggestion.

sova-soars-the-sora23:02:52

Hi I want to use ring's mock request to test a program/service/api So far it's coming along pretty well but I have a strange issue. I expect the result of the body to be a string, but

sova-soars-the-sora23:02:37

I end up getting

{:body #object[java.io.ByteArrayInputStream 0xhex... "java.io.ByteArrayInputStream@samehex"] ...}

sova-soars-the-sora23:02:44

Worth noting this is on Luminus with #muuntaja so I was thinking m/decode-whatever would work but it could not decode the ByteArrayInputStream. Am thinking Content-Type? But then it's application/edn on the request. So ... what to do.

hiredman23:02:35

decoding wouldn't turn the body into a string

hiredman23:02:33

depending on exactly what the middleware does the body might be replaced with the decode datastructure, or (less often, but maybe arguably more correct according to the ring SPEC) the read inpustream will be left in place, and new data added elsewhere