This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2017-03-24
Channels
- # aleph (4)
- # beginners (93)
- # cider (7)
- # cljs-dev (16)
- # cljsrn (5)
- # clojure (192)
- # clojure-dusseldorf (3)
- # clojure-italy (14)
- # clojure-russia (16)
- # clojure-serbia (1)
- # clojure-spec (85)
- # clojure-taiwan (1)
- # clojure-uk (79)
- # clojurescript (188)
- # code-reviews (9)
- # core-async (2)
- # crypto (1)
- # cursive (26)
- # datomic (21)
- # heroku (1)
- # hoplon (3)
- # jobs (7)
- # jobs-discuss (20)
- # jobs-rus (13)
- # off-topic (77)
- # om (15)
- # onyx (23)
- # pedestal (94)
- # planck (11)
- # proton (10)
- # protorepl (1)
- # re-frame (16)
- # ring (22)
- # ring-swagger (9)
- # rum (2)
- # specter (18)
- # testing (2)
- # untangled (14)
- # vim (12)
- # yada (58)
I would go with 1, that way you don't have to update the Vase docs everytime the Pedestal docs changes
To follow up on my earlier issue, my fairly new pedestal service started throwing exceptions when starting the REPL, complaining that No matching ctor found for class io.pedestal.http.ring_middlewares$response_fn_adapter$fn__11673
. After numerous lein clean
, deleting and re-loading dependencies in ~/.m2
, and other suggestions, I was still getting the error, but today it stopped happening.
@manutter51 I hate when bugs just go away on their own. That means they can come back on their own, too.
I didn’t have time this morning to fully test it, but the thing that seems to have done the trick is moving the session configuration to the service map in service.clj
. I thought I had done that earlier, but I realized I had added it to the map in server.clj
that run-dev
merges with the main service map.
moving the session setting to the right map seems to have fixed the issue
though I have no clue why that particular cause would have that particular effect.
Is the only way to exit the interceptor chain by throwing an exception? For example, I have 3 interceptors for my route and interceptor1 decides that the request should not be completed right now (maybe it was an invalid request, user not authorized, the server has other processing duties, etc.). Does interceptor1 have to throw an exception in order to stop subsequent interceptors from running, or is there another way?
I believe I found my answer 🙂 http://pedestal.io/api/pedestal.interceptor/io.pedestal.interceptor.chain.html#var-terminate
Once an interceptor returns a response, the execution of downstream interceptor :enter
fns is terminated
so to state it correctly, once an interceptor assoc’s a response to the context, the execution of downstream interceptor :enter
fns is terminated
Hmm, I think you’re right about the docs (http://pedestal.io/reference/interceptors), they don’t mention this.
Not sure if you'd ever want to do this but is it possible to terminate the :leave
execution as well?
Wait I don't think assoc
ing a :response
onto the context stops the interceptor flow. I just tried it and my second interceptor got called
Yeah the second interceptor is definitely getting called even though the context has a :response
assoc
'ed on it.
(def echo
{:name :echo
:enter
(fn [context]
(clojure.pprint/pprint context)
(let [request (:request context)
response (ok context)]
(assoc context :response response)))})
(def echo2
{:name :echo2
:enter (fn [context]
(println "echo2")
context)})
(def routes
(route/expand-routes
#{["/foo" :post [echo echo2] :route-name :foo]}))
If instead echo
calls terminate
:
(def echo
{:name :echo
:enter
(fn [context]
(clojure.pprint/pprint context)
(let [request (:request context)
response (ok context)]
(chain/terminate (assoc context :response response))))})
then echo2 is not printed.I’m going to play with this because it doesn’t behave how I expected. Perhaps things have changed!
This is the ns I'm messing with: http://pastebin.com/eMTkYEwu
@kenny, so this example (https://gist.github.com/ddeaguiar/74eed4784848e49fa83179a2086aa9ea) behaves as I described. Because the :io.pedestal.interceptor.chain/terminators
coll on the context has a single predicate which is set by the default servlet chain provider. https://github.com/pedestal/pedestal/blob/41337e41ae773f3c17baeb1c244be13ddf6c28db/service/src/io/pedestal/http/impl/servlet_interceptor.clj#L231
In my example, when you request
you’ll see this in the logs
16:01:31.660 [qtp2141354770-51] INFO terminator.service - {:msg "In Terminator", :line 23} │
16:01:31.663 [qtp2141354770-51] INFO terminator.service - {:terminators (#function[io.pedestal.http.impl.servlet-interceptor│
16:01:31.663 [qtp2141354770-51] DEBUG io.pedestal.interceptor.chain - {:in check-terminators, :terminate? true, :execution-id│
16:01:31.664 [qtp2141354770-51] DEBUG io.pedestal.interceptor.chain - {:in leave-all, :execution-id 19, :line 239} │
16:01:31.666 [qtp2141354770-51] DEBUG io.pedestal.interceptor.chain - {:interceptor :terminator.service/terminator, :stage :l│
16:01:31.666 [qtp2141354770-51] INFO terminator.service - {:msg "Leaving Terminator", :line 29}
I added the following line to my logback.xml
file to log what the interceptor chain is doing:
<logger name="io.pedestal.interceptor.chain" level="DEBUG" />
I copied this function from the guide.
(defn response [status body & {:as headers}]
{:status status :body body :headers headers})
If you don't provide headers (e.g. (response 200 "foo")
) then it returns {:status 200, :body "foo", :headers {}}
which is not a ring response.
(ring.util.response/response? (response 200 "foo"))
=> false
Instead you need to do this:
(defn response [status body & {:as headers}]
{:status status :body body :headers (or headers {})})
BTW, I really appreciate you asking this question. I’ve got a much deeper understanding of chain providers now!
Once you do that the interceptor chain is terminated when a :response
is assoc
'ed onto the context.
like if something’s assoc’d to the context as :response
but it’s not a valid response, then it should at least log a warning
If you create a custom chain provider and don’t need terminators, then don’t add io.pedestal.interceptor.chain/terminators
to the context
For example, the Fast Pedestal example creates an initial context https://github.com/pedestal/pedestal/blob/master/samples/fast-pedestal/src/fast_pedestal/fasterjetty_service.clj#L67
So the nuance is, if you’re using a the default servlet chain provider, or another chain provider which terminates when a response is assoc’d to the context, then you don’t need to call terminate
I’ll add a quote from ohpauleez > please don't terminate unless the situation is dire
On a side note, this came up because I was considering things like authentication. It's surprising that by default the only Pedestal authentication library geheimtur uses exceptions for control flow: https://github.com/propan/geheimtur/blob/master/src/geheimtur/interceptor.clj#L14
Just because other libs do it doesn't mean it was the right decision. IMO exceptions should only be used for exceptional things, not logic.
which, btw, I created a sample demonstrating it’s usage: https://github.com/pedestal/pedestal/pull/503
https://github.com/pedestal/pedestal/tree/master/samples/fast-pedestal > Avoid throwing exceptions (especially in interceptors)
So, for example, with buddy-auth, you don’t have to throw, you could just return a response as well
Right but because they have throwing as a default usage it hints others that the use of exceptions is acceptable for control flow.
So when I write interceptors for things like validation, I short-circuit by returning a response