clojurejava-ioliberatorslurp

Multiple clojure-liberator decisions reads request body


I have one defresource, that is supposed to take POST-requests, validate request body in :malformed-decision, save the body to database in :post!-decision and return the saved body in :handle-created.

(defn parse-project [context] (json/read-str
                               (slurp (get-in context [:request :body]))
                               :key-fn keyword))    
(defresource add-new-project
         :malformed? (fn[ctx] (not (project-is-valid (parse-project ctx))))
         :handle-malformed (fn [_] (generate-string (str "Malformed json!")))
         ...
         :post! (fn [ctx] (save-to-db (parse-project ctx))
         :handle-created  (fn [ctx] (... parse-project ...))

So my code reads three times the ByteArrayInputStream(that comes from :request :body) with slurp-function. First time works but second time when slurp is called, nil is passed as a param and comes java.io.EOFException: JSON error. I think the reader starts reading where it was left last time.

How could I read the body of the request three times? Or is there nice way to save the result of reading to variable and pass that to other liberator-decisions?


Solution

  • The context can be updated by the outcome of each decision and action functions. You can parse the project once in malformed? and return a map with the parsed project which will be merged into the context so it is available to the following decisions and actions. For example:

    (defresource add-new-project
      :malformed? (fn[ctx] (let [project (parse-project ctx)]
                             (when (project-is-valid project)
                               {:project project})))
      :handle-malformed (fn [_] (generate-string (str "Malformed json!")))
      :post! (fn [ctx] (save-to-db (:project ctx)))
      :handle-created (fn [ctx] (do-something (:project ctx))))
    

    If the project is valid, :malformed? returns the {:project project} map which will be merged into the context to be used in the next decisions and actions. If the project is not valid, it will return nil so execution continues in :handle-malformed.

    For more info on liberator's execution model, see https://clojure-liberator.github.io/liberator/doc/execution-model.html