关于Clojure:compojure-api规范对响应体的强制

compojure-api spec coercion on response body

我试图弄清楚如何使用compojure-api和spec做自定义强制。 通过阅读文档和代码,我可以对输入(正文)进行强制转换,但是无法对响应正文进行强制转换。

具体来说,我有一个自定义类型,一个时间戳,在我的应用中表示为long类型,但是对于Web API,我想使用并返回ISO时间戳(不,我不想在内部使用Joda)。

以下是我可用于输入强制的内容,但无法正确强制响应。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
(ns foo
  (:require [clj-time.core :as t]
            [clj-time.coerce :as t.c]
            [spec-tools.conform :as conform]
            [spec-tools.core :as st]))


(def timestamp (st/create-spec
            {:spec pos-int?
             :form `pos-int?
             :json-schema/default"2017-10-12T05:04:57.585Z"
             :type :timestamp}))

(defn json-timestamp->long [_ val]
  (t.c/to-long val))

(def custom-json-conforming
  (st/type-conforming
   (merge
    conform/json-type-conforming
    {:timestamp json-timestamp->long}
    conform/strip-extra-keys-type-conforming)))

(def custom-coercion
  (->  compojure.api.coercion.spec/default-options
       (assoc-in [:body :formats"application/json"] custom-json-
conforming)
       compojure.api.coercion.spec/create-coercion))

;; how do I use this for the response coercion?
(defn timestamp->json-string [_ val]
  (t.c/to-string val))
;; I've tried the following but it doesn't work:
#_(def custom-coercion
  (->  compojure.api.coercion.spec/default-options
       (assoc-in [:body :formats"application/json"] custom-json-
conforming)
       (assoc-in [:response :formats"application/json"]
                 (st/type-conforming
                  {:timestamp timestamp->json-string}))
       compojure.api.coercion.spec/create-coercion))


问题在于规格符合是单向转换管道:

s/conform(并且由于st/conform)确实对结果进行了转换和验证。您的响应强制首先将整数转换为日期字符串,然后针对原始规范谓词pos-int?对其进行验证,然后它就失败了。

为了支持双向转换,您需要将最终结果定义为以下两种可能的格式之一:将您的谓词更改为类似#(or (pos-int? %) (string? %))的东西,它应该可以工作。

或者,您可以有两个不同的规格记录,一个用于输入(带pos-int?谓词的timestamp-long),另一个用于输出(带string?谓词的timestamp-string)。但是,您需要记住对请求和响应使用正确的方法。

如果有额外的:transform模式(尚未在问题中编写),CLJ-2251可能会提供帮助,这会在不验证最终结果的情况下进行验证。

通常,返回转换由格式编码器完成,但它们通常在值类型上分派。例如,柴郡(Cheshire)看到一个Long,却不知道应将其写为日期字符串。

也许#clojure-spec懈怠的人可能会有所帮助。还想知道如何使用规范构建这种双向转换。