如何在Clojure Liberator中从POST处理程序返回JSON数据?

6
如何使用Clojure Liberator返回json数据?以下代码无法正常工作:
(defresource poster []
         :allowed-methods [:post :options]
         :available-media-types ["application/json"]
         :post!      (fn [ctx] (println "posting..."))
         :handle-created (fn [ctx] {:created "ok"}))

应该在发布后调用handle-created吗?

看一下Liberator决策图,也许在你的资源中添加:new? true可能会有所帮助?(如果你真的想返回201 Created的话。) - xsc
它返回201,但没有json数据。我想要返回带有201头的json数据。这可行吗? - Curiosity
我还注意到你将:available-media-types定义为字符串 - 它应该是一个字符串向量,表示可用的媒体类型,例如在你的情况下:available-media-types ["application/json"]。 - Tim X
是的,谢谢,那是一个打字错误。 - Curiosity
我该如何返回200 OK而不是201 Created?我正在尝试在一个端点上发布一个文本文件,但默认响应是201 Created,而不是200 OK。我错过了什么吗? - Daniel Colceag
@DanielColceag看一下这个链接:https://clojure-liberator.github.io/liberator/tutorial/decision-graph.html我认为你在new?上可能会得到true - Bartors
3个回答

3
:post!键相关联的函数不是处理程序函数。Liberator文档将其称为动作函数。
引用:

动作函数::post!,:put!和:delete!键提供了适合执行副作用的点。虽然它们像决策函数一样被评估,但布尔值没有影响,下一个决策步骤是恒定的。上下文更新的工作方式与决策函数完全相同。

因此,您无法直接从与:post!相关联的函数生成http响应。
:post!键相关联的函数可以返回某些内容,该内容将合并到上下文中。
引用:

上下文更新的工作方式与决策函数完全相同。

然后,处理程序函数稍后可以将该内容从上下文中提取出来,并使用它形成http响应。这些键中的任何一个处理程序函数都可能随后执行::handle-ok:handle-created:handle-no-content:handle-see-other:handle-multiple-representations
这个决策图确定哪个处理程序将被执行。
最好只回复一个指向新创建资源的位置标头,而不带任何正文,但以下是创建具有JSON正文和201状态的响应的一个示例。
(POST "/foo" [] (resource :allowed-methods [:post]
                           :available-media-types ["application/json"]
                           :handle-created {:created "ok"}))

尝试一下:
curl -X POST "localhost/foo" 
{"created":"ok"}

您可以在Liberator的project.clj文件中看到它使用了哪个JSON库。如果您想自己生成JSON字符串,可以按照以下方式进行:
:handle-created (fn [ctx] (liberator.representation/ring-response 
                          {:status 201 :body "{created:\"ok\"}"}))

这里提到了这里


1
这是我的做法 - 似乎可行,但我刚开始使用Liberator,可能有更好或更正确的方法! 我认为你需要一个手动创建的处理程序。例如 -
(defresource shopping-calc
  :allowed-methods [:post]
  :available-media-types ["application/json"]
  :malformed? (fn [context]
                (let [params (get-in context [:request :params])]
                  (or (empty? params)
                      (is-malformed? params))))
  :handle-malformed (fn [context]
                      (let [params (get-in context [:request :params])]
                        (generate-string (handle-malformed-calc params))))
  :handle-created (fn [context]
                    (let [params (get-in context [:request :params])]
                      (generate-string (calculate-total params)))))

我有一个类似这样的处理程序创建的处理程序。
(defn calculate-total [params]
  {:total (calc params)})

我还使用了ring/json中间件,在我的开发环境中,添加了Liberator跟踪工具。Liberator跟踪工具非常方便,它会向响应添加头部,显示Liberator的决策点。因此,对于你的问题,它可能会显示Liberator正在使用默认的handle-created处理程序,它只返回头部。要返回自己的json,您需要定义处理程序。
请注意,我没有使用post!方法。这是因为在这个例子中,我实际上并没有创建某种新的对象/项,比如向某种存储添加记录。如果您正在执行此操作,您可能会使用post!来添加记录,并定义handle-created来获取新记录(可能包括其他新字段,例如记录ID或时间戳等)并将其返回。
我使用:malformed?和handle-malformed进行基本错误检查。如果:malformed?返回true,则调用handle-malformed标头,在json主体中返回错误状态和错误消息。我发现将错误也以json形式返回非常有帮助,这样您就可以在客户端端处理所有内容。
我的处理程序和中间件定义如下。请注意,由于我正在提供应用程序和api路径,它们变得有点更加复杂,因为我希望将某些中间件应用于某些路径,而将其他中间件应用于其他路径。ring/ring-defaults 中还存在一个小错误,一旦修复,就会修改当前无法使用 wrap-defaults site-api 中间件的事情。请注意wrap-trace中间件。
(def app
  (if (env :dev)
    (routes (-> api-routes
                (wrap-reload)
                (wrap-routes wrap-json-params)
                (wrap-routes wrap-defaults api-defaults)
                (wrap-routes wrap-trace :header :ui))
            (-> app-routes
                (wrap-routes wrap-error-page)
                (wrap-routes wrap-exceptions)))
    (routes (-> api-routes
                (wrap-routes wrap-json-params)
                (wrap-routes wrap-defaults api-defaults))
            app-routes)))

谢谢。昨天我花了一些时间研究liberator的图表,现在已经可以使用post!和handle-created了。也许昨天我太累了。我还实现了从delete!方法返回添加respond-with-entity?为true,并将handle-ok作为删除操作的结果处理。 - Curiosity

1

状态码 201 created 用于在响应体中返回新创建资源的链接以及 Location 头部。如果你想在响应体中返回新创建的资源,应该使用 200 ok。默认情况下,Liberator 在 POST 请求后会以 204 no-content 结束。你需要设置 :respond-with-entity? true

你的资源定义应如下:

(defresource poster []
   :allowed-methods [:post :options]
   :available-media-types ["application/json"]
   :malformed? (fn [ctx]
                 [false {::resource (parse-json (get-in ctx [:request :body]))}])
   :post! (fn [ctx]
            (persist (::resource ctx)))
   :handle-ok (fn [ctx]
                (generate-json (::resource ctx))))

我建议使用Ceshire来解析和生成JSON。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接