Fork me on GitHub
#reitit
<
2023-02-18
>
vxe03:02:41

I would like to imitate the url format of an existing service using reitit , the format is https://internal-service/ABCDEFHIJK do reitit path params permit this format? Some initial testing with the following doesn't seem to work

["/:doc-id" {:post {:handler (fn [{:keys [path-params]}]
                                     {:status 200
                                        ;; :headers {"Content-Type" "image/png"}
                                      :body path-params})}}]

hifumi12304:02:45

post a full router instead of a stub — i want to rule out the possibility of you including a root route and accidentally introducing an extra slash

👀 2
vxe04:02:29

(def app
  (ring/ring-handler
   (ring/router
    [["/" {:tags "root"}
      ["/:doc-id" {:post {:handler (fn [{:keys [path-params]}]
                                     {:status 200
                                        ;; :headers {"Content-Type" "image/png"}
                                      :body path-params})}}]]
     ["/swagger.json"
      {:get {:no-doc true
             :swagger {:info {:title "my-api"
                              :description "with reitit-ring"}}
             :handler (swagger/create-swagger-handler)}}]

     ]

    {;;:reitit.middleware/transform dev/print-request-diffs ;; pretty diffs
     ;;:validate spec/validate ;; enable spec validation for route data
     ;;:reitit.spec/wrap spell/closed ;; strict top-level validation
     :exception pretty/exception
     :data {:coercion reitit.coercion.spec/coercion
            :muuntaja m/instance
            :middleware [;; swagger feature
                         swagger/swagger-feature
                         ;; query-params & form-params
                         parameters/parameters-middleware
                         ;; content-negotiation
                         muuntaja/format-negotiate-middleware
                         ;; encoding response body
                         muuntaja/format-response-middleware
                         ;; exception handling
                         exception/exception-middleware
                         ;; decoding request body
                         muuntaja/format-request-middleware
                         ;; coercing response bodys
                         coercion/coerce-response-middleware
                         ;; coercing request parameters
                         coercion/coerce-request-middleware
                         ;; multipart
                         multipart/multipart-middleware]}})
   (ring/routes
    (ring/create-resource-handler {:path "/public"})
    (swagger-ui/create-swagger-ui-handler
     {:path "/"
      :config {:validatorUrl nil
               :operationsSorter "alpha"}})
    (ring/create-default-handler))))

hifumi12304:02:57

bingo, that is exactly it

hifumi12304:02:09

replace /:doc-id with simply :doc-id

hifumi12304:02:58

I don’t know how to explain it well in English, so here’s some code denoting how the route tree works

[["/"
  [""]              ; /
  [":doc-id"]]      ; /:doc-id
 ["/swagger.json"]] ; /swagger.jsobn

vxe04:02:40

hmm deosn't seem to like that, must have missed bracket/paren somewhere?

vxe04:02:05

or maybe my handler is invalid

hifumi12304:02:44

What is the address you visited in your browser? What happens if you run (r/match-by-path your-router "/123")

hifumi12304:02:14

Oh I think I see the issue now

hifumi12304:02:36

/:doc-id and /swagger.json are conflicting paths. Set either one to :conflicting true

hifumi12304:02:18

Here’s an example from my clojurescript REPL

cljs.user> (-> (r/router
                [["/"
                  ["" {:name ::root}]
                  [":id" {:name ::root.id
                          :conflicting true}]]
                 ["/swagger.json" {:name ::swagger
                                   :conflicting true}]])
               (r/match-by-path "/123"))               
#reitit.core.Match{:template "/:id", :data {:name :cljs.user/root.id, :conflicting true}, :result nil, :path-params {:id "123"}, :path "/123"}

👀 2
hifumi12304:02:40

The reason this happens is because path params are strings (until you coerce them), so reitit has no way to tell whether you wanted /:id or /swagger.json when gets a path like / + string . By setting the paths as conflicting, reitit will know ahead of time that it will have to resolve the conflict

vxe04:02:59

getting further now , now my swagger is http 405:

[["/" 
      ["" {:name ::root}]
      [":doc-id" {:name ::root.doc-id
                  :conflicting true
                  }]]
     ["/swagger.json"
      {:name ::swagger
       :conflicting true
       :get {:no-doc true
             :swagger {:info {:title "my-api"
                              :description "with reitit-ring"}}
             :handler (swagger/create-swagger-handler)}}]

     ]

vxe04:02:06

sre.zomgd.core> (reitit.core/match-by-path app "/123")
Execution error (IllegalArgumentException) at reitit.core/eval54459$fn$G (core.cljc:38).
No implementation of method: :match-by-path of protocol: #'reitit.core/Router found for class: clojure.lang.AFunction$1

vxe04:02:25

metosin/reitit {:mvn/version "0.4.2"}

vxe04:02:36

i think my router is typo'd perhaps going to start from scratch and see + upgrade to latest version

hifumi12305:02:26

Place the /swagger.json route before the /:id route

hifumi12305:02:46

Conflict resolution algorithm isn’t documented well in Reitit, unfortunately. But in short, the first match wins in a conflict.

hifumi12305:02:55

That is to say, if we have

[["/:id"]
 ["/swagger.json"]]
then /:id always wins in a conflict. However, if we have the route tree
[["/swagger.json"]
 ["/:id"]]
then the path /swagger.json will be matched against /swagger.json first, so we get the /swagger.json route instead of /:id

hifumi12305:02:33

For reference, I am using [metosin/reitit "0.5.18"] , also the latest version of the library.

hifumi12305:02:50

@U5Y86G3KL Here is a demonstration of the first match winning in a conflict

cljs.user> (def router 
             (r/router
              [["/swagger.json" {:conflicting true}]
               ["/:id" {:conflicting true}]]))
#'cljs.user/router
cljs.user> (:template (r/match-by-path router "/123"))
"/:id"
cljs.user> (:template (r/match-by-path router "/swagger.json"))
"/swagger.json"

vxe05:02:04

hey @U0479UCF48H moving swagger above :doc-id + :conflicting true did the trick! tysm for your help 🙏

Ben Sless06:02:43

Thought: should route parameters schema allow a single schema for all parameters parts? This would allow cross validation, example, multi schema for body based on header