Giter Site home page Giter Site logo

go-chi / chi Goto Github PK

View Code? Open in Web Editor NEW
17.1K 209.0 954.0 3.66 MB

lightweight, idiomatic and composable router for building Go HTTP services

Home Page: https://go-chi.io

License: MIT License

Go 99.82% Makefile 0.18%
http router context middleware api rest-api microservices golang go

chi's People

Contributors

0daryo avatar alexandear avatar angelofallars avatar beornf avatar c2h5oh avatar chemidy avatar cyx avatar flimzy avatar gdm85 avatar joseustra avatar kanocz avatar lxfontes avatar mafredri avatar mmatczuk avatar muesli avatar mvdan avatar nkovacs avatar pearcedavis avatar pkieltyka avatar purificant avatar rafavaliev avatar shubhaankar-sharma avatar someone1 avatar thedevsaddam avatar therandomcharacter avatar tmatias avatar vasayxtx avatar vladdy avatar vojtechvitek avatar xiam avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

chi's Issues

Graceful shutdown

I'd like to explore ideas in how to gracefully shutdown a running http server. Now that go 1.7 supports context, we can use the request context to signal to all running requests the intent to shutdown the server and finish up their work because we're going to be exiting the program.

Initially the thought was to have a single parent server context where each routing context was derived, and if the parent was shutting down, cancelling the server context would signal to stop each child context. The problem here, is cancelling isn't the right thing to do, and it's actually the only option thats built into context.Context itself (see: golang/go#14660 (comment)).

On second thought, we could include a ShutdownCh on the request context, that would enhance each request to signal whenever the server is intending to finish up its work and stop processing. This is the best idea I have so far.

Middleware function gets recreated every function call

Steps to reproduce:

Use this middleware func:

func CtxMiddleware(next chi.Handler) chi.Handler {
  fmt.Println("mw recreated!")
  return chi.HandlerFunc(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
    ctx = context.WithValue(ctx, "key", "value")
    next.ServeHTTPC(ctx, w, r)
  })
}

"mw recreated!" should be echoed once on router.Use() call, but it echoes every time a request is handled.

Add support for fasthttp

fasthttp provides some considerable performance improvements.

I had forked chi and make it work for fasthttp. The result of the benchmark on my host show that chi/fasthttp is very close to echo/fasthttp and iris:

  • chi/fasthttp
benchmarking chi...
Running 10s test @ http://localhost:8080/teams/x-men/members/wolverine
  2 threads and 20 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.00ms    2.40ms  28.22ms   89.64%
    Req/Sec    53.71k     6.43k   72.56k    69.50%
  1076944 requests in 10.09s, 167.41MB read
Requests/sec: 106702.11
Transfer/sec:     16.59MB
benchmarking with pipleline...
Running 10s test @ http://localhost:8080
  2 threads and 20 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     3.80ms    7.96ms 124.13ms   84.49%
    Req/Sec   266.17k    16.76k  320.25k    74.50%
  5299709 requests in 10.01s, 823.83MB read
Requests/sec: 529503.08
Transfer/sec:     82.31MB
  • echo/fasthttp
benchmarking echo/fasthttp...
Running 10s test @ http://localhost:8080/teams/x-men/members/wolverine
  2 threads and 20 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     0.99ms    2.32ms  22.67ms   89.44%
    Req/Sec    53.56k     6.64k   90.73k    74.37%
  1070567 requests in 10.09s, 166.42MB read
Requests/sec: 106122.28
Transfer/sec:     16.50MB
benchmarking with pipleline...
Running 10s test @ http://localhost:8080
  2 threads and 20 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     3.99ms    7.70ms 131.53ms   83.88%
    Req/Sec   232.53k    14.14k  273.31k    72.50%
  4630831 requests in 10.01s, 719.86MB read
Requests/sec: 462480.63
Transfer/sec:     71.89MB

Would you like to add one branch for fasthttp?

Mux methods accept a HandlerFunc instead of Handler

I see the change that converts the mux signatures to accept an http.HandlerFunc instead of an http.Handler here: 2b1ed9c. There's no rationale given for the change.

In the std lib, http.Handler is the handler type and http.HandlerFunc is an adapter for functions to match the http.Handler interface. With the current mux implementation, anything that is an http.Handler type must be passed in like this:

router.Get("/", myHandler.ServeHTTP)

The ServeHTTP function is converted back into a Handler by being wrapped by the HandlerFunc type. It seems redundant and, in general I believe, breaks the expectation that a mux will accept a Handler implementation. This is also inconsistent with the Handle function of the mux and the middleware type definition of func(http.Handler) http.Handler.

Most projects in the pre-1.7 space handled this case by having two signatures for each mux function:

func (m *Mux) Get(path string, h http.Handler) {}
func (m *Mux) GetF(path string, h http.HandlerFunc) {}

I'd like to get http.Handler support back and am willing to put in a patch if we can figure out the right implementation for this project.

wildcards router not working when path conflict

hi, now I am using chi on golang 1.7, I meet such a problem:

    r := chi.NewRouter()

    // A good base middleware stack
    r.Use(middleware.Logger)
    r.Use(middleware.Recoverer)

    r.Options("/*", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("hi"))
    })

    r.Get("/foo", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("foo"))
    })

    http.ListenAndServe(":3333", r)

a options request for "/foo" just return 405 method not allow.......:(

v2 Roadmap

Below are remaining things I'd like to complete for v2 release:

  • Finalize chi.Router interface
  • Syntax: chi.URLParam(r, "id")
  • Example of Handler unit tests in v2
  • Include RoutePattern on the request context, which is the pattern string that matches a request, helpful for instrumentation middlewares (#45)
  • Example with handlers on a struct
  • Finalize render.Presenter (#47)
  • Godoc
  • Document examples (render, etc.)
  • Updated benchmarks
  • Real-life example with chi+config+Makefile+upper+tests+templates+http2

Weird (Incorrect?) URLParam results

I am not sure if this is a bug, or me just not using the routing correctly. I have some odd routes:

    r.Route("/api/v1", func(r chi.Router) {
        r.Route("/files", func(r chi.Router) {
            r.Get("/:foo/hello/:bar", a)
            r.Get("/:bar/world/:baz", b)
            r.Get("/:bar/sups/:baz/:foo", c)
        })
    })

chi correctly routes the request, however the params are not set correctly. For example a request to /api/v1/files/a/sups/b/c correctly routes to func c() however the decoded params look like {"bar":"","baz":"b","foo":"a","req":"c"}

Here is an example with failing tests:

https://gist.github.com/DylanJ/4d3c7d1ea6f99255df823be717e99bcc

JSON response

Now it is not very comfortable to send a JSON response:

type Foo struct {
    Bar string
}

func foo(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")

    b, err := json.Marshal(&Foo{Bar: "bar"})
    if err != nil {
        http.Error(w, err.Error(), 422)
        return
    }
    w.WriteHeader(http.StatusOK)
    w.Write(b)
}

What about add the JSON(code int, obj interface{}) method like gin or echo?

Getting more done in GitHub with ZenHub

Hola! @xiam has created a ZenHub account for the pressly organization. ZenHub is the leading team collaboration and project management solution built for GitHub.


How do I use ZenHub?

To get set up with ZenHub, all you have to do is download the browser extension and log in with your GitHub account. Once you do, youโ€™ll get access to ZenHubโ€™s complete feature-set immediately.

What can ZenHub do?

ZenHub adds a series of enhancements directly inside the GitHub UI:

  • Real-time, customizable task boards for GitHub issues;
  • Burndown charts, estimates, and velocity tracking based on GitHub Milestones;
  • Personal to-do lists and task prioritization;
  • โ€œ+1โ€ button for GitHub issues and comments;
  • Drag-and-drop file sharing;
  • Time-saving shortcuts like a quick repo switcher.

Add ZenHub to GitHub

Still curious? See more ZenHub features or read user reviews. This issue was written by your friendly ZenHub bot, posted by request from @xiam.

ZenHub Board

Route params mixup

First of all, love the project & the philosophy behind it - it's the perfect approach for Go 1.7+ IMO ๐Ÿ‘

But I'm having an issue with some supporting some legacy route formats. Requests map to the correct handlers but the parameters don't match up correctly. Here's a simplified example replacing the routes in TestTree to show the issue I'm having:

func TestTree(t *testing.T) {
    hDate := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
    hCat := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})

    tr := &node{}

    tr.InsertRoute(mGET, "/items/:year/:month", hDate)
    tr.InsertRoute(mGET, "/items/:category", hCat)

    tests := []struct {
        r string            // input request path
        h http.Handler      // output matched handler
        p map[string]string // output params
    }{
        {r: "/items/2016/08", h: hDate, p: map[string]string{"year": "2016", "month": "08"}},
        {r: "/items/things", h: hCat, p: map[string]string{"category": "things"}},
    }

That results in:

--- FAIL: TestTree (0.00s)
    tree_test.go:59: input [1]: find '/items/things' expecting params:map[category:things] , got:map[year:things]

Note that the /items/:category handler is correctly used but the parameter is called :year instead from the previous route.

It looks like it's due to how the tree is built with the name of the parameter ignored so it then gets the name from the previous node:

2016/08/23 10:40:56 [node 0 parent:0] typ:0 prefix: label: numEdges:1 isLeaf:false
2016/08/23 10:40:56 [node 1 parent:0] typ:0 prefix:/items/ label:/ numEdges:1 isLeaf:false
2016/08/23 10:40:56 [node 2 parent:1] typ:2 prefix::year label:: numEdges:1 isLeaf:true handler:map[512:<nil> 4:0x90450]
2016/08/23 10:40:56 [node 3 parent:2] typ:0 prefix:/ label:/ numEdges:1 isLeaf:false
2016/08/23 10:40:56 [node 4 parent:3] typ:2 prefix::month label:: numEdges:0 isLeaf:true handler:map[512:<nil> 4:0x90440]

Is this simply not supported (I know many Go routers are strict about the route variations, others such as Echo cope better with suffix variations) or is it a bug?

Many thanks.

Hi all, who's using chi in production?

Hey everyone, I'm curious to hear which companies are using chi in production, if you see this post and use it in your products, please let me know. I'd like to include a list in the README, it's inspiring to hear all the awesome companies using chi and it helps other get comfortable to adopt it with success from others.

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

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.

404 not found when expecting 405 method not allowed

A request sent to an existing resource without an handler for that method should return http 405.
If I understand correctly this should be the behaviour in

// Check if method is supported by chi
method, ok := methodMap[r.Method]
if !ok {
    methodNotAllowedHandler(ctx, w, r)
    return
}

but I am getting always an http 404 error.
methodMap is initialized at startup and a method will be always found in the map, after that an handler will not be found and we finish in the handler not found case.

Maybe this control has been implemented to manage unexpected http methods that the router will not be able to handle in every case. Am I right?

Websockets

Hi! Not sure what's going on, but it seems that gorilla websockets (and native ones by the looks of it) aren't compatible with writers supplied by Chi.

    hijacker, ok := w.(http.Hijacker)
    if !ok {
        return nil, nil, errors.New("the ResponseWriter doesn't support the Hijacker interface")
    }
    return hijacker.Hijack()

Hijack method here returns http: Hijack is incompatible with use of CloseNotifier error (https://golang.org/src/net/http/server.go#L154)

How to handle "/" with trailing slashes in a mounted router?

Thanks for this great framework, currently I'm moving a project to chi, I want to handle all these URLs: /blog, /blog/, /blog/list.
I can do this in a plain router like this:

// r is a Router variable
r.Get("/blog", blogHandler)
r.Get("/blog/", blogWithTrailingSlashHandler)
r.Get("/blog/list", blogListHandler)

but if I mount a nested router for /blog, how can I handle /blog/?

// r is a Router variable
blogRouter := chi.NewRouter()
blogRouter.Get("/", blogHandler)
// this does not work
blogRouter.Get("//", blogWithTrailingSlashHandler)
blogRouter.Get("/list", blogListHandler)
r.Mount("/blog", blogRouter)

Remove github restriction on markdown docs links

Relative links in markdown work on more places than just github. They work very nicely in our on-premis gitlab installation for example.

Is there any harm in removing this restriction? If that is not desired, could an additional option be added to MarkdownOpts to force link generation?

How to get params without route context?

Is it possible to get param from the handler without route context?

When i'm testing handler, i need to be able to retrieve params with chi.URLParam(), but when i try to do it outside router, i'm getting panic

Using Mux.pool

Hello, I'm using your http router chi. I like it some much, but there are several thing that I don't understand at all. Could you explain me that?

  1. Mux.pool is a pool of chi.Context. If I understood well, only reason why, is you are trying to have less allocation. But If this is true, why you don't have just one global pool for every Mux? It should not be more interesting share bigger structures like Mux into pool?
  2. In Mux.Group you again create new Mux - that should be copy of previous one. But it does not have initialized pool (neither shared with parent, nor created new). Is it good?
  3. In Mux.ServeHTTPC you use pool.Get() to obtain new Context. After that, next handler is called and at the end, context is reseted and return back to pool. It sound good, but if appeared some panic during next handler, context won't be returned back. Is it an intention or mistake?

FileServer prefix path problem

import (
    "os"
    "net/http"
    "github.com/pressly/chi"
)

r := chi.NewRouter()

workDir, _ := os.Getwd()
r.FileServer("/static", http.Dir(filepath.Join(workDir, "static")))

Expected URL: /static/moment/moment.min.js
But I got: /moment/moment.min.js

How can I set it so the /static prefix is preserved?

runtime error: invalid memory address or nil pointer dereference

I accidentally mounted an empty router, I didn't realise it, and it was hard to find out the problem due to the weird error message.

Code to reproduce this issue

package main

import (
    "net/http"
    "github.com/pressly/chi"
)

func main() {
    r := chi.NewRouter()

    apiRouter := chi.NewRouter()
    r.Handle("/api", apiRouter)

    http.ListenAndServe(":3000", r)
}

127.0.0.1:3000/abc: 404 page.
127.0.0.1:3000/api: panics.
127.0.0.1:3000/api/abc: panics.

Full error log:

2016/05/21 23:18:24 http: panic serving 127.0.0.1:49239: runtime error: invalid memory address or nil pointer dereference
goroutine 8 [running]:
net/http.(*conn).serve.func1(0xc82005e980)
    /usr/local/Cellar/go/1.6/libexec/src/net/http/server.go:1389 +0xc1
panic(0x325240, 0xc82000a1a0)
    /usr/local/Cellar/go/1.6/libexec/src/runtime/panic.go:426 +0x4e9
github.com/pressly/chi.(*Mux).ServeHTTPC(0xc8200125a0, 0x1a55910, 0xc820010cc0, 0x1a55858, 0xc8200f41a0, 0xc8200c2380)
    /Users/Mgen/go/src/github.com/pressly/chi/mux.go:283 +0x648
github.com/pressly/chi.treeRouter.ServeHTTPC(0xc820014e40, 0x0, 0x1a55910, 0xc820010cc0, 0x1a55858, 0xc8200f41a0, 0xc8200c2380)
    /Users/Mgen/go/src/github.com/pressly/chi/mux.go:352 +0x324
github.com/pressly/chi.(*treeRouter).ServeHTTPC(0xc82000af80, 0x1a55910, 0xc820010cc0, 0x1a55858, 0xc8200f41a0, 0xc8200c2380)
    <autogenerated>:18 +0xca
github.com/pressly/chi.(*Mux).ServeHTTPC(0xc820012550, 0x0, 0x0, 0x1a55858, 0xc8200f41a0, 0xc8200c2380)
    /Users/Mgen/go/src/github.com/pressly/chi/mux.go:266 +0x112
github.com/pressly/chi.(*Mux).ServeHTTP(0xc820012550, 0x1a55858, 0xc8200f41a0, 0xc8200c2380)
    /Users/Mgen/go/src/github.com/pressly/chi/mux.go:258 +0x4b
net/http.serverHandler.ServeHTTP(0xc82005e080, 0x1a55858, 0xc8200f41a0, 0xc8200c2380)
    /usr/local/Cellar/go/1.6/libexec/src/net/http/server.go:2081 +0x19e
net/http.(*conn).serve(0xc82005e980)
    /usr/local/Cellar/go/1.6/libexec/src/net/http/server.go:1472 +0xf2e
created by net/http.(*Server).Serve
    /usr/local/Cellar/go/1.6/libexec/src/net/http/server.go:2137 +0x44e

chi's future

Instead of an additional parameter of type context.Context with a new handler type, an alternative approach was favoured in the standard library.
See: golang/go@c1c7547

graceful shutdown?

@pkieltyka, How would you implement graceful feature for shutdown? Do we need it? If not, how would you implement this feature along side with chi?

Type chi.Middleware

Can we type func(next chi.Handler) chi.Handler ?

I use New.. methods for some middleware to add stuff to them, which results in:

func NewSomethingCtx(stuff *Stuff)  func(next chi.Handler) chi.Handler  {
    return func(next chi.Handler) chi.Handler {
        // stuff can be used here to set something up (should be done once, as mentioned in #10).
        return chi.HandlerFunc(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
            // stuff can also be used here, on each request.
            // actual middleware.
        })
    }
}

If would be nice if we could let NewSomethingCtx return a chi.Middleware.

Allow overwrite function methodNotAllowedHandler

I am just starting to use your project which seems excellent to me, but got into trouble with hard coded methodNotAllowedHandler function. I have to return JSON object on every error (like in JSON API spec) and I am not able to specify my own response to this error. Or can you suggest any solution to this? Thanks.

Configurable logger middleware

The middlewares in the repo work really well, cheers for that.

I use chi to serve dynamic content as well as static content. I have the logger middleware set up on the whole thing, which is good but it's too verbose for static content.

I thought about enabling it only for non-static content, but I would still like to get logs about 4XX's (and possibly 5XX's?) on the static content, for example if a link to an image is broken.

As far as I can tell the logger middleware isn't configurable, but some others are. A few options come to mind:

  • Fork the middleware and create a new configurable middleware (e.g. with a mode flag that tells it what is to be logged) - this would mean code duplication, as far as I can tell
  • Modify the current middleware - is this possible? I assume not, thinking that with v2 the api is frozen.
  • Make the middlewares extendible at some level to enable modifications and tweaks like these - don't know if this makes sense at all

Also, I was thinking that chi would really benefit from a "contrib" middleware repo. Kind of like what golang.org/x/ is to std. Only add to chi what is really basic and important, and the rest can live in a separate repo like chi-contrib where the community plays a bigger part. This would also allow changing the API of certain middlewares without breaking backwards compatibility, as these would not be released in tags along with the main repo.

appengine

i am stuck trying to use chi router for an appengine app, but because it uses "context" package from the standard library it can not build ... anyother trick that forking the router and replace "context" by "golang.org/x/net/context" ... not very happy to be stuck by this delay and inconsistency between golang 1.7 and appengine standard environment ...

[Feature Request]Static File serving

When I'm using chi for a project with swagger-ui, I feel it is not so convenient for serving static files. What about adding the static file serving feature like this:

func (mx *Mux) Static(path, root string) {
    fs := http.StripPrefix(path, http.FileServer(http.Dir(root)))
    mx.Get(path+"/*", func(w http.ResponseWriter, r *http.Request) {
        fs.ServeHTTP(w, r)
    })
}

Ability to set the base context

I'm not sure there's a facility for this already, but after searching through the code and docs, there doesn't seem to be a way to do it without making a middleware.

Basically the usecase is like:

ctx := context.WithValue(context.Background(), "foo", "bar")

mux := chi.NewMux()
mux.SetContext(ctx) // or could be in the constructor, or a functional option thing?

In any case the underlying goal is to just easily set the base context. Let me know if this makes sense.

http.Handler vs http.HandlerFunc

I couldn't grep it in the source code,

Is there a way to handle http.Handler using chi.NewRouter().Get(...) or Post(...)?

Should there be a GetFunc(pattern string, h http.HandlerFunc) and Get(pattern string, h http.Handler)?

If I have to choose one, http.Handler is more flexible than its func alternative.

v2 branch

Could you move the master branch to a v2 branch in order to be compatible with gopkg.in and glide ?

Subrouter hides parent routes

package main

import (
    "log"
    "net/http"

    "github.com/pressly/chi"
)

func main() {
    r := chi.NewRouter()

    // Parent route -- /foo
    r.Get("/foo", func(w http.ResponseWriter, r *http.Request) {
        log.Println("/foo")
    })

    // Subrouter -- defining /foo/bar
    r.Mount("/foo", subrouter())

    http.ListenAndServe(":3333", r)
}

func subrouter() http.Handler {
    r := chi.NewRouter()
    r.Get("/bar", func(w http.ResponseWriter, r *http.Request) {
        log.Println("/foo/bar")
    })
    return r
}
  1. $ curl -v localhost:3333/foo
    > GET /foo HTTP/1.1
    < HTTP/1.1 404 Not Found
    Not Found

The route **wasn't matched** -- seems like the subrouter hid it.
  1. $ curl -v localhost:3333/foo/
    > GET /foo/ HTTP/1.1
    < HTTP/1.1 404 Not Found
    404 page not found

Again.. but with **different body** -- different NotFound handler?
  1. $ curl -v localhost:3333/foo/bar
    > GET /foo/bar HTTP/1.1
    < HTTP/1.1 200 OK
    /foo/bar

OK

Ability to join routers.

It could be useful to be able to join multiple routers (but not forcing the second router to have a specific path).

My handlers is split into multiple packages and each package has NewRouter() function returning chi.Router.

The idea is for my main application, to import packages and then do

mainRouter.Join(package1.NewRouter())
mainRouter.Join(package2.NewRouter())

I'm currently doing it this way, but it's not that elegant

package1.AddRoutes(mainRouter)
package2.AddRoutes(mainRouter)

Mount is not useful, because it forces to pick a specific path for router.

SkipTrailingSlash

In project, that I'm writing for some handlers I need to remove trailing slash (REST server, where I cannot influence form of requests). I spent a few hours yesterday, but I have not find any good looking solution.

Problem, that when I handle something inside r.Group or r.Mount, inside this handler next handle to root does not work.
Example: In your example account code I don't want be able to handle only
curl 127.0.0.1:3333/accounts/123
but also
curl 127.0.0.1:3333/accounts/123/
with the same handler.

Inside accountsRouter there is no good looking solution how to use the same handler for both cases.
r.Get("/", getAccount)
handles only the first form. When I tried to change it into r.Get("//", ...) it does not handle anything.

Possible solution (if I understand well are):
a) Use two handlers for r.Route("/:accountID", ...) and r.Route("/:accountID/", ...) with same "next" function. That looks confusing, and also I can't use anonymous function. Also when some trailing slashes can appears in many handlers, I have to repeat such bad code for each handler.

b) Write middleware SkipTrailingSlash, that checks, whether path ends with /, and if it, just remove it. I made it, it works fine, but I have to modify chi.Context, because routeCtxKey is not visible neither from my application nor chi.middleware. I had to change it, but it is not nice, because main idea was publish internal routing mechanism and make it visible for everyone.

c) Add flag SkipTrailingSlash, into Mux. I thing that this is the best idea yet, but I welcome any better proposals. I could write it, that is not problem, but do you think it is a good idea?

Render: managing request and response payload as types

The render subpkg is pretty cool, and its great for managing the different kinds of content types a response may need, including streams. However, one of the important parts for a maintainable API is to manage the request and response payloads (the inputs and the outputs of an endpoint).

For request payloads, one simple idea is to have a Bind() middleware used in chi's r.With() inline middleware routing, that will take a request body and unmarshal it to a kind of struct. Perhaps.

type ArticleRequest {
  ID int64 `json:"id"`
  Name string `json:"name"`
}
// ...
r.Get("/", index)
r.With(render.Request(&ArticleRequest{})).Post("/articles", newArticle)

.. something like that.. the render.Request() would make a new &ArticleRequest{} object and put it on the context under render.RequestKey or something.. its a decent plan, except it would require some kind of reflection to make the new &ArticleRequest{} object.

As for response payloads, that should be much simpler, just..

type ArticleResponse {
  *data.Article // embed the core Article type
  // add more methods on it..
}
//...
render.Respond(w, r, &ArticleResponse{a})

if someone has other ideas too that would be awesome!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.