Skip to content
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

Added: Endware functionality #51

Open
wants to merge 11 commits into
base: master
Choose a base branch
from

Conversation

protometheus
Copy link

@protometheus protometheus commented Mar 25, 2020

For some use cases, the server may want to perform actions after the user's request has been serviced. Examples include:

  • logging the request
  • performing maintenance on resources
  • error handling
  • auditing
  • metrics
  • anything, really

For these purposes, I have added Endware as a solution. Endware is a http.Handler alias that allows alice users to create "after the fact" middleware in a similar way to how they create regular middleware. Endware is executed after all constructors and handlers have been invoked by Then(h)/ThenFunc(h).

Using endware is simple:
chain := alice.New(m1, m2)
becomes
chainWithEndware := alice.New(m1, m2).Finally(e1, e2)

This leads the flow of
chainWithEndware.Then(h)
to be
m1 -> m2 -> h -> e1 -> e2

The Append() functionality has been mirrored in AppendEndware(), as well as Extend being augmented to also extend endware. AfterFuncs() and AppendEndwareFuncs() are convenience method for those not dealing with http.Handlers directly.

This is still a WIP, just wanted to open it so you could take a look at your leisure.

Sorry in advance if this is not the way you are supposed to contribute code

@justinas
Copy link
Owner

Hi @protometheus and thank you for contributing.

Have you considered that any middleware as-is can run actions after the handler has served? Consider this:

func log(h http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        h.ServeHTTP(w, r)
        log.Println("A request has been served")
    })
}

fn main() {
    chain := alice.New(log).Then(myHandler)
}

The message will be printed out after myHandler has served.

@protometheus
Copy link
Author

protometheus commented Mar 26, 2020

Hey @justinas thanks for getting back so quickly! You are definitely correct that you can use the middleware as-is to represent endware. The only difference is that it is not clear from the Chain what the actual order of execution would be. So given:

func log1(h http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        h.ServeHTTP(w, r)
        log.Println("log1")
    })
}

func log2(h http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        h.ServeHTTP(w, r)
        log.Println("log2")
    })
}

func log3(h http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        log.Println("log3")
        h.ServeHTTP(w, r)
    })
}
func main() {
    chain := alice.New(log1, log2).Then(myHandler)
}

will do myHandler -> log2 -> log1. But

func main() {
    chain := alice.New(log1, log3).Then(myHandler)
}

will do log3 -> myHandler -> log1.

This determination gets more difficult the more complex the middleware. My intent was to make it more obvious the order of execution without having to actually look into each individual middleware to determine what the order is.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants