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

Flexible Handle Type #39

Open
rkusa opened this issue Oct 14, 2014 · 8 comments
Open

Flexible Handle Type #39

rkusa opened this issue Oct 14, 2014 · 8 comments
Labels
Milestone

Comments

@rkusa
Copy link

rkusa commented Oct 14, 2014

What do you think about making the Lookup method (or a similar one) more generic in terms of not enforcing a specific function footprint?

Currently, when building something on top of httprouter, each handler has to be a func(w http.ResponseWriter, r *http.Request, _ httprouter.Params). It would be nice if the Lookup method (or a similar one) returns an interface{}, while httprouter itself still enforces the httprouter.Handle type for its own public API. This would allow using custom handle types for modules build on top of httprouter.

@CoreyKaylor
Copy link

I think matching the method signature with the standard http.Handler interface would be ideal. Possibly when there are parameters you could do something similar to other packages and use the querystring with keys like ":querystringkey" ?

@tj
Copy link

tj commented Nov 27, 2014

+1 for matching http.Handler

@ydnar
Copy link

ydnar commented Jan 6, 2015

We implemented this on top of httprouter here: https://github.com/nbio/hitch

It uses per-request contexts from: https://github.com/nbio/httpcontext

@rogpeppe
Copy link
Contributor

rogpeppe commented Feb 5, 2015

I like it the way it is. With closures, it's already plenty flexible enough IMHO.

@rogpeppe
Copy link
Contributor

rogpeppe commented Feb 6, 2015

Another thought: if httprouter mirrored the http package and used a Handler type
instead of the Handle function type:

type Handler interface {
ServeHTTPRoute(http.RequestWriter, *http.Request, Params)
}

and the underlying storage was changed to store Handler rather than
Handle, then we'd get the above desired functionality automatically,
because it would be possible for callers to type-assert the returned
Handler to some other type.

Interface types can be nicer to debug too, being less opaque
than closures.

@julienschmidt julienschmidt added this to the v2 milestone May 27, 2015
@microamp
Copy link

I'd love this implemented too.

@l3pp4rd
Copy link

l3pp4rd commented Dec 9, 2015

It is possible to wrap request body as interface.
Allocation could be avoided if parameters would be built with each route on initialization

See the example:

package vanilla

// based on https://github.com/nbio/httpcontext
// authored by http://nb.io https://github.com/nbio.
// MIT License

import (
    "io"
    "net/http"
)

// Param is a single URL parameter, consisting of a key and a value.
type Param struct {
    Key   string
    Value string
}

// Params is a Param-slice, as returned by the router.
// The slice is ordered, the first URL parameter is also the first slice value.
// It is therefore safe to read values by the index.
type Params []Param

// ByName returns the value of the first Param which key matches the given name.
// If no matching Param is found, an empty string is returned.
func (ps Params) ByName(name string) string {
    for i := range ps {
        if ps[i].Key == name {
            return ps[i].Value
        }
    }
    return ""
}

// Parameters returns all path parameters for given
// request.
//
// If there were no parameters and route is static
// then nil is returned.
func Parameters(req *http.Request) Params {
    return parameterized(req).get()
}

type paramReadCloser interface {
    io.ReadCloser
    get() Params
    set(Params)
}

type parameters struct {
    io.ReadCloser
    all Params
}

func (p *parameters) get() Params {
    return p.all
}

func (p *parameters) set(params Params) {
    p.all = params
}

func parameterized(req *http.Request) paramReadCloser {
    p, ok := req.Body.(paramReadCloser)
    if !ok {
        p = &parameters{ReadCloser: req.Body}
        req.Body = p
    }
    return p
}

And usage example would be:

router.GET("/all/*node", http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
        fmt.Fprintf(w, "got var: "+Parameters(req).ByName("node"))
}))

@l3pp4rd
Copy link

l3pp4rd commented May 19, 2017

@tj guess you should be very excited to see this https://github.com/DATA-DOG/fastroute have a look at Router interface

maybe it is not the right place to add such a link, but well, worth it..

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

No branches or pull requests

8 participants