- Notifications
You must be signed in to change notification settings - Fork147
Routing
Compojure-api usesCompojure for routing.
The big difference is, that all compojure-api route functions & macros returncompojure.api.routes/Route-records which can both act as normal ring handlers (e.g. can be called with request to produce an response) and they satisfy thecompojure.api.routes/Routing protocol for collecting the route information.
(definc-route (GET"/inc" []:query-params [x:- s/Int]:return {:result s/Int} (ok {:result (inc x)})))inc-route; #Route{:path "/inc"; :method :get; :info {:parameters {:query {Keyword Any, :x Int}}; :responses {200 {:schema {:result Int}, :description ""}}}}(inc-route {:request-method:get,:uri"/inc":query-params {}}); CompilerException clojure.lang.ExceptionInfo: Request validation failed: {:x missing-required-key}(inc-route {:request-method:get,:uri"/inc":query-params {:x1}}); {:status 200, :headers {}, :body {:result 2}, :compojure.api.meta/serializable? true}
At api creation time, the route-tree is walked and the reverse-route tree is generated - to be used both for swagger-docs & for bi-directional routing. To resolve the route-tree manually, you can callget-routes.
(compojure.api.routes/get-routes (context"/api" [] inc-route (POST"/mortem" []:summary"oh, noes" (ok {:rest"in piece"})))); [["/api/inc" :get {:parameters {:query {Keyword Any, :x Int}}, :responses {200 {:schema {:result Int}, :description ""}}}]; ["/api/mortem" :post {:summary "oh, well"}]]
Routes can have a (preferably qualified) keyword:name. When anapi is created, a reverse route tree is created from it's subroutes and the routing table is injected into the request. One can get a full string path to a route by calingcompojure.api.routes/path-for* with the request, route name and optionally path-parameters as a map. There is also a helper macrocompojure.api.routes/path-for which reads the request from lexical scope bindings. It is only available in the http endpoint macros (GET,POST etc.). Path parameters are written into Strings using standard JSON decoding.
(require '[compojure.api.routes:as routes])(defapp (api;; a named route (GET"/pong/:id" []:path-params [id:- s/Int]:name::pong (ok {:id id}));; reverse routing via a macro (GET"/ping1" [] (temporary-redirect (routes/path-for::pong {:id1})));; reverse routing via function (GET"/ping2" request (temporary-redirect (routes/path-for*::pong request {:id2})))))(app {:request-method:get,:uri"/ping1"}); {:status 307, :headers {"Location" "/pong/1"}, :body ""}(app {:request-method:get,:uri"/ping2"}); {:status 307, :headers {"Location" "/pong/2"}, :body ""}
If the api routes contain routes, which do not satisfy theRouting protocol (e.g. normal ring/compojure functions), an callback-function[:api :invalid-routes-fn] is called. By default, an warning is logged. At runtime, everything works as expected.
By default, aWARN is logged.
(api (GET"/ping" [] (ok {:message"I satisfy the Routing protocol!"})) (compojure.core/GET"/pong" [] (ok {:message"I dont."}))); WARN Not all child routes satisfy compojure.api.routing/Routing. {:path nil, :method nil}, invalid child routes: [#function[compojure.core/if-method/fn--19598]]
Marking routesundocumented will stop the route collector to entering those routes (still works at runtime thou).
(api (GET"/ping" [] (ok {:message"I satisfy the Routing protocol!"})) (undocumented (compojure.core/GET"/pong" [] (ok {:message"I dont."}))))
You can also mark compojure-api routes asundocumented - here, the whole api is undocumented
(api (undocumented (GET"/ping" [] (ok {:message"I satisfy the Routing protocol!"})) (compojure.core/GET"/pong" [] (ok {:message"I dont."}))))
One can also change how the api handles non-compojure-api routes. Here, we break at compile-time:
(api {:api {:invalid-routes-fn compojure.api.routes/fail-on-invalid-child-routes}} (GET"/ping" [] (ok {:message"I satisfy the Routing protocol!"})) (compojure.core/GET"/pong" [] (ok {:message"I dont."}))); CompilerException clojure.lang.ExceptionInfo: Not all child routes satisfy compojure.api.routing/Routing. {:path nil, :method nil, :invalid [#function[compojure.core/if-method/fn--19598]]}
... or just ignore the bad routes
(api {:api {:invalid-routes-fnnil}} (GET"/ping" [] (ok {:message"I satisfy the Routing protocol!"})) (compojure.core/GET"/pong" [] (ok {:message"I dont."})))
To set up a "didn't match anything" handler within anapi, just do like you would do with ring/compojure.
(api inc-route (undocumented (compojure.route/not-found (ok {:not"found"}))))