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

Support a mounted router that does not inherit middleware from the parent. #73

Closed
buro9 opened this issue Aug 16, 2016 · 4 comments
Closed

Comments

@buro9
Copy link

buro9 commented Aug 16, 2016

I have a chi server configured roughly:

package ui

import (
    "fmt"
    "net/http"

    "github.com/pressly/chi"
    "github.com/pressly/chi/middleware"
)

func ListenAndServe() error {
    r := chi.NewRouter()

    r.Use(middleware.RequestID)
    r.Use(middleware.Logger)
    r.Use(middleware.Recoverer)
    r.Use(middleware.RedirectSlashes)
    r.Use(session)

    r.Get("/", homeGet)

    r.Mount("/static", staticFiles())

    return http.ListenAndServeTLS(fmt.Sprintf(":%d", *listenPort), *certFile, *keyFile, r)
}

func staticFiles() http.Handler {
    r := chi.NewRouter()

    // Do nothing, but implement http.Handler
    r.Use(func(next http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            next.ServeHTTP(w, r)
        })
    })

    // Serve static files
    r.Mount("/",
        http.StripPrefix(
            "/static/",
            http.FileServer(http.Dir(*filesPath+"/static/")),
        ),
    )

    return r
}

It is going to serve a fairly large website and the standard handlers are web pages, and the mounted /static path are all of the static assets needed for the page, i.e. JS, CSS, images, etc.

I would like to add my own custom middleware to each web page that I serve, at the top level, and this will do things like look at whether a session cookie exists and put that in the context, determine basic authentication and authorisation as part of that session middleware and put that in the context too.

The issue I have is that adding such middleware results in the mounted static path handler receiving that middleware too.

I would like a form of Mount that resets the middleware (does not inherit).

This would allow the mounter to explicitly add only the middleware required by the mount point, and this in effect allows the mounted route to not have some other middleware (the stuff that is going to figure out session cookies, for which static files need not know about).

I'm not sure whether this is on your roadmap or whether you've encountered a similar need.

@pkieltyka
Copy link
Member

pkieltyka commented Aug 16, 2016

hey @buro9 - this can definitely be done, its just a matter of the approach. There isn't a way to opt out of a parent middleware stack, but that isn't quite necessary either.

I suggest something like..

func ListenAndServe() error {
    r := chi.NewRouter()

    r.Use(middleware.RequestID)
    r.Use(middleware.Logger)
    r.Use(middleware.Recoverer)
    r.Use(middleware.RedirectSlashes)

    // Protected routes.
    // This works by starting a new middleware stack for these routes.
    r.Group(func(r chi.Router) {
      r.Use(session)
      r.Get("/", homeGet)
    })

    r.Mount("/static", staticFiles())

    return http.ListenAndServeTLS(fmt.Sprintf(":%d", *listenPort), *certFile, *keyFile, r)
}

@pkieltyka
Copy link
Member

pkieltyka commented Aug 16, 2016

or..

func ListenAndServe() error {
    r := chi.NewRouter()

    r.Use(middleware.RequestID)
    r.Use(middleware.Logger)
    r.Use(middleware.Recoverer)
    r.Use(middleware.RedirectSlashes)

    // or..
    r.Mount("/", appRoutes())

    // or.. which is effectively the same as r.Mount("/", anotherR),
    // Route() is just an inline definition
    r.Route("/", func(r chi.Router) {
      r.Use(session)
      r.Get("/", homeGet)
    })


    r.Mount("/static", staticFiles())

    return http.ListenAndServeTLS(fmt.Sprintf(":%d", *listenPort), *certFile, *keyFile, r)
}

func appRoutes() http.Handler {
  r := chi.NewRouter()
  r.Use(session)
  r.Get("/", h)
  r.Get("/etc", h2)
  r.Mount("/other", r2) // which will nest the paths
  return r
}

@buro9
Copy link
Author

buro9 commented Aug 16, 2016

Thanks for that, I'll go with the first r.Group method as it reads nicer.

Actually going to take it further and put no middleware at the top level and then define groups for all routes.

i.e.

package ui

import (
    "fmt"
    "net/http"

    "github.com/pressly/chi"
    "github.com/pressly/chi/middleware"
)

func ListenAndServe() error {
    r := chi.NewRouter()

    // Pages group, handles all routes for pages and defines the appropriate
    // middleware for web pages
    r.Group(func(r chi.Router) {
        r.Use(middleware.RequestID)
        r.Use(middleware.Logger)
        r.Use(middleware.RedirectSlashes)
        r.Use(middleware.Recoverer)
        r.Use(session)

        r.Get("/", homeGet)
    })

    // Static file group, defines minimal middleware
    r.Group(func(r chi.Router) {
        r.Use(middleware.Logger)

        r.Mount("/static", staticFiles())
        r.Get("/favicon.ico", favicon)
        r.Get("/robots.txt", robots)
    })

    return http.ListenAndServeTLS(
        fmt.Sprintf(":%d", *listenPort),
        *certFile,
        *keyFile,
        r,
    )
}

func staticFiles() http.Handler {
    r := chi.NewRouter()

    // Do nothing, but implement http.Handler
    r.Use(func(next http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            next.ServeHTTP(w, r)
        })
    })

    // Serve static files
    r.Mount("/",
        http.StripPrefix(
            "/static/",
            http.FileServer(http.Dir(*filesPath+"/static/")),
        ),
    )

    return r
}

@buro9 buro9 closed this as completed Aug 16, 2016
@pkieltyka
Copy link
Member

:)

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

No branches or pull requests

2 participants