web

package module
v1.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 29, 2024 License: Apache-2.0 Imports: 21 Imported by: 1

README

web

GoDoc Build Status Codecov Release license-Apache 2

The web package aims to provide a simpler and more user-friendly development experience.

Note: This package does not depend on the go-spring

Install

go get go-spring.dev/web@latest

Features:

  • Automatically bind models based on ContentType.
  • Automatically output based on function return type.
  • Support binding value from path/query/header/cookie/form/body.
  • Support binding files for easier file uploads handling.
  • Support customizing global output formats and route-level custom output.
  • Support custom parameter validators.
  • Support handler converter, adding the above capabilities with just one line of code for all http servers based on the standard library solution.
  • Support for middlewares based on chain of responsibility.

Router

web router is based on a kind of Patricia Radix trie. The router is compatible with net/http.

Router interface:
// Router registers routes to be matched and dispatches a handler.
//
type Router interface {
	Routes
	http.Handler

	// Use appends a MiddlewareFunc to the chain.
	Use(mwf ...MiddlewareFunc) Router

	// Renderer to be used Response renderer in default.
	Renderer(renderer Renderer) Router

	// Group creates a new router group.
	Group(pattern string, fn ...func(r Router)) Router

	// Handle registers a new route with a matcher for the URL pattern.
	Handle(pattern string, handler http.Handler)

	// HandleFunc registers a new route with a matcher for the URL pattern.
	HandleFunc(pattern string, handler http.HandlerFunc)

	// Any registers a route that matches all the HTTP methods.
	// GET, POST, PUT, PATCH, HEAD, OPTIONS, DELETE, CONNECT, TRACE.
	Any(pattern string, handler interface{})

	// Get registers a new GET route with a matcher for the URL path of the get method.
	Get(pattern string, handler interface{})

	// Head registers a new HEAD route with a matcher for the URL path of the head method.
	Head(pattern string, handler interface{})

	// Post registers a new POST route with a matcher for the URL path of the post method.
	Post(pattern string, handler interface{})

	// Put registers a new PUT route with a matcher for the URL path of the put method.
	Put(pattern string, handler interface{})

	// Patch registers a new PATCH route with a matcher for the URL path of the patch method.
	Patch(pattern string, handler interface{})

	// Delete registers a new DELETE route with a matcher for the URL path of the delete method.
	Delete(pattern string, handler interface{})

	// Connect registers a new CONNECT route with a matcher for the URL path of the connect method.
	Connect(pattern string, handler interface{})

	// Options registers a new OPTIONS route with a matcher for the URL path of the options method.
	Options(pattern string, handler interface{})

	// Trace registers a new TRACE route with a matcher for the URL path of the trace method.
	Trace(pattern string, handler interface{})

	// NotFound to be used when no route matches.
	NotFound(handler http.HandlerFunc)

	// MethodNotAllowed to be used when the request method does not match the route.
	MethodNotAllowed(handler http.HandlerFunc)
}

Getting Started

HelloWorld
package main

import (
	"context"
	"net/http"

	"go-spring.dev/web"
)

func main() {
	var router = web.NewRouter()

	router.Get("/greeting", func(ctx context.Context, req struct {
		Name string `query:"name"`
	}) string {
		return "Hello, " + req.Name
	})

	http.ListenAndServe(":8080", router)
}
$ curl -i -X GET 'http://127.0.0.1:8080/greeting?name=world'
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Mon, 25 Dec 2023 06:13:03 GMT
Content-Length: 33

{"code":0,"data":"Hello, world"}
Custom Render

Allows you to customize the renderer, using the default JsonRender if not specified.

package main

import (
	"context"
	"net/http"

	"go-spring.dev/web"
)

func main() {
	var router = web.NewRouter()

	router.Renderer(web.RendererFunc(func(ctx *web.Context, err error, result interface{}) {
		if nil != err {
			ctx.String(500, "%v", err)
		} else {
			ctx.String(200, "%v", result)
		}
	}))

	router.Get("/greeting", func(ctx context.Context, req struct {
		Name string `query:"name"`
	}) string {
		return "Hello, " + req.Name
	})

	http.ListenAndServe(":8080", router)

	/*
        $ curl -i -X GET 'http://127.0.0.1:8080/greeting?name=world'
        HTTP/1.1 200 OK
        Content-Type: text/plain; charset=utf-8
        Date: Mon, 25 Dec 2023 06:35:32 GMT
        Content-Length: 12
    
        Hello, world
	*/
}

Custom validator

Allows you to register a custom value validator. If the value verification fails, request processing aborts.

In this example, we will use go-validator/validator, you can refer to this example to register your custom validator.

package main

import (
	"context"
	"log/slog"
	"mime/multipart"
	"net/http"

	"go-spring.dev/web"
	"go-spring.dev/web/binding"
	"gopkg.in/validator.v2"
)

var router = web.NewRouter()
var validatorInst = validator.NewValidator().WithTag("validate")

func init() {
	binding.RegisterValidator(func(i interface{}) error {
		return validatorInst.Validate(i)
	})
}

type UserRegisterModel struct {
	Username  string                `form:"username" validate:"min=6,max=20"`  // username
	Password  string                `form:"password" validate:"min=10,max=20"` // password
	Avatar    *multipart.FileHeader `form:"avatar" validate:"nonzero"`         // avatar
	Captcha   string                `form:"captcha" validate:"min=4,max=4"`    // captcha
	UserAgent string                `header:"User-Agent"`                      // user agent
	Ad        string                `query:"ad"`                               // advertising ID
	Token     string                `cookie:"token"`                           // token
}

func main() {
	router.Post("/user/register", UserRegister)

	http.ListenAndServe(":8080", router)
}

func UserRegister(ctx context.Context, req UserRegisterModel) string {
	slog.Info("user register",
		slog.String("username", req.Username),
		slog.String("password", req.Password),
		slog.String("captcha", req.Captcha),
		slog.String("userAgent", req.UserAgent),
		slog.String("ad", req.Ad),
		slog.String("token", req.Token),
	)
	return "success"
}

Middlewares

Compatible with middlewares based on standard library solutions.

package main

import (
	"context"
	"log/slog"
	"net/http"
	"time"

	"go-spring.dev/web"
)

func main() {
	var router = web.NewRouter()

	// access log
	router.Use(func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
			t1 := time.Now()
			next.ServeHTTP(writer, request)
			slog.Info("access log", slog.String("path", request.URL.Path), slog.String("method", request.Method), slog.Duration("cost", time.Since(t1)))
		})
	})

	// cors
	router.Use(func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
			writer.Header().Set("Access-Control-Allow-Origin", "*")
			writer.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS")
			writer.Header().Set("Access-Control-Allow-Headers", "Origin, Content-Type")

			// preflight request
			if request.Method == http.MethodOptions {
				writer.WriteHeader(http.StatusNoContent)
				return
			}

			next.ServeHTTP(writer, request)
		})
	})

	router.Group("/public", func(r web.Router) {
		r.Post("/register", func(ctx context.Context) string { return "register: do something" })
		r.Post("/forgot", func(ctx context.Context) string { return "forgot: do something" })
		r.Post("/login", func(ctx context.Context, req struct {
			Username string `form:"username"`
			Password string `form:"password"`
		}) error {
			if "admin" == req.Username && "admin123" == req.Password {
				web.FromContext(ctx).SetCookie("token", req.Username, 600, "/", "", false, false)
				return nil
			}
			return web.Error(400, "login failed")
		})
	})

	router.Group("/user", func(r web.Router) {

		// user login check
		r.Use(func(next http.Handler) http.Handler {
			return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
				// check login state in cookies
				//
				if _, err := request.Cookie("token"); nil != err {
					writer.WriteHeader(http.StatusForbidden)
					return
				}

				// login check success
				next.ServeHTTP(writer, request)
			})
		})

		r.Get("/userInfo", func(ctx context.Context) interface{} {
			// TODO: load user from database
			//
			return map[string]interface{}{
				"username": "admin",
				"time":     time.Now().String(),
			}
		})

		r.Get("/logout", func(ctx context.Context) string {
			// delete cookie
			web.FromContext(ctx).SetCookie("token", "", -1, "/", "", false, false)
			return "success"
		})

	})

	http.ListenAndServe(":8080", router)
}

Acknowledgments

License

The repository released under version 2.0 of the Apache License.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Bind

func Bind(fn interface{}, render Renderer) http.HandlerFunc

Bind convert fn to HandlerFunc.

func(ctx context.Context)

func(ctx context.Context) R

func(ctx context.Context) error

func(ctx context.Context, req T) R

func(ctx context.Context, req T) error

func(ctx context.Context, req T) (R, error)

func(writer http.ResponseWriter, request *http.Request)

func Walk

func Walk(r Routes, walkFn WalkFunc) error

Walk walks any router tree that implements Routes interface.

func WithContext

func WithContext(parent context.Context, ctx *Context) context.Context

func WithRouteContext

func WithRouteContext(parent context.Context, ctx *RouteContext) context.Context

Types

type ChainHandler added in v1.0.1

type ChainHandler struct {
	Endpoint http.Handler

	Middlewares Middlewares
	// contains filtered or unexported fields
}

func (*ChainHandler) ServeHTTP added in v1.0.1

func (c *ChainHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)

func (*ChainHandler) Unwrap added in v1.0.1

func (c *ChainHandler) Unwrap() any

type Context

type Context struct {
	// A ResponseWriter interface is used by an HTTP handler to
	// construct an HTTP response.
	Writer http.ResponseWriter

	// A Request represents an HTTP request received by a server
	// or to be sent by a client.
	Request *http.Request
	// contains filtered or unexported fields
}

func FromContext

func FromContext(ctx context.Context) *Context

func (*Context) Bind

func (c *Context) Bind(r interface{}) error

Bind checks the Method and Content-Type to select a binding engine automatically, Depending on the "Content-Type" header different bindings are used, for example:

"application/json" --> JSON binding
"application/xml"  --> XML binding

func (*Context) ClientIP

func (c *Context) ClientIP() string

ClientIP implements one best effort algorithm to return the real client IP. It calls c.RemoteIP() under the hood, to check if the remote IP is a trusted proxy or not. If it is it will then try to parse the headers defined in RemoteIPHeaders (defaulting to [X-Forwarded-For, X-Real-Ip]). If the headers are not syntactically valid OR the remote IP does not correspond to a trusted proxy, the remote IP (coming from Request.RemoteAddr) is returned.

func (*Context) ContentType

func (c *Context) ContentType() string

ContentType returns the request header `Content-Type`.

func (*Context) Context

func (c *Context) Context() context.Context

Context returns the request's context.

func (*Context) Cookie

func (c *Context) Cookie(name string) (string, bool)

Cookie returns the named cookie provided in the request.

func (*Context) Data

func (c *Context) Data(code int, contentType string, data []byte) error

Data writes some data into the body stream and updates the HTTP code.

func (*Context) File

func (c *Context) File(filepath string)

File writes the specified file into the body stream in an efficient way.

func (*Context) FileAttachment

func (c *Context) FileAttachment(filepath, filename string)

FileAttachment writes the specified file into the body stream in an efficient way On the client side, the file will typically be downloaded with the given filename

func (*Context) FormParams

func (c *Context) FormParams() (url.Values, error)

FormParams returns the form in the request.

func (*Context) Header

func (c *Context) Header(key string) (string, bool)

Header returns the named header in the request.

func (*Context) IndentedJSON

func (c *Context) IndentedJSON(code int, obj interface{}) error

IndentedJSON serializes the given struct as pretty JSON (indented + endlines) into the response body. It also sets the Content-Type as "application/json".

func (*Context) IndentedXML

func (c *Context) IndentedXML(code int, obj interface{}) error

IndentedXML serializes the given struct as pretty XML (indented + endlines) into the response body. It also sets the Content-Type as "application/xml".

func (*Context) IsWebsocket

func (c *Context) IsWebsocket() bool

IsWebsocket returns true if the request headers indicate that a websocket handshake is being initiated by the client.

func (*Context) JSON

func (c *Context) JSON(code int, obj interface{}) error

JSON serializes the given struct as JSON into the response body. It also sets the Content-Type as "application/json".

func (*Context) MultipartParams

func (c *Context) MultipartParams(maxMemory int64) (*multipart.Form, error)

MultipartParams returns a request body as multipart/form-data. The whole request body is parsed and up to a total of maxMemory bytes of its file parts are stored in memory, with the remainder stored on disk in temporary files.

func (*Context) PathParam

func (c *Context) PathParam(name string) (string, bool)

PathParam returns the named variables in the request.

func (*Context) QueryParam

func (c *Context) QueryParam(name string) (string, bool)

QueryParam returns the named query in the request.

func (*Context) Redirect

func (c *Context) Redirect(code int, location string) error

Redirect returns an HTTP redirect to the specific location.

func (*Context) RemoteIP

func (c *Context) RemoteIP() string

RemoteIP parses the IP from Request.RemoteAddr, normalizes and returns the IP (without the port).

func (*Context) Render

func (c *Context) Render(code int, render render.Renderer) error

Render writes the response headers and calls render.Render to render data.

func (*Context) RequestBody

func (c *Context) RequestBody() io.Reader

RequestBody returns the request body.

func (*Context) SetCookie

func (c *Context) SetCookie(name, value string, maxAge int, path, domain string, secure, httpOnly bool)

SetCookie adds a Set-Cookie header to the ResponseWriter's headers. The provided cookie must have a valid Name. Invalid cookies may be silently dropped.

func (*Context) SetHeader

func (c *Context) SetHeader(key, value string)

SetHeader is an intelligent shortcut for c.Writer.Header().Set(key, value). It writes a header in the response. If value == "", this method removes the header `c.Writer.Header().Del(key)`

func (*Context) SetSameSite

func (c *Context) SetSameSite(samesite http.SameSite)

SetSameSite with cookie

func (*Context) Status

func (c *Context) Status(code int)

Status sets the HTTP response code.

func (*Context) String

func (c *Context) String(code int, format string, args ...interface{}) error

String writes the given string into the response body.

func (*Context) XML

func (c *Context) XML(code int, obj interface{}) error

XML serializes the given struct as XML into the response body. It also sets the Content-Type as "application/xml".

type HttpError

type HttpError struct {
	Code    int
	Message string
}

func Error

func Error(code int, format string, args ...interface{}) HttpError

func (HttpError) Error

func (e HttpError) Error() string

type MiddlewareFunc

type MiddlewareFunc = func(next http.Handler) http.Handler

MiddlewareFunc is a function which receives an http.Handler and returns another http.Handler. Typically, the returned handler is a closure which does something with the http.ResponseWriter and http.Request passed to it, and then calls the handler passed as parameter to the MiddlewareFunc.

func Recovery added in v1.0.1

func Recovery() MiddlewareFunc

Recovery returns a middleware that recovers from any panics and writes a 500 if there was one.

func RecoveryWith added in v1.0.1

func RecoveryWith(panicOut io.Writer) MiddlewareFunc

RecoveryWith returns a middleware for a given writer that recovers from any panics and writes a 500 if there was one.

type Middlewares

type Middlewares []MiddlewareFunc

Middlewares type is a slice of standard middleware handlers with methods to compose middleware chains and http.Handler's.

func (Middlewares) Handler

func (mws Middlewares) Handler(h http.Handler) http.Handler

Handler builds and returns a http.Handler from the chain of middlewares, with `h http.Handler` as the final handler.

func (Middlewares) HandlerFunc

func (mws Middlewares) HandlerFunc(h http.HandlerFunc) http.Handler

HandlerFunc builds and returns a http.Handler from the chain of middlewares, with `h http.Handler` as the final handler.

type Options

type Options struct {
	// Addr optionally specifies the TCP address for the server to listen on,
	// in the form "host:port". If empty, ":http" (port 8080) is used.
	// The service names are defined in RFC 6335 and assigned by IANA.
	// See net.Dial for details of the address format.
	Addr string `json:"addr" value:"${addr:=}"`

	// CertFile containing a certificate and matching private key for the
	// server must be provided if neither the Server's
	// TLSConfig.Certificates nor TLSConfig.GetCertificate are populated.
	// If the certificate is signed by a certificate authority, the
	// certFile should be the concatenation of the server's certificate,
	// any intermediates, and the CA's certificate.
	CertFile string `json:"cert-file" value:"${cert-file:=}"`

	// KeyFile containing a private key file.
	KeyFile string `json:"key-file" value:"${key-file:=}"`

	// ReadTimeout is the maximum duration for reading the entire
	// request, including the body. A zero or negative value means
	// there will be no timeout.
	//
	// Because ReadTimeout does not let Handlers make per-request
	// decisions on each request body's acceptable deadline or
	// upload rate, most users will prefer to use
	// ReadHeaderTimeout. It is valid to use them both.
	ReadTimeout time.Duration `json:"read-timeout" value:"${read-timeout:=0s}"`

	// ReadHeaderTimeout is the amount of time allowed to read
	// request headers. The connection's read deadline is reset
	// after reading the headers and the Handler can decide what
	// is considered too slow for the body. If ReadHeaderTimeout
	// is zero, the value of ReadTimeout is used. If both are
	// zero, there is no timeout.
	ReadHeaderTimeout time.Duration `json:"read-header-timeout" value:"${read-header-timeout:=0s}"`

	// WriteTimeout is the maximum duration before timing out
	// writes of the response. It is reset whenever a new
	// request's header is read. Like ReadTimeout, it does not
	// let Handlers make decisions on a per-request basis.
	// A zero or negative value means there will be no timeout.
	WriteTimeout time.Duration `json:"write-timeout" value:"${write-timeout:=0s}"`

	// IdleTimeout is the maximum amount of time to wait for the
	// next request when keep-alives are enabled. If IdleTimeout
	// is zero, the value of ReadTimeout is used. If both are
	// zero, there is no timeout.
	IdleTimeout time.Duration `json:"idle-timeout" value:"${idle-timeout:=0s}"`

	// MaxHeaderBytes controls the maximum number of bytes the
	// server will read parsing the request header's keys and
	// values, including the request line. It does not limit the
	// size of the request body.
	// If zero, DefaultMaxHeaderBytes is used.
	MaxHeaderBytes int `json:"max-header-bytes" value:"${max-header-bytes:=0}"`

	// Router optionally specifies an external router.
	Router Router `json:"-"`
}

func (Options) IsTls

func (options Options) IsTls() bool

func (Options) TlsConfig

func (options Options) TlsConfig() *tls.Config

type Renderer

type Renderer interface {
	Render(ctx *Context, err error, result interface{})
}

type RendererFunc

type RendererFunc func(ctx *Context, err error, result interface{})

func JsonRender

func JsonRender() RendererFunc

JsonRender is default Render

func (RendererFunc) Render

func (fn RendererFunc) Render(ctx *Context, err error, result interface{})

type Route

type Route struct {
	SubRoutes Routes
	Handlers  map[string]http.Handler
	Pattern   string
}

Route describes the details of a routing handler. Handlers map key is an HTTP method

type RouteContext

type RouteContext struct {
	Routes Routes
	// URLParams are the stack of routeParams captured during the
	// routing lifecycle across a stack of sub-routers.
	URLParams RouteParams

	// Routing path/method override used during the route search.
	RoutePath   string
	RouteMethod string

	// The endpoint routing pattern that matched the request URI path
	// or `RoutePath` of the current sub-router. This value will update
	// during the lifecycle of a request passing through a stack of
	// sub-routers.
	RoutePattern string
	// contains filtered or unexported fields
}

func FromRouteContext

func FromRouteContext(ctx context.Context) *RouteContext

func (*RouteContext) AllowedMethods added in v1.0.1

func (c *RouteContext) AllowedMethods() (methods []string)

AllowedMethods report allowed http methods.

func (*RouteContext) Reset

func (c *RouteContext) Reset()

Reset context to initial state

type RouteParams

type RouteParams struct {
	Keys, Values []string
}

RouteParams is a structure to track URL routing parameters efficiently.

func (*RouteParams) Add

func (s *RouteParams) Add(key, value string)

Add will append a URL parameter to the end of the route param

func (*RouteParams) Get

func (s *RouteParams) Get(key string) (value string, ok bool)

type Router

type Router interface {
	Routes
	http.Handler

	// Use appends a MiddlewareFunc to the chain.
	Use(mwf ...MiddlewareFunc) Router

	// Renderer to be used Response renderer in default.
	Renderer(renderer Renderer) Router

	// Group creates a new router group.
	Group(pattern string, fn ...func(r Router)) Router

	// Handle registers a new route with a matcher for the URL pattern.
	Handle(pattern string, handler http.Handler)

	// HandleFunc registers a new route with a matcher for the URL pattern.
	HandleFunc(pattern string, handler http.HandlerFunc)

	// Any registers a route that matches all the HTTP methods.
	// GET, POST, PUT, PATCH, HEAD, OPTIONS, DELETE, CONNECT, TRACE.
	Any(pattern string, handler interface{})

	// Get registers a new GET route with a matcher for the URL path of the get method.
	Get(pattern string, handler interface{})

	// Head registers a new HEAD route with a matcher for the URL path of the head method.
	Head(pattern string, handler interface{})

	// Post registers a new POST route with a matcher for the URL path of the post method.
	Post(pattern string, handler interface{})

	// Put registers a new PUT route with a matcher for the URL path of the put method.
	Put(pattern string, handler interface{})

	// Patch registers a new PATCH route with a matcher for the URL path of the patch method.
	Patch(pattern string, handler interface{})

	// Delete registers a new DELETE route with a matcher for the URL path of the delete method.
	Delete(pattern string, handler interface{})

	// Connect registers a new CONNECT route with a matcher for the URL path of the connect method.
	Connect(pattern string, handler interface{})

	// Options registers a new OPTIONS route with a matcher for the URL path of the options method.
	Options(pattern string, handler interface{})

	// Trace registers a new TRACE route with a matcher for the URL path of the trace method.
	Trace(pattern string, handler interface{})

	// NotFound to be used when no route matches.
	NotFound(handler http.HandlerFunc)

	// MethodNotAllowed to be used when the request method does not match the route.
	MethodNotAllowed(handler http.HandlerFunc)
}

Router registers routes to be matched and dispatches a handler.

Registers a new route with a matcher for the URL pattern. Automatic binding request to handler input params and validate params.

Router.Any() Router.Get() Router.Head() Router.Post() Router.Put() Router.Patch() Router.Delete() Router.Connect() Router.Options() Router.Trace()

The handler accepts the following functional signatures:

func(ctx context.Context)

func(ctx context.Context) R

func(ctx context.Context) error

func(ctx context.Context, req T) R

func(ctx context.Context, req T) error

func(ctx context.Context, req T) (R, error)

It implements the http.Handler interface, so it can be registered to serve requests:

	func main() {
   var router = web.NewRouter()
	  router.Get("/greeting", func(ctx context.Context) string {
	    return "greeting!!!"
	  })
	  http.ListenAndServe(":8080", router)
	}

func NewRouter

func NewRouter() Router

NewRouter returns a new router instance.

type Routes

type Routes interface {
	// Routes returns the routing tree in an easily traversable structure.
	Routes() []Route

	// Middlewares returns the list of middlewares in use by the router.
	Middlewares() Middlewares

	// Match searches the routing tree for a handler that matches
	// the method/path - similar to routing a http request, but without
	// executing the handler thereafter.
	Match(ctx *RouteContext, method, path string) bool
}

type Server

type Server struct {
	Router
	// contains filtered or unexported fields
}

A Server defines parameters for running an HTTP server.

func NewServer

func NewServer(options Options) *Server

NewServer returns a new server instance.

func (*Server) Addr

func (s *Server) Addr() string

Addr returns the server listen address.

func (*Server) Run

func (s *Server) Run() error

Run listens on the TCP network address Addr and then calls Serve to handle requests on incoming connections. Accepted connections are configured to enable TCP keep-alives.

func (*Server) Shutdown

func (s *Server) Shutdown(ctx context.Context) error

Shutdown gracefully shuts down the server without interrupting any active connections. Shutdown works by first closing all open listeners, then closing all idle connections, and then waiting indefinitely for connections to return to idle and then shut down. If the provided context expires before the shutdown is complete, Shutdown returns the context's error, otherwise it returns any error returned from closing the Server's underlying Listener(s).

type WalkFunc

type WalkFunc func(method string, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error

WalkFunc is the type of the function called for each method and route visited by Walk.

Directories

Path Synopsis
Package binding ...
Package binding ...

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL