maze

package module
v0.0.0-...-f1a1e36 Latest Latest
Warning

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

Go to latest
Published: Apr 5, 2021 License: MIT Imports: 16 Imported by: 3

README

maze

HTTP router based on chained filter rules

Maze enable us to define a set of request interceptors, called filters, where we can do stuff, like beginning a database transaction or checking the user credencials.

Lets start with the classic "Hello World"

package main

import (
	"net/http"

	"github.com/quintans/maze"
	"github.com/quintans/toolkit/log"
)

func init() {
	maze.SetLogger(log.LoggerFor("github.com/quintans/maze"))
}

func main() {
	// creates maze with the default context factory.
	var mz = maze.NewMaze(nil)

	// Hello World filter
	mz.GET("/*", func(c maze.IContext) error {
		return c.TEXT(http.StatusOK, "Hello World!")
	})

	if err := mz.ListenAndServe(":8888"); err != nil {
		panic(err)
	}
}

In this example, any GET will return "Hello World!", because every url matches the filter "/". The asterisk means anything. We can filter what ends with "" (e.g: "/static/") or what begins with "" (e.g: "*.js").

We can chain filters. Inside a filter, if we want to call the next filter in the chain we just use the Proceed() method of the context.

The following example shows the chaining of filters and the use of Proceed(). We move the Hello filter to a method and add another filter to trace the requests.

// logs request path
func trace(c maze.IContext) error {
	logger.Debugf("requesting %s", c.GetRequest().URL.Path)
	return c.Proceed()
}

// Hello World filter
func helloWorld(c maze.IContext) error {
	return c.TEXT("Hello World!")
}

func main() {
	//...

	mz.GET("/*", trace, helloWorld)

	//...
}

In each filter we decide if we want to proceed or to return and ending the request.

We can also define rules to declare REST endpoints like this: "/rest/greet/:Id/sayhi/:Name".

It is also possible to extend the context.

Here is a complete example:

package main

import (
	"fmt"
	"net/http"

	"github.com/quintans/maze"
	"github.com/quintans/toolkit/log"
)

func init() {
	maze.SetLogger(log.LoggerFor("github.com/quintans/maze"))
}

// logs request path
func trace(c maze.IContext) error {
	fmt.Println("==> requesting", c.GetRequest().URL.Path)
	return c.Proceed()
}

type GreetingService struct{}

func (this *GreetingService) SayHi(ctx maze.IContext) error {
	var q struct {
		Id   int    `schema:"id"`
		Name string `schema:"name"`
	}
	if err := ctx.Vars(&q); err != nil {
		return err
	}

	return ctx.(*AppCtx).Reply(fmt.Sprintf("Hi %s. Your ID is %d", q.Name, q.Id))
}

type AppCtx struct {
	*maze.Context
}

func (this *AppCtx) Proceed() error {
	return this.Next(this)
}

// Reply writes in JSON format.
// This is a demonstrative example of how we can extend Context.
func (this *AppCtx) Reply(value interface{}) error {
	return this.JSON(http.StatusOK, value)
}

func main() {
	// creates maze with specialized context factory.
	var mz = maze.NewMaze(maze.WithContextFactory(func(w http.ResponseWriter, r *http.Request, filters []*maze.Filter) maze.IContext {
		var ctx = new(AppCtx)
		ctx.MazeContext = maze.NewContext(w, r, filters)
		return ctx
	}))

	var greetingsService = new(GreetingService)
	// we apply a filter to requests starting with /rest/greet/*
	mz.Push("/rest/greet/*", trace)

	// since the rule does not start with '/' and the last rule ended with '*'
	// the applied rule will be the concatenation of the previous one
	// with this one resulting in "/rest/greet/sayhi/:Id"
	mz.GET("sayhi/:Id", greetingsService.SayHi)

	mz.GET("/*", func(ctx maze.IContext) error {
		ctx.TEXT(
			http.StatusBadRequest,
			"invalid URI.\nUse /rest/greet/sayhi/:Id[?name=Name] eg: /rest/greet/sayhi/123?name=Quintans",
		)
		return nil
	})

	fmt.Println("Listening at port 8888")
	if err := mz.ListenAndServe(":8888"); err != nil {
		panic(err)
	}
}

Run it and access http://localhost:8888/rest/greet/sayhi/123?name=Quintans

Documentation

Index

Constants

View Source
const (
	UNKNOWN_SRV = "JSONRPC01"
	UNKNOWN_ACT = "JSONRPC02"
)
View Source
const (
	WILDCARD        = "*"
	WILDCARD_BEFORE = -1
	WILDCARD_AFTER  = 1
)
View Source
const ISO8601 = "2006-01-02T15:04:05.999999999Z0700"

Variables

This section is empty.

Functions

func ResponseBuffer

func ResponseBuffer(c IContext) error

ResponseBuffer buffers the response, permitting setting headers after starting writing the response.

func StaticGz

func StaticGz(dir string) func(c IContext) error

Types

type Action

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

func NewAction

func NewAction(name string) *Action

func (*Action) SetFilters

func (a *Action) SetFilters(filters ...Handler)

type ContextFactory

type ContextFactory func(l Logger, w http.ResponseWriter, r *http.Request, filters []*Filter) IContext

type Filter

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

func NewFilter

func NewFilter(rule string, handler Handler) *Filter

func (*Filter) IsValid

func (f *Filter) IsValid(request *http.Request) bool

func (*Filter) String

func (f *Filter) String() string

type Filterer

type Filterer interface {
	Handle(ctx IContext) error
}

type Handler

type Handler func(IContext) error

type IContext

type IContext interface {
	Proceed() error
	GetResponse() http.ResponseWriter
	SetResponse(http.ResponseWriter)
	GetRequest() *http.Request
	GetAttribute(interface{}) interface{}
	SetAttribute(interface{}, interface{})
	CurrentFilter() *Filter

	// Payload put the json string in the request body into the struct passed as an interface{}
	Payload(interface{}) error
	// PathVars put the path parameters in a url into the struct passed as an interface{}
	PathVars(interface{}) error
	// QueryVars put the parameters in the query part of a url into the struct passed as an interface{}
	QueryVars(interface{}) error
	// Vars
	Vars(interface{}) error
	// Values gets a path parameter converter
	PathValues() Values
	// Values gets a parameter converter (path + query)
	Values() Values
	// Load calls Vars and Payload
	Load(value interface{}) error
	// TEXT converts to string the interface{} value and sends it into the response with a status code
	TEXT(int, interface{}) error
	// JSON marshals the interface{} value into a json string and sends it into the response with a status code
	JSON(int, interface{}) error
}

type JsonRpc

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

func NewJsonRpc

func NewJsonRpc(logger Logger, svc interface{}, filters ...Handler) (*JsonRpc, error)

func (*JsonRpc) Build

func (r *JsonRpc) Build(servicePath string) []*Filter

func (*JsonRpc) GetAction

func (r *JsonRpc) GetAction(actionName string) *Action

func (*JsonRpc) SetActionFilters

func (r *JsonRpc) SetActionFilters(actionName string, filters ...Handler)

func (*JsonRpc) SetFilters

func (r *JsonRpc) SetFilters(filters ...Handler)

type Logger

type Logger interface {
	Debugf(format string, args ...interface{})
	Infof(format string, args ...interface{})
	Warnf(format string, args ...interface{})
	Errorf(format string, args ...interface{})
	Fatalf(format string, args ...interface{})
	WithTags(tags Tags) Logger
	WithError(err error) Logger
}

type LogrusWrap

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

func NewLogrus

func NewLogrus(logger *logrus.Logger) LogrusWrap

func (LogrusWrap) Debugf

func (l LogrusWrap) Debugf(format string, args ...interface{})

func (LogrusWrap) Errorf

func (l LogrusWrap) Errorf(format string, args ...interface{})

func (LogrusWrap) Fatalf

func (l LogrusWrap) Fatalf(format string, args ...interface{})

func (LogrusWrap) Infof

func (l LogrusWrap) Infof(format string, args ...interface{})

func (LogrusWrap) Warnf

func (l LogrusWrap) Warnf(format string, args ...interface{})

func (LogrusWrap) WithError

func (l LogrusWrap) WithError(err error) Logger

func (LogrusWrap) WithTags

func (l LogrusWrap) WithTags(vals Tags) Logger

type Maze

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

func NewMaze

func NewMaze(options ...Option) *Maze

NewMaze creates maze with context factory. If nil, it uses a default context factory

func (*Maze) Add

func (m *Maze) Add(filters ...*Filter)

func (*Maze) DELETE

func (m *Maze) DELETE(rule string, filters ...Handler)

func (*Maze) GET

func (m *Maze) GET(rule string, filters ...Handler)

func (*Maze) ListenAndServe

func (m *Maze) ListenAndServe(addr string) error

func (*Maze) PATCH

func (m *Maze) PATCH(rule string, filters ...Handler)

func (*Maze) POST

func (m *Maze) POST(rule string, filters ...Handler)

func (*Maze) PUT

func (m *Maze) PUT(rule string, filters ...Handler)

func (*Maze) Push

func (m *Maze) Push(rule string, filters ...Handler)

func (*Maze) PushMethod

func (m *Maze) PushMethod(methods []string, rule string, handlers ...Handler)

PushMethod adds the filters to the end of the last filters. If the current rule does NOT start with '/', the applied rule will be the concatenation of the last rule that started with '/' and ended with a '*' with this current one (the '*' is omitted). eg: /greet/* + sayHi/:Id = /greet/sayHi/:Id

func (*Maze) ServeHTTP

func (m *Maze) ServeHTTP(w http.ResponseWriter, r *http.Request)

func (*Maze) Static

func (m *Maze) Static(rule string, dir string)

Static serves static content. rule defines the rule and dir the relative path

type MazeContext

type MazeContext struct {
	Response   http.ResponseWriter
	Request    *http.Request
	Attributes map[interface{}]interface{} // attributes only valid in this request
	// contains filtered or unexported fields
}

func NewContext

func NewContext(logger Logger, w http.ResponseWriter, r *http.Request, filters []*Filter) *MazeContext

func (*MazeContext) CurrentFilter

func (c *MazeContext) CurrentFilter() *Filter

func (*MazeContext) GetAttribute

func (c *MazeContext) GetAttribute(key interface{}) interface{}

func (*MazeContext) GetRequest

func (c *MazeContext) GetRequest() *http.Request

func (*MazeContext) GetResponse

func (c *MazeContext) GetResponse() http.ResponseWriter

func (*MazeContext) JSON

func (c *MazeContext) JSON(status int, value interface{}) error

func (*MazeContext) Load

func (c *MazeContext) Load(value interface{}) error

func (*MazeContext) Next

func (c *MazeContext) Next(mc IContext) error

func (*MazeContext) PathValues

func (c *MazeContext) PathValues() Values

func (*MazeContext) PathVars

func (c *MazeContext) PathVars(value interface{}) error

PathVars put the path parameters in a url into the struct passed as an interface{}

func (*MazeContext) Payload

func (c *MazeContext) Payload(value interface{}) error

func (*MazeContext) Proceed

func (c *MazeContext) Proceed() error

Proceed proceeds to the next valid rule This method should be reimplemented in specialized Context, extending this one

func (*MazeContext) QueryVars

func (c *MazeContext) QueryVars(value interface{}) error

QueryVars put the parameters in the query part of a url into the struct passed as an interface{}

func (*MazeContext) SetAttribute

func (c *MazeContext) SetAttribute(key interface{}, value interface{})

func (*MazeContext) SetResponse

func (c *MazeContext) SetResponse(w http.ResponseWriter)

func (*MazeContext) TEXT

func (c *MazeContext) TEXT(status int, value interface{}) error

TEXT transforms value to text and send it as text content type with the specified status (eg: http.StatusOK)

func (*MazeContext) Values

func (c *MazeContext) Values() Values

func (*MazeContext) Vars

func (c *MazeContext) Vars(value interface{}) error

type Option

type Option func(m *Maze)

func WithContextFactory

func WithContextFactory(cf ContextFactory) Option

func WithLogger

func WithLogger(l Logger) Option

type Sse

type Sse struct {
	Id    string
	Retry uint64
	Event string
	Data  []string
}

Sse is a server sent event

func NewSse

func NewSse(data ...string) Sse

NewSse creates a sse with only data

type SseBroker

type SseBroker struct {
	sync.RWMutex

	OnConnect func() (Sse, error)
	// contains filtered or unexported fields
}

func NewSseBroker

func NewSseBroker() *SseBroker

func (*SseBroker) HasSubscribers

func (s *SseBroker) HasSubscribers() bool

func (*SseBroker) Send

func (s *SseBroker) Send(e Sse)

func (*SseBroker) Serve

func (s *SseBroker) Serve(c IContext) error

type Tags

type Tags map[string]interface{}

type Values

type Values map[string][]string

func (Values) AsBool

func (p Values) AsBool(k string) bool

func (Values) AsBools

func (p Values) AsBools(k string) []bool

func (Values) AsFloat

func (p Values) AsFloat(k string) float64

func (Values) AsFloats

func (p Values) AsFloats(k string) []float64

func (Values) AsInt

func (p Values) AsInt(k string) int64

func (Values) AsInts

func (p Values) AsInts(k string) []int64

func (Values) AsString

func (p Values) AsString(k string) string

func (Values) AsStrings

func (p Values) AsStrings(k string) []string

func (Values) AsTime

func (p Values) AsTime(k string) time.Time

func (Values) AsTimes

func (p Values) AsTimes(k string) []time.Time

func (Values) AsUint

func (p Values) AsUint(k string) uint64

func (Values) AsUints

func (p Values) AsUints(k string) []uint64

Directories

Path Synopsis
example

Jump to

Keyboard shortcuts

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