compojure-api 对响应主体的规范强制
compojure-api spec coercion on response body
我正在尝试弄清楚如何使用 compojure-api 和规范进行自定义强制转换。通过阅读文档和代码,我已经能够对输入(正文)进行强制转换,但无法对响应正文进行强制转换。
具体来说,我有一个自定义类型,一个时间戳,在我的应用程序中表示为 long 但对于网络 API 我想使用和 return ISO 时间戳(不,我不想在内部使用 Joda)。
以下是我对输入强制有效的方法,但我无法正确强制响应。
(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))
问题是 Spec Conforming 是一种单向转换管道:
s/conform
(因此 st/conform
)对结果进行转换和验证。您的响应强制首先将整数转换为日期字符串,然后根据原始规范谓词 pos-int?
对其进行验证,但它失败了。
要支持双向转换,您需要将最终结果定义为两种可能的格式之一:例如将您的谓词更改为 #(or (pos-int? %) (string? %))
之类的东西,它应该可以工作。
或者您可以有两个不同的规范记录,一个用于输入(timestamp-long
with pos-int?
predicate),另一个用于输出(timestamp-string
with string?
predicate)。但是需要记住使用正确的请求和响应。
CLJ-2251 如果有额外的 :transform
模式(还没有写在问题中),可能会有所帮助,这将在不验证最终结果的情况下保持一致。
通常,return 转换由格式编码器完成,但它们通常在值类型上分派。例如 Cheshire 只看到一个 Long 并且不知道它应该写成日期字符串。
也许 #clojure-spec slack 上的人可以提供帮助。也想知道如何用规范构建这种双向转换。
我正在尝试弄清楚如何使用 compojure-api 和规范进行自定义强制转换。通过阅读文档和代码,我已经能够对输入(正文)进行强制转换,但无法对响应正文进行强制转换。
具体来说,我有一个自定义类型,一个时间戳,在我的应用程序中表示为 long 但对于网络 API 我想使用和 return ISO 时间戳(不,我不想在内部使用 Joda)。
以下是我对输入强制有效的方法,但我无法正确强制响应。
(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))
问题是 Spec Conforming 是一种单向转换管道:
s/conform
(因此 st/conform
)对结果进行转换和验证。您的响应强制首先将整数转换为日期字符串,然后根据原始规范谓词 pos-int?
对其进行验证,但它失败了。
要支持双向转换,您需要将最终结果定义为两种可能的格式之一:例如将您的谓词更改为 #(or (pos-int? %) (string? %))
之类的东西,它应该可以工作。
或者您可以有两个不同的规范记录,一个用于输入(timestamp-long
with pos-int?
predicate),另一个用于输出(timestamp-string
with string?
predicate)。但是需要记住使用正确的请求和响应。
CLJ-2251 如果有额外的 :transform
模式(还没有写在问题中),可能会有所帮助,这将在不验证最终结果的情况下保持一致。
通常,return 转换由格式编码器完成,但它们通常在值类型上分派。例如 Cheshire 只看到一个 Long 并且不知道它应该写成日期字符串。
也许 #clojure-spec slack 上的人可以提供帮助。也想知道如何用规范构建这种双向转换。