-
-
Notifications
You must be signed in to change notification settings - Fork 989
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How to pass information back to middleware? #914
Comments
Each middleware that needs to get information from downstream handlers needs to create their own storage in the context, e.g. a pointer to a value, that will be dereferenced and filled from within downstream (so it can bubble back up). There's no other way around this, as contexts get unwrapped on a way back up. I agree it would be useful to have a common middleware pkg for this. |
Thanks @VojtechVitek, are you suggesting that, I can do like this:
Then extract the value and fill it in handler:
|
You'd typically do something like the following, but more reasonably would be a "top level" handler that wraps any function into an handler that returns an error instead of passing state through context. type contextKey int
const errorHolderKey contextKey = 1
type errorHolder struct {
Error error
}
func Middleware() func(http.Handler) http.Handler {
return func(h http.Handler) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
ctx := req.Context()
holder := &errorHolder{}
ctx = context.WithValue(ctx, errorHolderKey, holder)
h.ServeHTTP(rw, req.WithContext(ctx))
// Do something with "holder"
})
}
}
func handler(w ..., r ...) {
something, err := processTheRequest(req)
if err != nil {
holder := context.Value(errorHolderKey).(*errorHolder)
// holder is a pointer to a errorHolder, this allows us to mutate it's state.
holder.Error = err
}
} Better would be func ErrorHandler(handler func(w http.ResponseWriter, r *http.Request) error) http.HandlerFund {
return func(w http.ResponseWriter, r *http.Request) {
if err := handler(w, r); err != nil {
// handle the error, display to user, assuming the error allows it.
}
}
}
USAGE:
mux.HandleFunc("/something", ErrorHandler(func(w http.ResponseWriter, r *http.Request) error {
if ( ... ) {
return fmt.Errorf("something broke")
}
return nil
})) |
@koote Yes, exactly. An example from a recent HTTP logger prototype:
|
The problem is in middleware some information is unknown, those information can only be determined later.
For example, I have an instrumentation middleware, which measures the request handling time (egress - ingress), and logs the error when request handling hit any errors, obviously those kind of information is only available when request reaches the handler implements business logic. When instrumentation middleware receives the request, it has no way to know how long will processing current request take or whether current request will encounter any errors.
However, in golang the only way to pass information when process http request is using the context object, the information flows from upstream to downstream, I can wrap any values or objects into context using
context.WithValue
and export the key, downstream modules can use the key to extract those data out from context, but I cannot do it in reverse order, passing the information from downstream back to upstream modules like middleware.Seems I need a mechanism to setup a callback hook in http process pipeline, when request pops up from the stack/pipeline, but I cannot find such a mechanism is supported by go-chi, does anyone have a good suggestion on this? Thanks.
The text was updated successfully, but these errors were encountered: