Closed zerg000000 closed 7 years ago
You can define and use middleware around your endpoint routes just as you normally would. Not everything has to go into the :middleware
key of the system configuration map.
Here's an example:
(defn data-endpoint
[config]
(let [{:keys [db jwt]} config
backend (jws-backend jwt)]
(-> (routes
(wrap-routes
(GET "/data" {{start :start end :end} :params} (index db start end))
restrict #{:user :modeller :admin}))
(wrap-authorization backend)
(wrap-authentication backend))))
Actually, I am using similar method to get it to work, but the middleware configuration become everywhere. Take authentication as example, I will not use the auth middleware on only one endpoint. e.g. I need to have an oauth middleware to protect the whole api, so I need to wrap-oauth
in every endpoint
.
Another issue is the order of the middlewares. The config.edn
defined middlewares will wrap outer. If I have a middleware which have dependencies on component
and must be placed outer, I have to make all middlewares placed in endpoint
. e.g. I have a cors middleware need to read database for accepted domain name pattern, it must be placed at the first middleware. Therefore, the not-found
, ring-defaults
all will need to defined in endpoint
instead of inside config.edn
.
I don't currently have a good solution to this. You can put middleware in the endpoint of course. You could also create your own HandlerComponent
, since there's nothing in Duct that says you have to use the components it supplies for you. Perhaps use the Duct HandlerComponent
code as a basis, and give it the capability to pass in the containing component to the middleware functions.
I'll give this some thought, and try to get a solution in for the next version. I'm changing the configuration around anyway, so a better solution may present itself in the process.
Couldn't you create different root endpoints, and pass endpoints to those rather than the handler component?
So for example, you'd have the :app
handler component, and you could have both a :secure
and :insecure
endpoint. The app would depend on both (i.e. :app [:secure :insecure]
), and you would have overarching middleware applied to :app
. Then in the :secure
endpoint you'd manually wrap everything in auth middleware, but you could pass in other endpoint routes?
So like :secure [:ep1 :ep2 :db]
and you'd do something like:
(def secure-endpoint [config]
(let [{:keys [ep1 ep2 db]} config]
(wrap-authentication
(routes
(:build-routes ep1)
(:build-routes ep2))
;; stuff with db for auth)))
Thanks @adairdavid. I think this is a much better workaround that don't need to add middlewares everywhere.
Fixed by 0.9.0.
authentication/authorization are common use case of
middleware
, which always involve datastore (component
). However, under current implementation,middleware
must be a function called bysystem
, and have no chance to declare the dependencies of components and inject the component required by the middleware.