Browse Source

Use "vanity" types to clarify use of interface{}

In order to expose a convenient API, it's unfortunately necessary to
lean on Go's interface{} quite a bit: in reality we only accept a
handful of types at each call site, but it's impossible to express this
using the type system.

Prior to this commit (as well as the ParsePattern commit), I exposed all
of this type information in the form of an enormous comment on web.Mux,
however this was pretty gross. Instead, let's use "vanity" type aliases
for interface{} to provide documentation about which types are accepted
where. This will hopefully make the API documentation easier to skim,
but doesn't affect any existing uses of Goji.

This commit also clarifies a couple other parts of the documentation.
Carl Jackson 11 years ago
parent
commit
d65ed21ea9
4 changed files with 120 additions and 119 deletions
  1. +14
    -14
      default.go
  2. +43
    -76
      web/mux.go
  3. +4
    -27
      web/pattern.go
  4. +59
    -2
      web/web.go

+ 14
- 14
default.go View File

@ -19,84 +19,84 @@ func init() {
// Use appends the given middleware to the default Mux's middleware stack. See // Use appends the given middleware to the default Mux's middleware stack. See
// the documentation for web.Mux.Use for more information. // the documentation for web.Mux.Use for more information.
func Use(middleware interface{}) {
func Use(middleware web.MiddlewareType) {
DefaultMux.Use(middleware) DefaultMux.Use(middleware)
} }
// Insert the given middleware into the default Mux's middleware stack. See the // Insert the given middleware into the default Mux's middleware stack. See the
// documentation for web.Mux.Insert for more information. // documentation for web.Mux.Insert for more information.
func Insert(middleware, before interface{}) error {
func Insert(middleware, before web.MiddlewareType) error {
return DefaultMux.Insert(middleware, before) return DefaultMux.Insert(middleware, before)
} }
// Abandon removes the given middleware from the default Mux's middleware stack. // Abandon removes the given middleware from the default Mux's middleware stack.
// See the documentation for web.Mux.Abandon for more information. // See the documentation for web.Mux.Abandon for more information.
func Abandon(middleware interface{}) error {
func Abandon(middleware web.MiddlewareType) error {
return DefaultMux.Abandon(middleware) return DefaultMux.Abandon(middleware)
} }
// Handle adds a route to the default Mux. See the documentation for web.Mux for // Handle adds a route to the default Mux. See the documentation for web.Mux for
// more information about what types this function accepts. // more information about what types this function accepts.
func Handle(pattern interface{}, handler interface{}) {
func Handle(pattern web.PatternType, handler web.HandlerType) {
DefaultMux.Handle(pattern, handler) DefaultMux.Handle(pattern, handler)
} }
// Connect adds a CONNECT route to the default Mux. See the documentation for // Connect adds a CONNECT route to the default Mux. See the documentation for
// web.Mux for more information about what types this function accepts. // web.Mux for more information about what types this function accepts.
func Connect(pattern interface{}, handler interface{}) {
func Connect(pattern web.PatternType, handler web.HandlerType) {
DefaultMux.Connect(pattern, handler) DefaultMux.Connect(pattern, handler)
} }
// Delete adds a DELETE route to the default Mux. See the documentation for // Delete adds a DELETE route to the default Mux. See the documentation for
// web.Mux for more information about what types this function accepts. // web.Mux for more information about what types this function accepts.
func Delete(pattern interface{}, handler interface{}) {
func Delete(pattern web.PatternType, handler web.HandlerType) {
DefaultMux.Delete(pattern, handler) DefaultMux.Delete(pattern, handler)
} }
// Get adds a GET route to the default Mux. See the documentation for web.Mux for // Get adds a GET route to the default Mux. See the documentation for web.Mux for
// more information about what types this function accepts. // more information about what types this function accepts.
func Get(pattern interface{}, handler interface{}) {
func Get(pattern web.PatternType, handler web.HandlerType) {
DefaultMux.Get(pattern, handler) DefaultMux.Get(pattern, handler)
} }
// Head adds a HEAD route to the default Mux. See the documentation for web.Mux // Head adds a HEAD route to the default Mux. See the documentation for web.Mux
// for more information about what types this function accepts. // for more information about what types this function accepts.
func Head(pattern interface{}, handler interface{}) {
func Head(pattern web.PatternType, handler web.HandlerType) {
DefaultMux.Head(pattern, handler) DefaultMux.Head(pattern, handler)
} }
// Options adds a OPTIONS route to the default Mux. See the documentation for // Options adds a OPTIONS route to the default Mux. See the documentation for
// web.Mux for more information about what types this function accepts. // web.Mux for more information about what types this function accepts.
func Options(pattern interface{}, handler interface{}) {
func Options(pattern web.PatternType, handler web.HandlerType) {
DefaultMux.Options(pattern, handler) DefaultMux.Options(pattern, handler)
} }
// Patch adds a PATCH route to the default Mux. See the documentation for web.Mux // Patch adds a PATCH route to the default Mux. See the documentation for web.Mux
// for more information about what types this function accepts. // for more information about what types this function accepts.
func Patch(pattern interface{}, handler interface{}) {
func Patch(pattern web.PatternType, handler web.HandlerType) {
DefaultMux.Patch(pattern, handler) DefaultMux.Patch(pattern, handler)
} }
// Post adds a POST route to the default Mux. See the documentation for web.Mux // Post adds a POST route to the default Mux. See the documentation for web.Mux
// for more information about what types this function accepts. // for more information about what types this function accepts.
func Post(pattern interface{}, handler interface{}) {
func Post(pattern web.PatternType, handler web.HandlerType) {
DefaultMux.Post(pattern, handler) DefaultMux.Post(pattern, handler)
} }
// Put adds a PUT route to the default Mux. See the documentation for web.Mux for // Put adds a PUT route to the default Mux. See the documentation for web.Mux for
// more information about what types this function accepts. // more information about what types this function accepts.
func Put(pattern interface{}, handler interface{}) {
func Put(pattern web.PatternType, handler web.HandlerType) {
DefaultMux.Put(pattern, handler) DefaultMux.Put(pattern, handler)
} }
// Trace adds a TRACE route to the default Mux. See the documentation for // Trace adds a TRACE route to the default Mux. See the documentation for
// web.Mux for more information about what types this function accepts. // web.Mux for more information about what types this function accepts.
func Trace(pattern interface{}, handler interface{}) {
func Trace(pattern web.PatternType, handler web.HandlerType) {
DefaultMux.Trace(pattern, handler) DefaultMux.Trace(pattern, handler)
} }
// NotFound sets the NotFound handler for the default Mux. See the documentation // NotFound sets the NotFound handler for the default Mux. See the documentation
// for web.Mux.NotFound for more information. // for web.Mux.NotFound for more information.
func NotFound(handler interface{}) {
func NotFound(handler web.HandlerType) {
DefaultMux.NotFound(handler) DefaultMux.NotFound(handler)
} }

+ 43
- 76
web/mux.go View File

@ -5,38 +5,18 @@ import (
) )
/* /*
Mux is an HTTP multiplexer, much like net/http's ServeMux.
Routes may be added using any of the various HTTP-method-specific functions.
When processing a request, when iterating in insertion order the first route
that matches both the request's path and method is used.
There are two other differences worth mentioning between web.Mux and
http.ServeMux. First, string patterns (i.e., Sinatra-like patterns) must match
exactly: the "rooted subtree" behavior of ServeMux is not implemented. Secondly,
unlike ServeMux, Mux does not support Host-specific patterns.
If you require any of these features, remember that you are free to mix and
match muxes at any part of the stack.
In order to provide a sane API, many functions on Mux take interface{}'s. This
is obviously not a very satisfying solution, but it's probably the best we can
do for now. Instead of duplicating documentation on each method, the types
accepted by those functions are documented here.
A middleware (the untyped parameter in Use() and Insert()) must be one of the
following types:
- func(http.Handler) http.Handler
- func(c *web.C, http.Handler) http.Handler
All of the route-adding functions on Mux take two untyped parameters: pattern
and handler. Pattern will be passed to ParsePattern, which takes a web.Pattern,
a string, or a regular expression (more information can be found in the
ParsePattern documentation). Handler must be one of the following types:
- http.Handler
- web.Handler
- func(w http.ResponseWriter, r *http.Request)
- func(c web.C, w http.ResponseWriter, r *http.Request)
Mux is an HTTP multiplexer, much like net/http's ServeMux. It functions as both
a middleware stack and as an HTTP router.
Middleware provide a great abstraction for actions that must be performed on
every request, such as request logging and authentication. To append, insert,
and remove middleware, you can call the Use, Insert, and Abandon functions
respectively.
Routes may be added using any of the HTTP verb functions (Get, Post, etc.), or
through the generic Handle function. Goji's routing algorithm is very simple:
routes are processed in the order they are added, and the first matching route
will be executed. Routes match if their HTTP method and Pattern both match.
*/ */
type Mux struct { type Mux struct {
ms mStack ms mStack
@ -59,7 +39,7 @@ func New() *Mux {
return &mux return &mux
} }
// ServeHTTP processes HTTP requests. It make Muxes satisfy net/http.Handler.
// ServeHTTP processes HTTP requests. Satisfies net/http.Handler.
func (m *Mux) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (m *Mux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
stack := m.ms.alloc() stack := m.ms.alloc()
stack.ServeHTTP(w, r) stack.ServeHTTP(w, r)
@ -67,7 +47,7 @@ func (m *Mux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
} }
// ServeHTTPC creates a context dependent request with the given Mux. Satisfies // ServeHTTPC creates a context dependent request with the given Mux. Satisfies
// the web.Handler interface.
// the Handler interface.
func (m *Mux) ServeHTTPC(c C, w http.ResponseWriter, r *http.Request) { func (m *Mux) ServeHTTPC(c C, w http.ResponseWriter, r *http.Request) {
stack := m.ms.alloc() stack := m.ms.alloc()
stack.ServeHTTPC(c, w, r) stack.ServeHTTPC(c, w, r)
@ -76,23 +56,21 @@ func (m *Mux) ServeHTTPC(c C, w http.ResponseWriter, r *http.Request) {
// Middleware Stack functions // Middleware Stack functions
// Append the given middleware to the middleware stack. See the documentation
// for type Mux for a list of valid middleware types.
// Append the given middleware to the middleware stack.
// //
// No attempt is made to enforce the uniqueness of middlewares. It is illegal to // No attempt is made to enforce the uniqueness of middlewares. It is illegal to
// call this function concurrently with active requests. // call this function concurrently with active requests.
func (m *Mux) Use(middleware interface{}) {
func (m *Mux) Use(middleware MiddlewareType) {
m.ms.Use(middleware) m.ms.Use(middleware)
} }
// Insert the given middleware immediately before a given existing middleware in // Insert the given middleware immediately before a given existing middleware in
// the stack. See the documentation for type Mux for a list of valid middleware
// types. Returns an error if no middleware has the name given by "before."
// the stack. Returns an error if "before" cannot be found in the current stack.
// //
// No attempt is made to enforce the uniqueness of middlewares. If the insertion // No attempt is made to enforce the uniqueness of middlewares. If the insertion
// point is ambiguous, the first (outermost) one is chosen. It is illegal to // point is ambiguous, the first (outermost) one is chosen. It is illegal to
// call this function concurrently with active requests. // call this function concurrently with active requests.
func (m *Mux) Insert(middleware, before interface{}) error {
func (m *Mux) Insert(middleware, before MiddlewareType) error {
return m.ms.Insert(middleware, before) return m.ms.Insert(middleware, before)
} }
@ -102,7 +80,7 @@ func (m *Mux) Insert(middleware, before interface{}) error {
// If the name of the middleware to delete is ambiguous, the first (outermost) // If the name of the middleware to delete is ambiguous, the first (outermost)
// one is chosen. It is illegal to call this function concurrently with active // one is chosen. It is illegal to call this function concurrently with active
// requests. // requests.
func (m *Mux) Abandon(middleware interface{}) error {
func (m *Mux) Abandon(middleware MiddlewareType) error {
return m.ms.Abandon(middleware) return m.ms.Abandon(middleware)
} }
@ -110,96 +88,85 @@ func (m *Mux) Abandon(middleware interface{}) error {
/* /*
Dispatch to the given handler when the pattern matches, regardless of HTTP Dispatch to the given handler when the pattern matches, regardless of HTTP
method. See the documentation for type Mux for a description of what types are
accepted for pattern and handler.
method.
This method is commonly used to implement sub-routing: an admin application, for This method is commonly used to implement sub-routing: an admin application, for
instance, can expose a single handler that is attached to the main Mux by instance, can expose a single handler that is attached to the main Mux by
calling Handle("/admin*", adminHandler) or similar. Note that this function
calling Handle("/admin/*", adminHandler) or similar. Note that this function
doesn't strip this prefix from the path before forwarding it on (e.g., the doesn't strip this prefix from the path before forwarding it on (e.g., the
handler will see the full path, including the "/admin" part), but this
handler will see the full path, including the "/admin/" part), but this
functionality can easily be performed by an extra middleware layer. functionality can easily be performed by an extra middleware layer.
*/ */
func (m *Mux) Handle(pattern interface{}, handler interface{}) {
func (m *Mux) Handle(pattern PatternType, handler HandlerType) {
m.rt.handleUntyped(pattern, mALL, handler) m.rt.handleUntyped(pattern, mALL, handler)
} }
// Dispatch to the given handler when the pattern matches and the HTTP method is // Dispatch to the given handler when the pattern matches and the HTTP method is
// CONNECT. See the documentation for type Mux for a description of what types
// are accepted for pattern and handler.
func (m *Mux) Connect(pattern interface{}, handler interface{}) {
// CONNECT.
func (m *Mux) Connect(pattern PatternType, handler HandlerType) {
m.rt.handleUntyped(pattern, mCONNECT, handler) m.rt.handleUntyped(pattern, mCONNECT, handler)
} }
// Dispatch to the given handler when the pattern matches and the HTTP method is // Dispatch to the given handler when the pattern matches and the HTTP method is
// DELETE. See the documentation for type Mux for a description of what types
// are accepted for pattern and handler.
func (m *Mux) Delete(pattern interface{}, handler interface{}) {
// DELETE.
func (m *Mux) Delete(pattern PatternType, handler HandlerType) {
m.rt.handleUntyped(pattern, mDELETE, handler) m.rt.handleUntyped(pattern, mDELETE, handler)
} }
// Dispatch to the given handler when the pattern matches and the HTTP method is // Dispatch to the given handler when the pattern matches and the HTTP method is
// GET. See the documentation for type Mux for a description of what types are
// accepted for pattern and handler.
// GET.
// //
// All GET handlers also transparently serve HEAD requests, since net/http will // All GET handlers also transparently serve HEAD requests, since net/http will
// take care of all the fiddly bits for you. If you wish to provide an alternate // take care of all the fiddly bits for you. If you wish to provide an alternate
// implementation of HEAD, you should add a handler explicitly and place it // implementation of HEAD, you should add a handler explicitly and place it
// above your GET handler. // above your GET handler.
func (m *Mux) Get(pattern interface{}, handler interface{}) {
func (m *Mux) Get(pattern PatternType, handler HandlerType) {
m.rt.handleUntyped(pattern, mGET|mHEAD, handler) m.rt.handleUntyped(pattern, mGET|mHEAD, handler)
} }
// Dispatch to the given handler when the pattern matches and the HTTP method is // Dispatch to the given handler when the pattern matches and the HTTP method is
// HEAD. See the documentation for type Mux for a description of what types are
// accepted for pattern and handler.
func (m *Mux) Head(pattern interface{}, handler interface{}) {
// HEAD.
func (m *Mux) Head(pattern PatternType, handler HandlerType) {
m.rt.handleUntyped(pattern, mHEAD, handler) m.rt.handleUntyped(pattern, mHEAD, handler)
} }
// Dispatch to the given handler when the pattern matches and the HTTP method is // Dispatch to the given handler when the pattern matches and the HTTP method is
// OPTIONS. See the documentation for type Mux for a description of what types
// are accepted for pattern and handler.
func (m *Mux) Options(pattern interface{}, handler interface{}) {
// OPTIONS.
func (m *Mux) Options(pattern PatternType, handler HandlerType) {
m.rt.handleUntyped(pattern, mOPTIONS, handler) m.rt.handleUntyped(pattern, mOPTIONS, handler)
} }
// Dispatch to the given handler when the pattern matches and the HTTP method is // Dispatch to the given handler when the pattern matches and the HTTP method is
// PATCH. See the documentation for type Mux for a description of what types are
// accepted for pattern and handler.
func (m *Mux) Patch(pattern interface{}, handler interface{}) {
// PATCH.
func (m *Mux) Patch(pattern PatternType, handler HandlerType) {
m.rt.handleUntyped(pattern, mPATCH, handler) m.rt.handleUntyped(pattern, mPATCH, handler)
} }
// Dispatch to the given handler when the pattern matches and the HTTP method is // Dispatch to the given handler when the pattern matches and the HTTP method is
// POST. See the documentation for type Mux for a description of what types are
// accepted for pattern and handler.
func (m *Mux) Post(pattern interface{}, handler interface{}) {
// POST.
func (m *Mux) Post(pattern PatternType, handler HandlerType) {
m.rt.handleUntyped(pattern, mPOST, handler) m.rt.handleUntyped(pattern, mPOST, handler)
} }
// Dispatch to the given handler when the pattern matches and the HTTP method is // Dispatch to the given handler when the pattern matches and the HTTP method is
// PUT. See the documentation for type Mux for a description of what types are
// accepted for pattern and handler.
func (m *Mux) Put(pattern interface{}, handler interface{}) {
// PUT.
func (m *Mux) Put(pattern PatternType, handler HandlerType) {
m.rt.handleUntyped(pattern, mPUT, handler) m.rt.handleUntyped(pattern, mPUT, handler)
} }
// Dispatch to the given handler when the pattern matches and the HTTP method is // Dispatch to the given handler when the pattern matches and the HTTP method is
// TRACE. See the documentation for type Mux for a description of what types are
// accepted for pattern and handler.
func (m *Mux) Trace(pattern interface{}, handler interface{}) {
// TRACE.
func (m *Mux) Trace(pattern PatternType, handler HandlerType) {
m.rt.handleUntyped(pattern, mTRACE, handler) m.rt.handleUntyped(pattern, mTRACE, handler)
} }
// Set the fallback (i.e., 404) handler for this mux. See the documentation for
// type Mux for a description of what types are accepted for handler.
// Set the fallback (i.e., 404) handler for this mux.
// //
// As a convenience, the context environment variable "goji.web.validMethods" // As a convenience, the context environment variable "goji.web.validMethods"
// (also available as the constant ValidMethodsKey) will be set to the list of // (also available as the constant ValidMethodsKey) will be set to the list of
// HTTP methods that could have been routed had they been provided on an // HTTP methods that could have been routed had they been provided on an
// otherwise identical request. // otherwise identical request.
func (m *Mux) NotFound(handler interface{}) {
func (m *Mux) NotFound(handler HandlerType) {
m.rt.notFound = parseHandler(handler) m.rt.notFound = parseHandler(handler)
} }


+ 4
- 27
web/pattern.go View File

@ -36,35 +36,12 @@ ParsePattern is used internally by Goji to parse route patterns. It is exposed
publicly to make it easier to write thin wrappers around the built-in Pattern publicly to make it easier to write thin wrappers around the built-in Pattern
implementations. implementations.
Although its parameter has type interface{}, ParsePattern only accepts arguments
of three types:
- web.Pattern, which is passed through
- string, which is interpreted as a Sinatra-like URL pattern. In
particular, the following syntax is recognized:
- a path segment starting with with a colon will match any
string placed at that position. e.g., "/:name" will match
"/carl", binding "name" to "carl".
- a pattern ending with "/*" will match any route with that
prefix. For instance, the pattern "/u/:name/*" will match
"/u/carl/" and "/u/carl/projects/123", but not "/u/carl"
(because there is no trailing slash). In addition to any names
bound in the pattern, the special key "*" is bound to the
unmatched tail of the match, but including the leading "/". So
for the two matching examples above, "*" would be bound to "/"
and "/projects/123" respectively.
- regexp.Regexp, which is assumed to be a Perl-style regular expression
that is anchored on the left (i.e., the beginning of the string). If
your regular expression is not anchored on the left, a
hopefully-identical left-anchored regular expression will be created
and used instead. Named capturing groups will bind URLParams of the
same name; unnamed capturing groups will be bound to the variables
"$1", "$2", etc.
ParsePattern fatally exits (using log.Fatalf) if it is passed a value of an ParsePattern fatally exits (using log.Fatalf) if it is passed a value of an
unexpected type. It is the caller's responsibility to ensure that ParsePattern
is called in a type-safe manner.
unexpected type (see the documentation for PatternType for a list of which types
are accepted). It is the caller's responsibility to ensure that ParsePattern is
called in a type-safe manner.
*/ */
func ParsePattern(raw interface{}) Pattern {
func ParsePattern(raw PatternType) Pattern {
switch v := raw.(type) { switch v := raw.(type) {
case Pattern: case Pattern:
return v return v


+ 59
- 2
web/web.go View File

@ -80,7 +80,7 @@ middleware layers and given to the final request handler.
type C struct { type C struct {
// URLParams is a map of variables extracted from the URL (typically // URLParams is a map of variables extracted from the URL (typically
// from the path portion) during routing. See the documentation for the // from the path portion) during routing. See the documentation for the
// URL Pattern you are using (or the documentation for ParsePattern for
// URL Pattern you are using (or the documentation for PatternType for
// the case of standard pattern types) for more information about how // the case of standard pattern types) for more information about how
// variables are extracted and named. // variables are extracted and named.
URLParams map[string]string URLParams map[string]string
@ -98,7 +98,7 @@ type Handler interface {
} }
// HandlerFunc is similar to net/http's http.HandlerFunc, but supports a context // HandlerFunc is similar to net/http's http.HandlerFunc, but supports a context
// object. Implements both http.Handler and web.Handler.
// object. Implements both http.Handler and Handler.
type HandlerFunc func(C, http.ResponseWriter, *http.Request) type HandlerFunc func(C, http.ResponseWriter, *http.Request)
// ServeHTTP implements http.Handler, allowing HandlerFunc's to be used with // ServeHTTP implements http.Handler, allowing HandlerFunc's to be used with
@ -112,3 +112,60 @@ func (h HandlerFunc) ServeHTTP(w http.ResponseWriter, r *http.Request) {
func (h HandlerFunc) ServeHTTPC(c C, w http.ResponseWriter, r *http.Request) { func (h HandlerFunc) ServeHTTPC(c C, w http.ResponseWriter, r *http.Request) {
h(c, w, r) h(c, w, r)
} }
/*
PatternType is the type denoting Patterns and types that Goji internally
converts to Pattern (via the ParsePattern function). In order to provide an
expressive API, this type is an alias for interface{} (that is named for the
purposes of documentation), however only the following concrete types are
accepted:
- types that implement Pattern
- string, which is interpreted as a Sinatra-like URL pattern. In
particular, the following syntax is recognized:
- a path segment starting with with a colon will match any
string placed at that position. e.g., "/:name" will match
"/carl", binding "name" to "carl".
- a pattern ending with "/*" will match any route with that
prefix. For instance, the pattern "/u/:name/*" will match
"/u/carl/" and "/u/carl/projects/123", but not "/u/carl"
(because there is no trailing slash). In addition to any names
bound in the pattern, the special key "*" is bound to the
unmatched tail of the match, but including the leading "/". So
for the two matching examples above, "*" would be bound to "/"
and "/projects/123" respectively.
Unlike http.ServeMux's patterns, string patterns support neither the
"rooted subtree" behavior nor Host-specific routes. Users who require
either of these features are encouraged to compose package http's mux
with the mux provided by this package.
- regexp.Regexp, which is assumed to be a Perl-style regular expression
that is anchored on the left (i.e., the beginning of the string). If
your regular expression is not anchored on the left, a
hopefully-identical left-anchored regular expression will be created
and used instead.
Capturing groups will be converted into bound URL parameters in
URLParams. If the capturing group is named, that name will be used;
otherwise the special identifiers "$1", "$2", etc. will be used.
*/
type PatternType interface{}
/*
HandlerType is the type of Handlers and types that Goji internally converts to
Handler. In order to provide an expressive API, this type is an alias for
interface{} (that is named for the purposes of documentation), however only the
following concrete types are accepted:
- types that implement http.Handler
- types that implement Handler
- func(w http.ResponseWriter, r *http.Request)
- func(c web.C, w http.ResponseWriter, r *http.Request)
*/
type HandlerType interface{}
/*
MiddlewareType is the type of Goji middleware. In order to provide an expressive
API, this type is an alias for interface{} (that is named for the purposes of
documentation), however only the following concrete types are accepted:
- func(http.Handler) http.Handler
- func(c *web.C, http.Handler) http.Handler
*/
type MiddlewareType interface{}

Loading…
Cancel
Save