This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-05-30
Channels
- # babashka (3)
- # beginners (1)
- # cider (2)
- # clara (6)
- # clj-kondo (15)
- # clojure (7)
- # clojure-germany (11)
- # clojurescript (1)
- # conjure (27)
- # core-async (10)
- # cursive (3)
- # datahike (5)
- # datomic (2)
- # figwheel-main (1)
- # fulcro (8)
- # heroku (8)
- # leiningen (8)
- # music (1)
- # off-topic (8)
- # polylith (13)
- # reitit (54)
- # shadow-cljs (2)
- # xtdb (13)
Hallo. Playing around with this. I don't quite understand what the right way is to do validation of my endpoints and error handling, I want to use malli.
((ring/ring-handler (ring/router ["/question" {:post {:handler (fn [_] {:status 200, :body "ok"})}}])) {:request-method :post, :uri "/question" :body "2"})
So how would I validate that the body that is being send is according to some malli schema.
Hi @nickik, here is a repo that you can use for some learning,
. You'll notice here
I define some routes
(`https://github.com/dharrigan/startrek/blob/master/src/startrek/base/api/starship/routes.clj#L71) is the post
and at line 72
is the spec to the route, for validating the post
(def router2 (ring/router ["/question" {:post {:handler (fn [_] {:status 200, :body "ok"})
:parameters {:body [:vector :int]}}}]))
(def app2 (ring/ring-handler router2))
(app2 {:request-method :post, :uri "/question" :body-params "test"})
@dharrigan Seems to me this should produce an error
Or maybe:
(def router2 (ring/router ["/question" {:post {:handler (fn [_] {:status 200, :body "ok"})
:coercion reitit.coercion.malli/coercion
:parameters {:body [:vector :int]}}}]
{:compile coercion/compile-request-coercers}))
(def app2 (ring/ring-handler router2))
(app2 {:request-method :post, :uri "/question" :body-params "test"})
["/question" {:post {:handler (fn [request]
(prn request)
{:status 200 :body (-> request :body-params :question)})
:parameters {:body [:map
[:question [:vector int?]]]}}}]]
❯ http --verbose :8080/question question:='[1, 2]'
POST /question HTTP/1.1
Accept: application/json, */*;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 20
Content-Type: application/json
Host: localhost:8080
User-Agent: HTTPie/2.4.0
{
"question": [
1,
2
]
}
HTTP/1.1 200 OK
Content-Length: 5
Content-Type: application/json;charset=utf-8
Date: Sun, 30 May 2021 18:01:17 GMT
Server: Jetty(9.4.40.v20210413)
[
1,
2
]
❯ http --verbose :8080/question question:='[1, "david"]'
POST /question HTTP/1.1
Accept: application/json, */*;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 26
Content-Type: application/json
Host: localhost:8080
User-Agent: HTTPie/2.4.0
{
"question": [
1,
"david"
]
}
HTTP/1.1 500 Server Error
Cache-Control: must-revalidate,no-cache,no-store
Connection: close
Content-Length: 13635
Content-Type: application/json
Server: Jetty(9.4.40.v20210413)
{
"cause0": "clojure.lang.ExceptionInfo: Request coercion failed: #reitit.coercion.CoercionError{:schema [:map {:closed true} [:question [:vector int?]]], :value {:question [1 "david"]}, :errors (#Error{:path [:question 0], :in [:question 1], :schema int?,
you would have to of course, put some exception handling in place, but it looks to me it's doing the right thing.
"question": [
1,
"david"
]
}
HTTP/1.1 400 Bad Request
Content-Length: 355
Content-Type: application/json;charset=utf-8
Date: Sun, 30 May 2021 18:07:16 GMT
Server: Jetty(9.4.40.v20210413)
{
"coercion": "malli",
"errors": [
{
"in": [
"question",
1
],
"message": "should be an int",
"path": [
"question",
0
],
"schema": "int?",
"value": "david"
}
],
"humanized": {
"question": [
null,
[
"should be an int"
]
]
},
"in": [
"request",
"body-params"
],
"schema": "[:map {:closed true} [:question [:vector int?]]]",
"type": "reitit.coercion/request-coercion",
"value": {
"question": [
1,
"david"
]
}
}
Can you also do this in app like so:
(app2 {:request-method :post, :uri "/question" :body-params [1]})
["/question" {:post {:handler (fn [request]
{:status 200 :body (-> request :body-params)})
:parameters {:body [:vector int?]}}}]]
❯ echo '[1, "david"]' | http --verbose :8080/question
POST /question HTTP/1.1
Accept: application/json, */*;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 13
Content-Type: application/json
Host: localhost:8080
User-Agent: HTTPie/2.4.0
[
1,
"david"
]
HTTP/1.1 400 Bad Request
Content-Length: 273
Content-Type: application/json;charset=utf-8
Date: Sun, 30 May 2021 18:32:32 GMT
Server: Jetty(9.4.40.v20210413)
{
"coercion": "malli",
"errors": [
{
"in": [
1
],
"message": "should be an int",
"path": [
0
],
"schema": "int?",
"value": "david"
}
],
"humanized": [
null,
[
"should be an int"
]
],
"in": [
"request",
"body-params"
],
"schema": "[:vector int?]",
"type": "reitit.coercion/request-coercion",
"value": [
1,
"david"
]
}
@dharrigan Maybe I'm stupid. Can you send me the full config for the routes?
(ring/ring-handler
(ring/router ["/question" {:post {:handler (fn [request]
(prn request)
{:status 200 :body (-> request :body-params :question)})
:parameters {:body [:map
[:question [:vector int?]]]}}}]
{:data {:compile coercion/compile-request-coercers}} ))
I don't understand what configuration is required. Your projects seems to do this: {:data {:coercion rcm/coercion{:validate rs/validate}}
But somehow for me this doesn't work. In your case you don't seem to have the 'compile' but the documentation here: https://cljdoc.org/d/metosin/reitit/0.5.13/doc/coercion/malli
Mh its funny, I had it working for a minute without all that stuff, so the :muuntaja and the :middleware is required? Whats the minimal configuration?
Ok, so basically my issue was that I didn't understand that the middleware is required to get it to work within ring. The documentation on the malli part was only for the route matching.
@dharrigan You seem to be using clip. Do you like it? I have been using integrant/duct and trying to decide to switch to clip.
I like clip a lot. I've been through integrant (tried a bit of duct, was turn off by it) and component and mount