web

package
v3.0.0-beta.2 Latest Latest
Warning

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

Go to latest
Published: Apr 4, 2019 License: OSL-3.0 Imports: 29 Imported by: 152

README

Router

Flamingo provides basic routing functionality with the standard web.Router

You can use this package either standalone, or together with the prefixrouter module.

Basic routing concept

For the path of an URL a standard routing concept is applied, where at the end the URL is matched to a controller.

  • a route is assigned to a handle. (A handle is a string that represents a unique name). A handle can be something like cms.page.view
  • for a handle a controller can be registered. The indirection through handles allows us to register different controllers for certain handlers in different contexts.

Routes can be configured the following ways (See basic usage below):

  • Via router.Registry in your modules initialisation (typical in module.go)
  • As part of the project configuration. This again allows us to have different routing paths configured for different contexts.

Basic Usage

Registering Routes in Modules

Routes are registered normally during initialisation of your Flamingo module.

In order to register new Routes you need to bind a new RoutesModule, which is normally an internal type of your Flamingo module.

Internally, Flamingo uses a Dingo multibinding to add your RoutesModule. You just have to call web.BindRoutes to register your own RoutesModule:

// Configure DI
func (m *) Configure(injector *dingo.Injector) {
	web.BindRoutes(injector, new(routes))
}

routes needs to implement the type:

// RoutesModule defines a router RoutesModule, which is able to register routes
RoutesModule interface {
  Routes(registry *RouterRegistry)
}

Insides the Routes method you can then use the router.Registry to register new Routes.

For example:

func (r *routes) Routes(registry *router.Registry) {
	registry.Route("/hello", "hello")
	registry.HandleGet("hello", r.helloController.Get)
}
Registering Routes via Configuration

Add a routes.yml in your config folder like this:

- path: /
  name: index
  controller: flamingo.render(tpl="index")

- path: /anotherPath
  controller: flamingo.render(tpl="index")

- path: /redirect
  controller: flamingo.redirect(to="index")

You can use the Flamingo default controllers (see below)

Routing Details

Route

A route defines a mapping from a path to a "handler identifier".

The handler identifier is used to easily support reverse routing and rewriting mechanisms.

Handler

A "handler identifier" can be mapped to one or more Actions, e.g.:

registry.HandleGet("hello", r.helloController.Get)
registry.HandlePost("hello", r.helloController.Get)
Data Controller

Views can request arbitrary data via the data template function.

Route Format

The route format is based on the format the Play Framework is using.

Essentially there are 4 types of parts, of which the route is constructed

Static

A piece which is just static, such as /foo/bar/asd.

Parameter

A part with a named parameter, /foo/:param/ which spans the request up to the next / or . (e.g. .html).

Regex

A (optionally named) regex parameter such as /foo/$param<[0-9]+> which captures everything the regex captures, where param in this example is the name of the parameter.

Wildcard

A wildcard which captures everything, such as /foo/bar/*param. Note that slashes are not escaped here!

Router Target

The target of a route is a controller name and optional attributes.

Parameters

Parameters are comma-separated identifiers.

If no parameters are specified and not brackets are used every route parameter is available as a parameter.

  • controller.view Gets all available parameters
  • controller.view(param1, param2) param1 and param2 will be set
  • controller.view(param1 ?= "foo", param2 = "bar") param1 is optional, if not specified it is set to "foo". param2 is always set to "bar".

If specified parameters don't have a value or optional value and are not part of the path, then they are taken from GET parameters.

Catchall

It is possible to specify a catchall address, which gets all parameters and applies all "leftover" as GET parameters, use * to indicate a catchall.

Example:

controller.view(param1, *)

This is quite helpful for reverse-routing.

Default Controller

Currently Flamingo registers the following controllers:

  • flamingo.redirect(to, ...) Redirects to to. All other parameters (but to) are passed on as URL parameters
  • flamingo.redirectUrl(url) Redirects to url
  • flamingo.redirectPermanent(to, ...) Redirects permanently to to. All other parameters (but to) are passed on as URL parameters
  • flamingo.redirectPermanentUrl(url) Redirects permanently to url

Configured routes

Beside registering routes in the code it is also possible to register them in your routes.yml.

The root node consists of an array of objects with:

  • controller: must name a controller to execute
  • path: optional path where this is accessable
  • name: optional name where this will be available for reverse routing

Context routes always take precedence over normal routes!

Example
- path: /
  controller: flamingo.redirect(to="cms.page.view", name="home")
  name: home
- path: /home
  controller: cms.page.view(name="home")
- path: /special
  controller: cms.page.view(name?="special")

This will result in the following accessible routes:

  • /: Redirects to /home (because there is a route for cms.page.view with name set to home. Otherwise this would go to /cms/home)
  • /home: Shows cms.page.view(name="home")
  • /special: Shows cms.page.view(name="special")
  • /special?name=foo: Shows cms.page.view(name="foo") (optional argument retrieved from GET)

The / route is now also available as a controller named home, which is just an alias for calling the flamingo.redirect controller with the parameters to="cms.page.view" and name="home".

Router filter

Router filters can be used as middleware in the dispatching process. The filters are executed before the controller action. A router filter can be registered via dingo injection in module.go's Configure function:

func (m *Module) Configure(injector *dingo.Injector) {
	injector.BindMulti(new(router.Filter)).To(new(myFilter))
}

A Filter must implement the web.Filter interface by providing a Filter function: Filter(ctx context.Context, req *web.Request, w http.ResponseWriter, fc *web.FilterChain) web.Result.

The filters are handled in order of dingo.Modules as defined in flamingo.App() call. You will have to return fc.Next(ctx, req, w) in your Filter function to call the next filter. If you return something else, the chain will be aborted and the actual controller action will not be executed.

Routing config

You can define the URL under which the routing takes place:

flamingo.router.scheme=https
flamingo.router.host=www.example.com
flamingo.router.path=subpath

This will result in:

If the config is not set, then the router will generate URLs based on the current hostname.

Documentation

Index

Examples

Constants

View Source
const (
	// FlamingoError is the Controller name for errors
	FlamingoError = "flamingo.error"
	// FlamingoNotfound is the Controller name for 404 notfound
	FlamingoNotfound = "flamingo.notfound"
)

Variables

View Source
var (

	// ControllerKey exposes the current controller/handler key
	ControllerKey, _ = tag.NewKey("controller")

	RouterError contextKeyType = "error"
)
View Source
var (
	// ErrFormNotFound is returned for unknown form values
	ErrFormNotFound = errors.New("form value not found")

	// ErrQueryNotFound is returned for unknown URL query parameters
	ErrQueryNotFound = errors.New("query value not found")
)

Functions

func BindRoutes

func BindRoutes(injector *dingo.Injector, m RoutesModule)

BindRoutes is a convenience helper to multi-bind router modules

func ContextWithRequest

func ContextWithRequest(ctx context.Context, r *Request) context.Context

ContextWithRequest stores the request in a new context

func ContextWithSession

func ContextWithSession(ctx context.Context, session *Session) context.Context

ContextWithSession returns a new Context with an attached session

func HandlerCmd

func HandlerCmd(router *Router, area *config.Area) *cobra.Command

HandlerCmd for debugging the router/handler configuration

func RoutesCmd

func RoutesCmd(router *Router, area *config.Area) *cobra.Command

RoutesCmd for debugging the router configuration

func URLTitle

func URLTitle(title string) string

URLTitle normalizes a title for nice usage in URLs

Example

Example usage of the URLTitle helper function

fmt.Println(URLTitle("test/a 123 name % / _ - _ test"))
Output:

test_a-123-name-test

Types

type Action

type Action func(ctx context.Context, req *Request) Result

Action defines an explicit http action

func WrapDataAction

func WrapDataAction(da DataAction) Action

WrapDataAction allows to register a data action for a HTTP method

func WrapHTTPHandler

func WrapHTTPHandler(handler http.Handler) Action

WrapHTTPHandler wraps an http.Handler to be used in the flamingo http package

func (Action) ServeHTTP

func (a Action) ServeHTTP(rw http.ResponseWriter, r *http.Request)

type CanonicalDomainFunc

type CanonicalDomainFunc struct {
	// contains filtered or unexported fields
}

CanonicalDomainFunc is exported as a template function

func (*CanonicalDomainFunc) Func

func (c *CanonicalDomainFunc) Func(ctx context.Context) interface{}

Func returns the canonicalDomain func

func (*CanonicalDomainFunc) Inject

Inject dependencies

type DataAction

type DataAction func(ctx context.Context, req *Request, callParams RequestParams) interface{}

DataAction is a method called which does not return the web response itself, but data instead

type DataResponse

type DataResponse struct {
	Response
	Data interface{}
}

DataResponse returns a response containing data, e.g. as JSON

func (*DataResponse) Apply

Apply response todo: support more than json

func (*DataResponse) SetNoCache

func (r *DataResponse) SetNoCache() *DataResponse

SetNoCache helper

func (*DataResponse) Status

func (r *DataResponse) Status(status uint) *DataResponse

Status changes response status code

type Filter

type Filter interface {
	Filter(ctx context.Context, req *Request, w http.ResponseWriter, fc *FilterChain) Result
}

Filter is an interface which can filter requests

type FilterChain

type FilterChain struct {
	// contains filtered or unexported fields
}

FilterChain defines the chain which contains all filters which will be worked off

func NewFilterChain

func NewFilterChain(final lastFilter, filters ...Filter) *FilterChain

NewFilterChain constructs and sets the final filter and optional filters

func (*FilterChain) AddPostApply

func (fc *FilterChain) AddPostApply(callback func(err error, result Result))

AddPostApply adds a callback to be called after the response has been applied to the responsewriter

func (*FilterChain) Next

func (fc *FilterChain) Next(ctx context.Context, req *Request, w http.ResponseWriter) Result

Next calls the next filter and deletes it of the chain

type GetPartialDataFunc

type GetPartialDataFunc struct{}

GetPartialDataFunc allows to get partial data

func (*GetPartialDataFunc) Func

func (*GetPartialDataFunc) Func(c context.Context) interface{}

Func getter to bind the context

type Handler

type Handler struct {
	// contains filtered or unexported fields
}

Handler defines a concrete Controller

func (*Handler) GetHandlerName

func (handler *Handler) GetHandlerName() string

GetHandlerName getter

func (*Handler) GetPath

func (handler *Handler) GetPath() string

GetPath getter

func (*Handler) Normalize

func (handler *Handler) Normalize(params ...string) *Handler

Normalize enforces a normalization of passed parameters

type IsExternalURL

type IsExternalURL struct {
	// contains filtered or unexported fields
}

IsExternalURL is exported as a template function

func (*IsExternalURL) Func

func (c *IsExternalURL) Func(ctx context.Context) interface{}

Func returns a boolean if a given URL is external

func (*IsExternalURL) Inject

func (c *IsExternalURL) Inject(router ReverseRouter) *IsExternalURL

Inject dependencies

type Match

type Match struct {
	Values map[string]string
}

Match is the result of matching a path

type OnFinishEvent

type OnFinishEvent struct {
	OnRequestEvent
	Error error
}

OnFinishEvent is the event object associated to OnFinish

type OnRequestEvent

type OnRequestEvent struct {
	Request        *Request
	ResponseWriter http.ResponseWriter
}

OnRequestEvent contains the bare request

type OnResponseEvent

type OnResponseEvent struct {
	OnRequestEvent
	Result Result
}

OnResponseEvent is the event associated to OnResponse

type Path

type Path struct {
	// contains filtered or unexported fields
}

Path is a matchable routing path

func NewPath

func NewPath(path string) (*Path, error)

NewPath returns a new path

func (*Path) Match

func (p *Path) Match(path string) *Match

Match matches a given path

func (*Path) Render

func (p *Path) Render(values map[string]string, usedValues map[string]struct{}) (string, error)

Render a path for a given list of values

type RenderResponse

type RenderResponse struct {
	DataResponse
	Template string
	// contains filtered or unexported fields
}

RenderResponse renders data

func (*RenderResponse) Apply

Apply response

func (*RenderResponse) SetNoCache

func (r *RenderResponse) SetNoCache() *RenderResponse

SetNoCache helper

type Request

type Request struct {
	Params RequestParams
	Values sync.Map
	// contains filtered or unexported fields
}

Request object stores the actual HTTP Request, Session, Params and attached Values

func CreateRequest

func CreateRequest(r *http.Request, s *Session) *Request

CreateRequest creates a new request, with optional http.Request and Session. If any variable is nil it is ignored, otherwise it is copied into the new Request.

func RequestFromContext

func RequestFromContext(ctx context.Context) *Request

RequestFromContext retrieves the request from the context, if available

func (*Request) Form

func (r *Request) Form(name string) ([]string, error)

Form get POST value

func (*Request) Form1

func (r *Request) Form1(name string) (string, error)

Form1 get first POST value

func (*Request) FormAll

func (r *Request) FormAll() (map[string][]string, error)

FormAll get all POST values

func (*Request) Query

func (r *Request) Query(name string) ([]string, error)

Query looks up Raw Query map for Param

func (*Request) Query1

func (r *Request) Query1(name string) (string, error)

Query1 looks up Raw Query map for First Param

func (*Request) QueryAll

func (r *Request) QueryAll() url.Values

QueryAll returns a Map of the Raw Query

func (*Request) RemoteAddress

func (r *Request) RemoteAddress() []string

RemoteAddress get the requests real remote address

func (*Request) Request

func (r *Request) Request() *http.Request

Request getter

func (*Request) Session

func (r *Request) Session() *Session

Session getter

type RequestParams

type RequestParams map[string]string

RequestParams store string->string values for request data

type Responder

type Responder struct {
	// contains filtered or unexported fields
}

Responder generates responses

func (*Responder) Data

func (r *Responder) Data(data interface{}) *DataResponse

Data returns a data response which can be serialized

func (*Responder) Download

func (r *Responder) Download(data io.Reader, contentType string, fileName string, forceDownload bool) *Response

Download returns a download response to handle file downloads

func (*Responder) Forbidden

func (r *Responder) Forbidden(err error) *ServerErrorResponse

Forbidden creates a 403 error response

func (*Responder) HTTP

func (r *Responder) HTTP(status uint, body io.Reader) *Response

HTTP Response generator

func (*Responder) Inject

func (r *Responder) Inject(router *Router, logger flamingo.Logger, cfg *struct {
	Engine                flamingo.TemplateEngine `inject:",optional"`
	Debug                 bool                    `inject:"config:debug.mode"`
	TemplateForbidden     string                  `inject:"config:flamingo.template.err403"`
	TemplateNotFound      string                  `inject:"config:flamingo.template.err404"`
	TemplateUnavailable   string                  `inject:"config:flamingo.template.err503"`
	TemplateErrorWithCode string                  `inject:"config:flamingo.template.errWithCode"`
}) *Responder

Inject Responder dependencies

func (*Responder) NotFound

func (r *Responder) NotFound(err error) *ServerErrorResponse

NotFound creates a 404 error response

func (*Responder) Render

func (r *Responder) Render(tpl string, data interface{}) *RenderResponse

Render creates a render response, with the supplied template and data

func (*Responder) RouteRedirect

func (r *Responder) RouteRedirect(to string, data map[string]string) *RouteRedirectResponse

RouteRedirect generator

func (*Responder) ServerError

func (r *Responder) ServerError(err error) *ServerErrorResponse

ServerError creates a 500 error response

func (*Responder) ServerErrorWithCodeAndTemplate

func (r *Responder) ServerErrorWithCodeAndTemplate(err error, tpl string, status uint) *ServerErrorResponse

ServerErrorWithCodeAndTemplate error response with template and http status code

func (*Responder) TODO

func (r *Responder) TODO() *Response

TODO creates a 501 Not Implemented response

func (*Responder) URLRedirect

func (r *Responder) URLRedirect(url *url.URL) *URLRedirectResponse

URLRedirect returns a 303 redirect to a given URL

func (*Responder) Unavailable

func (r *Responder) Unavailable(err error) *ServerErrorResponse

Unavailable creates a 503 error response

type Response

type Response struct {
	Status uint
	Body   io.Reader
	Header http.Header
}

Response contains a status and a body

func (*Response) Apply

Apply response

func (*Response) SetNoCache

func (r *Response) SetNoCache() *Response

SetNoCache helper

type Result

type Result interface {
	// Apply executes the response on the http.ResponseWriter
	Apply(ctx context.Context, rw http.ResponseWriter) error
}

Result defines the generic web response

type ReverseRouter

type ReverseRouter interface {
	// Relative returns a root-relative URL, starting with `/`
	// if to starts with "/" it will be used as the target, instead of resolving the URL
	Relative(to string, params map[string]string) (*url.URL, error)
	// Absolute returns an absolute URL, with scheme and host.
	// It takes the request to construct as many information as possible
	// if to starts with "/" it will be used as the target, instead of resolving the URL
	Absolute(r *Request, to string, params map[string]string) (*url.URL, error)
}

ReverseRouter allows to retrieve urls for controller

type RouteRedirectResponse

type RouteRedirectResponse struct {
	Response
	To   string
	Data map[string]string
	// contains filtered or unexported fields
}

RouteRedirectResponse redirects to a certain route

func (*RouteRedirectResponse) Apply

Apply response

func (*RouteRedirectResponse) Permanent

Permanent marks a redirect as being permanent (http 301)

func (*RouteRedirectResponse) SetNoCache

SetNoCache helper

type Router

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

func (*Router) Absolute

func (r *Router) Absolute(req *Request, to string, params map[string]string) (*url.URL, error)

Absolute returns an absolute URL, with scheme and host. It takes the request to construct as many information as possible

func (*Router) Base

func (r *Router) Base() *url.URL

func (*Router) Data

func (r *Router) Data(ctx context.Context, handler string, params map[interface{}]interface{}) interface{}

Data calls a flamingo data controller

func (*Router) Handler

func (r *Router) Handler() http.Handler

func (*Router) Inject

func (r *Router) Inject(
	cfg *struct {
		// base url configuration
		Scheme       string         `inject:"config:flamingo.router.scheme,optional"`
		Host         string         `inject:"config:flamingo.router.host,optional"`
		Path         string         `inject:"config:flamingo.router.path,optional"`
		SessionStore sessions.Store `inject:",optional"`
	},
	eventRouter flamingo.EventRouter,
	filterProvider filterProvider,
	routesProvider routesProvider,
	logger flamingo.Logger,
	configArea *config.Area,
)

func (*Router) ListenAndServe

func (r *Router) ListenAndServe(addr string) error

func (*Router) Relative

func (r *Router) Relative(to string, params map[string]string) (*url.URL, error)

Relative returns a root-relative URL, starting with `/`

func (*Router) URL

func (r *Router) URL(to string, params map[string]string) (*url.URL, error)

deprecated

type RouterRegistry

type RouterRegistry struct {
	// contains filtered or unexported fields
}

RouterRegistry holds a list of all routes and handlers to be registered in modules.

We have: routes: key-params -> path, for reverse routes

path: url-pattern -> key+params

Handler: key -> Controller

func NewRegistry

func NewRegistry() *RouterRegistry

NewRegistry creates a new RouterRegistry

func (*RouterRegistry) Alias

func (registry *RouterRegistry) Alias(name, to string)

Alias for an existing router definition

func (*RouterRegistry) GetRoutes

func (registry *RouterRegistry) GetRoutes() []*Handler

GetRoutes returns registered Routes

func (*RouterRegistry) HandleAny

func (registry *RouterRegistry) HandleAny(name string, action Action)

HandleAny serves as a fallback to handle HTTP requests which are not taken care of by other handlers

func (*RouterRegistry) HandleData

func (registry *RouterRegistry) HandleData(name string, action DataAction)

HandleData sets the controllers data action

func (*RouterRegistry) HandleDelete

func (registry *RouterRegistry) HandleDelete(name string, action Action)

HandleDelete handles HTTP DELETE requests

func (*RouterRegistry) HandleGet

func (registry *RouterRegistry) HandleGet(name string, action Action)

HandleGet handles a HTTP GET request

func (*RouterRegistry) HandleHead

func (registry *RouterRegistry) HandleHead(name string, action Action)

HandleHead handles HTTP HEAD requests

func (*RouterRegistry) HandleMethod

func (registry *RouterRegistry) HandleMethod(method, name string, action Action)

HandleMethod handles requests for the specified HTTP Method

func (*RouterRegistry) HandleOptions

func (registry *RouterRegistry) HandleOptions(name string, action Action)

HandleOptions handles HTTP OPTIONS requests

func (*RouterRegistry) HandlePost

func (registry *RouterRegistry) HandlePost(name string, action Action)

HandlePost handles HTTP POST requests

func (*RouterRegistry) HandlePut

func (registry *RouterRegistry) HandlePut(name string, action Action)

HandlePut handles HTTP PUT requests

func (*RouterRegistry) Has

func (registry *RouterRegistry) Has(method, name string) bool

Has checks if a method is set for a given handler name

func (*RouterRegistry) HasAny

func (registry *RouterRegistry) HasAny(name string) bool

HasAny checks if an any handler is set for a given name

func (*RouterRegistry) HasData

func (registry *RouterRegistry) HasData(name string) bool

HasData checks if a data handler is set for a given name

func (*RouterRegistry) Reverse

func (registry *RouterRegistry) Reverse(name string, params map[string]string) (string, error)

Reverse builds the path from a named route with params

func (*RouterRegistry) Route

func (registry *RouterRegistry) Route(path, handler string) (*Handler, error)

Route assigns a route to a Handler

type RoutesModule

type RoutesModule interface {
	Routes(registry *RouterRegistry)
}

RoutesModule defines a router RoutesModule, which is able to register routes

type ServerErrorResponse

type ServerErrorResponse struct {
	RenderResponse
	Error error
}

ServerErrorResponse returns a server error, by default http 500

func (*ServerErrorResponse) Apply

Apply response

func (*ServerErrorResponse) SetNoCache

func (r *ServerErrorResponse) SetNoCache() *ServerErrorResponse

SetNoCache helper

type Session

type Session struct {
	// contains filtered or unexported fields
}

Session holds the data connected to the current user session

func EmptySession

func EmptySession() *Session

EmptySession creates an empty session instance for testing etc.

func SessionFromContext

func SessionFromContext(ctx context.Context) *Session

SessionFromContext allows to retrieve the stored session

func (*Session) AddFlash

func (s *Session) AddFlash(value interface{}, vars ...string)

AddFlash adds a flash message to the session. todo change?

func (*Session) ClearAll

func (s *Session) ClearAll() *Session

ClearAll removes all values from the session

func (*Session) Delete

func (s *Session) Delete(key interface{})

Delete a given key from the session

func (*Session) Flashes

func (s *Session) Flashes(vars ...string) []interface{}

Flashes returns a slice of flash messages from the session todo change?

func (*Session) ID

func (s *Session) ID() (id string)

ID returns the Session id

func (*Session) Keys

func (s *Session) Keys() []interface{}

Keys returns an unordered list of session keys

func (*Session) Load

func (s *Session) Load(key interface{}) (data interface{}, ok bool)

Load data by a key

func (*Session) Store

func (s *Session) Store(key interface{}, data interface{}) *Session

Store data with a key in the Session

func (*Session) Try

func (s *Session) Try(key interface{}) (data interface{})

Try to load data by a key

type SetPartialDataFunc

type SetPartialDataFunc struct{}

SetPartialDataFunc allows to set partial data

func (*SetPartialDataFunc) Func

func (*SetPartialDataFunc) Func(c context.Context) interface{}

Func getter to bind the context

type URLRedirectResponse

type URLRedirectResponse struct {
	Response
	URL *url.URL
}

URLRedirectResponse redirects to a certain URL

func (*URLRedirectResponse) Apply

Apply response

func (*URLRedirectResponse) Permanent

func (r *URLRedirectResponse) Permanent() *URLRedirectResponse

Permanent marks a redirect as being permanent (http 301)

func (*URLRedirectResponse) SetNoCache

func (r *URLRedirectResponse) SetNoCache() *URLRedirectResponse

SetNoCache helper

Jump to

Keyboard shortcuts

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