compojure-api spec приведение на тело ответа
Я пытаюсь выяснить, как сделать пользовательское приведение с помощью compojure-api и spec. Читая документы и код, я смог принуждение к входу (тело), но не могу принуждение к телу ответа.
В частности, у меня есть пользовательский тип, метка времени, которая представлена как длинная в моем приложении, но для веб-API я хочу потреблять и возвращать метки времени 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))
1 ответ
Проблема в том, что Spec Conforming - это односторонний конвейер преобразования:
s/conform
(и из-за этого st/conform
) преобразовывает и проверяет результат. Приведение ответа сначала преобразует целое число в строку даты и проверяет ее по исходному предикату спецификации, который pos-int?
и это терпит неудачу на этом.
Для поддержки двусторонних преобразований необходимо определить конечный результат в качестве одного из возможных форматов: например, изменить предикат на что-то вроде #(or (pos-int? %) (string? %))
и это должно работать.
Или вы можете иметь две разные записи Spec, одна для ввода (timestamp-long
с pos-int?
предикат) и другой для выходных данных (timestamp-string
с string?
сказуемое). Но нужно помнить, чтобы использовать правильные для запроса и ответов.
CLJ-2251 мог бы помочь, если бы была и дополнительная :transform
режим (еще не написан в выпуске), который будет соответствовать без проверки конечных результатов.
Обычно обратные преобразования выполняются кодировщиком формата, но они обычно отправляются для типов значений. Например, Cheshire просто видит Long и не имеет ни малейшего представления, что его следует записать в виде строки даты.
Может быть, люди из #clojure-spec могут помочь. Также хотелось бы узнать, как построить этот вид двустороннего преобразования с помощью spec.