hero

package
v12.3.1 Latest Latest
Warning

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

Go to latest
Published: Feb 23, 2023 License: BSD-3-Clause Imports: 17 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrSeeOther may be returned from a dependency handler to skip a specific dependency
	// based on custom logic.
	ErrSeeOther = fmt.Errorf("see other")
	// ErrStopExecution may be returned from a dependency handler to stop
	// and return the execution of the function without error (it calls ctx.StopExecution() too).
	// It may be occurred from request-scoped dependencies as well.
	ErrStopExecution = fmt.Errorf("stop execution")
)
View Source
var (
	// DefaultErrStatusCode is the default error status code (400)
	// when the response contains a non-nil error or a request-scoped binding error occur.
	DefaultErrStatusCode = 400

	// DefaultErrorHandler is the default error handler which is fired
	// when a function returns a non-nil error or a request-scoped dependency failed to binded.
	DefaultErrorHandler = ErrorHandlerFunc(func(ctx *context.Context, err error) {
		if err != ErrStopExecution {
			if status := ctx.GetStatusCode(); status == 0 || !context.StatusCodeNotSuccessful(status) {
				ctx.StatusCode(DefaultErrStatusCode)
			}

			_, _ = ctx.WriteString(err.Error())
		}

		ctx.StopExecution()
	})
)
View Source
var BuiltinDependencies = []*Dependency{

	newDependency(func(ctx *context.Context) *context.Context { return ctx }, true, nil).Explicitly(),

	newDependency(func(ctx *context.Context) stdContext.Context {
		return ctx.Request().Context()
	}, true, nil).Explicitly(),

	newDependency(func(ctx *context.Context) *sessions.Session {
		session := sessions.Get(ctx)
		if session == nil {
			ctx.Application().Logger().Debugf("binding: session is nil\nMaybe inside HandleHTTPError? Register it with app.UseRouter(sess.Handler()) to fix it")

		}

		return session
	}, true, nil).Explicitly(),

	newDependency(func(ctx *context.Context) *golog.Logger {
		return ctx.Application().Logger()
	}, true, nil).Explicitly(),

	newDependency(func(ctx *context.Context) time.Time {
		return time.Now()
	}, true, nil).Explicitly(),

	newDependency(func(ctx *context.Context) *http.Request {
		return ctx.Request()
	}, true, nil).Explicitly(),

	newDependency(func(ctx *context.Context) http.ResponseWriter {
		return ctx.ResponseWriter()
	}, true, nil).Explicitly(),

	newDependency(func(ctx *context.Context) http.Header {
		return ctx.Request().Header
	}, true, nil).Explicitly(),

	newDependency(func(ctx *context.Context) net.IP {
		return net.ParseIP(ctx.RemoteAddr())
	}, true, nil).Explicitly(),

	newDependency(func(ctx *context.Context) Code {
		return Code(ctx.GetStatusCode())
	}, true, nil).Explicitly(),

	newDependency(func(ctx *context.Context) Err {
		err := ctx.GetErr()
		if err == nil {
			return nil
		}
		return err
	}, true, nil).Explicitly(),

	newDependency(func(ctx *context.Context) context.User {
		u := ctx.User()
		if u == nil {
			return nil
		}

		return u
	}, true, nil),
}

BuiltinDependencies is a list of builtin dependencies that are added on Container's initilization. Contains the iris context, standard context, iris sessions and time dependencies.

View Source
var Default = New().WithLogger(golog.Default)

Default is the default container value which can be used for dependencies share.

View Source
var DefaultDependencyMatcher = func(dep *Dependency, in reflect.Type) bool {
	if dep.Explicit {
		return dep.DestType == in
	}

	return dep.DestType == nil || equalTypes(dep.DestType, in)
}

DefaultDependencyMatcher is the default dependency match function for all DI containers. It is used to collect dependencies from struct's fields and function's parameters.

View Source
var ErrMissingDependency = errors.New("missing dependency")

ErrMissingDependency may returned only from the `Container.Inject` method when not a matching dependency found for "toPtr".

Functions

func Handler

func Handler(fn interface{}) context.Handler

Handler accepts a "handler" function which can accept any input arguments that match with the Container's `Dependencies` and any output result; like string, int (string,int), custom structs, Result(View | Response) and anything you can imagine. It returns a standard `iris/context.Handler` which can be used anywhere in an Iris Application, as middleware or as simple route handler or subdomain's handler.

Types

type Code

type Code int

Code is a special type for status code. It's used for a builtin dependency to map the status code given by a previous method or middleware. Use a type like that in order to not conflict with any developer-registered dependencies. Alternatively: ctx.GetStatusCode().

func (Code) String

func (code Code) String() string

String implements the fmt.Stringer interface. Returns the text corresponding to this status code, e.g. "Not Found". Same as iris.StatusText(int(code)).

func (Code) Value

func (code Code) Value() int

Value returns the underline int value. Same as int(code).

type Container

type Container struct {
	// Optional Logger to report dependencies and matched bindings
	// per struct, function and method.
	// By default it is set by the Party creator of this Container.
	Logger *golog.Logger
	// Sorter specifies how the inputs should be sorted before binded.
	// Defaults to sort by "thinnest" target empty interface.
	Sorter Sorter
	// The dependencies entries.
	Dependencies []*Dependency

	// MarkExportedFieldsAsRequired reports whether all struct's fields
	// MUST be binded to a dependency from the `Dependencies` list field.
	// In-short, if it is set to true and if at least one exported field
	// of a struct is not binded to a dependency then
	// the entire application will exit with a panic message before server startup.
	MarkExportedFieldsAsRequired bool
	// DisablePayloadAutoBinding reports whether
	// a function's parameter or struct's field of struct type
	// should not be binded automatically to the request body (e.g. JSON)
	// if a dependency for that type is missing.
	// By default the binder will bind structs to request body,
	// set to true to disable that kind of behavior.
	DisablePayloadAutoBinding bool

	// DisableStructDynamicBindings if true panics on struct handler (controller)
	// if at least one input binding depends on the request and not in a static structure.
	DisableStructDynamicBindings bool

	// DependencyMatcher holds the function that compares equality between
	// a dependency with an input. Defaults to DefaultMatchDependencyFunc.
	DependencyMatcher DependencyMatcher
	// GetErrorHandler should return a valid `ErrorHandler` to handle bindings AND handler dispatch errors.
	// Defaults to a functon which returns the `DefaultErrorHandler`.
	GetErrorHandler func(*context.Context) ErrorHandler // cannot be nil.
	// Reports contains an ordered list of information about bindings for further analysys and testing.
	Reports []*Report
	// contains filtered or unexported fields
}

Container contains and delivers the Dependencies that will be binded to the controller(s) or handler(s) that can be created using the Container's `Handler` and `Struct` methods.

This is not exported for being used by everyone, use it only when you want to share containers between multi mvc.go#Application or make custom hero handlers that can be used on the standard iris' APIBuilder.

For a more high-level structure please take a look at the "mvc.go#Application".

func New

func New(dependencies ...interface{}) *Container

New returns a new Container, a container for dependencies and a factory for handlers and controllers, this is used internally by the `mvc#Application` structure. Please take a look at the structure's documentation for more information.

func (*Container) Clone

func (c *Container) Clone() *Container

Clone returns a new cloned container. It copies the ErrorHandler, Dependencies and all Options from "c" receiver.

func (*Container) Handler

func (c *Container) Handler(fn interface{}) context.Handler

Handler accepts a handler "fn" function which can accept any input arguments that match with the Container's `Dependencies` and any output result; like string, int (string,int), custom structs, Result(View | Response) and more. It returns a standard `iris/context.Handler` which can be used anywhere in an Iris Application, as middleware or as simple route handler or subdomain's handler.

func(...<T>) iris.Handler

- if <T> are all static dependencies then there is no reflection involved at serve-time.

func(pathParameter string, ...<T>)

- one or more path parameters (e.g. :uid, :string, :int, :path, :uint64) are automatically binded to the first input Go standard types (string, int, uint64 and e.t.c.)

func(<T>) error

- if a function returns an error then this error's text is sent to the client automatically.

func(<T>) <R>

- The result of the function is a dependency too. If <R> is a request-scope dependency (dynamic) then this function will be called at every request.

func(<T>) <R>

- If <R> is static dependency (e.g. a database or a service) then its result can be used as a static dependency to the next dependencies or to the controller/function itself.

func (*Container) HandlerWithParams

func (c *Container) HandlerWithParams(fn interface{}, paramsCount int) context.Handler

HandlerWithParams same as `Handler` but it can receive a total path parameters counts to resolve coblex path parameters input dependencies.

func (*Container) Inject

func (c *Container) Inject(toPtr interface{}) error

Inject SHOULD only be used outside of HTTP handlers (performance is not priority for this method) as it does not pre-calculate the available list of bindings for the "toPtr" and the registered dependencies.

It sets a static-only matching dependency to the value of "toPtr". The parameter "toPtr" SHOULD be a pointer to a value corresponding to a dependency, like input parameter of a handler or field of a struct.

If no matching dependency found, the `Inject` method returns an `ErrMissingDependency` and "toPtr" keeps its original state (e.g. nil).

Example Code: c.Register(&LocalDatabase{...}) [...] var db Database err := c.Inject(&db)

func (*Container) Register

func (c *Container) Register(dependency interface{}) *Dependency

Register adds a dependency. The value can be a single struct value or a function. Follow the rules: * <T>{structValue} * func(accepts <T>) returns <D> or (<D>, error) * func(accepts iris.Context) returns <D> or (<D>, error) * func(accepts1 iris.Context, accepts2 *hero.Input) returns <D> or (<D>, error)

A Dependency can accept a previous registered dependency and return a new one or the same updated. * func(accepts1 <D>, accepts2 <T>) returns <E> or (<E>, error) or error * func(acceptsPathParameter1 string, id uint64) returns <T> or (<T>, error)

Usage:

- Register(loggerService{prefix: "dev"}) - Register(func(ctx iris.Context) User {...}) - Register(func(User) OtherResponse {...})

func (*Container) Struct

func (c *Container) Struct(ptrValue interface{}, partyParamsCount int) *Struct

Struct accepts a pointer to a struct value and returns a structure which contains bindings for the struct's fields and a method to extract a Handler from this struct's method.

func (*Container) UseResultHandler

func (c *Container) UseResultHandler(handler func(next ResultHandler) ResultHandler) *Container

UseResultHandler adds a result handler to the Container. A result handler can be used to inject the returned struct value from a request handler or to replace the default renderer.

func (*Container) WithLogger

func (c *Container) WithLogger(logger *golog.Logger) *Container

WithLogger injects a logger to use to debug dependencies and bindings.

type Dependency

type Dependency struct {
	OriginalValue interface{} // Used for debugging and for logging only.
	Source        Source
	Handle        DependencyHandler
	// It's the exact type of return to bind, if declared to return <T>, otherwise nil.
	DestType reflect.Type
	Static   bool
	// If true then input and dependency DestType should be indedical,
	// not just assiginable to each other.
	// Example of use case: depenendency like time.Time that we want to be bindable
	// only to time.Time inputs and not to a service with a `String() string` method that time.Time struct implements too.
	Explicit bool

	// Match holds the matcher. Defaults to the Container's one.
	Match DependencyMatchFunc
}

Dependency describes the design-time dependency to be injected at serve time. Contains its source location, the dependency handler (provider) itself and information such as static for static struct values or explicit to bind a value to its exact DestType and not if just assignable to it (interfaces).

func NewDependency

func NewDependency(dependency interface{}, funcDependencies ...*Dependency) *Dependency

NewDependency converts a function or a function which accepts other dependencies or static struct value to a *Dependency.

See `Container.Handler` for more.

func Register

func Register(dependency interface{}) *Dependency

Register adds a dependency. The value can be a single struct value-instance or a function which has one input and one output, that output type will be binded to the handler's input argument, if matching.

Usage: - Register(loggerService{prefix: "dev"}) - Register(func(ctx iris.Context) User {...}) - Register(func(User) OtherResponse {...})

func (*Dependency) Explicitly

func (d *Dependency) Explicitly() *Dependency

Explicitly sets Explicit option to true. See `Dependency.Explicit` field godoc for more.

Returns itself.

func (*Dependency) String

func (d *Dependency) String() string

type DependencyHandler

type DependencyHandler = func(ctx *context.Context, input *Input) (reflect.Value, error)

DependencyHandler is the native function declaration which implementors should return a value match to an input.

type DependencyMatchFunc

type DependencyMatchFunc = func(in reflect.Type) bool

DependencyMatchFunc type alias describes dependency match function with an input (field or parameter).

See "DependencyMatcher" too, which can be used on a Container to change the way dependencies are matched to inputs for all dependencies.

func ToDependencyMatchFunc

func ToDependencyMatchFunc(d *Dependency, match DependencyMatcher) DependencyMatchFunc

ToDependencyMatchFunc converts a DependencyMatcher (generic for all dependencies) to a dependency-specific input matcher.

type DependencyMatcher

type DependencyMatcher = func(*Dependency, reflect.Type) bool

DependencyMatcher type alias describes a dependency match function.

type Err

type Err error

Err is a special type for error stored in mvc responses or context. It's used for a builtin dependency to map the error given by a previous method or middleware. Use a type like that in order to not conflict with any developer-registered dependencies. Alternatively: ctx.GetErr().

type ErrorHandler

type ErrorHandler interface {
	HandleError(*context.Context, error)
}

ErrorHandler describes an interface to handle errors per hero handler and its dependencies.

Handles non-nil errors return from a hero handler or a controller's method (see `getBindingsFor` and `Handler`) the error may return from a request-scoped dependency too (see `Handler`).

type ErrorHandlerFunc

type ErrorHandlerFunc func(*context.Context, error)

ErrorHandlerFunc implements the `ErrorHandler`. It describes the type defnition for an error function handler.

func (ErrorHandlerFunc) HandleError

func (fn ErrorHandlerFunc) HandleError(ctx *context.Context, err error)

HandleError fires when a non-nil error returns from a request-scoped dependency at serve-time or the handler itself.

type Input

type Input struct {
	Index            int    // for func inputs
	StructFieldIndex []int  // for struct fields in order to support embedded ones.
	StructFieldName  string // the struct field's name.
	Type             reflect.Type
	// contains filtered or unexported fields
}

Input contains the input reference of which a dependency is binded to.

type PreflightResult

type PreflightResult interface {
	Preflight(*context.Context) error
}

PreflightResult is an interface which implementers should be responsible to perform preflight checks of a <T> resource (or Result) before sent to the client.

If a non-nil error returned from the `Preflight` method then the JSON result will be not sent to the client and an ErrorHandler will be responsible to render the error.

Usage: a custom struct value will be a JSON body response (by-default) but it contains "Code int" and `ID string` fields, the "Code" should be the status code of the response and the "ID" should be sent as a Header of "X-Request-ID: $ID".

The caller can manage it at the handler itself. However, to reduce thoese type of duplications it's preferable to use such a standard interface instead.

The Preflight method can return `iris.ErrStopExecution` to render and override any interface that the structure value may implement, e.g. mvc.Result.

type Report

type Report struct {
	// The name is the last part of the name of a struct or its methods or a function.
	// Each name is splited by its package.struct.field or package.funcName or package.func.inlineFunc.
	Name string
	// If it's a struct or package or function
	// then it contains children reports of each one of its methods or input parameters
	// respectfully.
	Reports []*Report

	Parent  *Report
	Entries []ReportEntry
}

A Report holds meta information about dependency sources and target values per package, struct, struct's fields, struct's method, package-level function or closure. E.g. main -> (*UserController) -> HandleHTTPError.

type ReportEntry

type ReportEntry struct {
	InputPosition   int          // struct field position or parameter position.
	InputFieldName  string       // if it's a struct field, then this is its type name (we can't get param names).
	InputFieldType  reflect.Type // the input's type.
	DependencyValue interface{}  // the dependency value binded to that InputPosition of Name.
	DependencyFile  string       // the file
	DependencyLine  int          // and line number of the dependency's value.
	Static          bool
}

A ReportEntry holds the information about a binding.

type Response

type Response struct {
	Code        int
	ContentType string
	Content     []byte

	// If not empty then content type is the "text/plain"
	// and content is the text as []byte. If not empty and
	// the "Lang" field is not empty then this "Text" field
	// becomes the current locale file's key.
	Text string
	// If not empty then "Text" field becomes the locale file's key that should point
	// to a translation file's unique key. See `Object` for locale template data.
	// The "Lang" field is the language code
	// that should render the text inside the locale file's key.
	Lang string
	// If not nil then it will fire that as "application/json" or any
	// previously set "ContentType". If "Lang" and "Text" are not empty
	// then this "Object" field becomes the template data that the
	// locale text should use to be rendered.
	Object interface{}

	// If Path is not empty then it will redirect
	// the client to this Path, if Code is >= 300 and < 400
	// then it will use that Code to do the redirection, otherwise
	// StatusFound(302) or StatusSeeOther(303) for post methods will be used.
	// Except when err != nil.
	Path string

	// if not empty then fire a 400 bad request error
	// unless the Status is > 200, then fire that error code
	// with the Err.Error() string as its content.
	//
	// if Err.Error() is empty then it fires the custom error handler
	// if any otherwise the framework sends the default http error text based on the status.
	Err error
	Try func() int
}

Response completes the `methodfunc.Result` interface. It's being used as an alternative return value which wraps the status code, the content type, a content as bytes or as string and an error, it's smart enough to complete the request and send the correct response to the client.

func (Response) Dispatch

func (r Response) Dispatch(ctx *context.Context)

Dispatch writes the response result to the context's response writer.

type Result

type Result interface {
	// Dispatch should send a response to the client.
	Dispatch(*context.Context)
}

Result is a response dispatcher. All types that complete this interface can be returned as values from the method functions.

Example at: https://github.com/XpamAmAdEuS/iris/tree/master/_examples/dependency-injection/overview.

func Try

func Try(fn func() Result, failure ...Result) Result

Try will check if "fn" ran without any panics, using recovery, and return its result as the final response otherwise it returns the "failure" response if any, if not then a 400 bad request is being sent.

Example usage at: https://github.com/XpamAmAdEuS/iris/blob/master/hero/func_result_test.go.

type ResultHandler

type ResultHandler func(ctx *context.Context, v interface{}) error

ResultHandler describes the function type which should serve the "v" struct value.

type Sorter

type Sorter func(t1 reflect.Type, t2 reflect.Type) bool

Sorter is the type for sort customization of a struct's fields and its available bindable values.

Sorting applies only when a field can accept more than one registered value.

type Source

type Source struct {
	File   string
	Line   int
	Caller string
}

Source describes where a dependency is located at the source code itself.

func (Source) String

func (s Source) String() string

type Struct

type Struct struct {
	Container *Container
	Singleton bool
	// contains filtered or unexported fields
}

Struct keeps a record of a particular struct value injection. See `Container.Struct` and `mvc#Application.Handle` methods.

func (*Struct) Acquire

func (s *Struct) Acquire(ctx *context.Context) (reflect.Value, error)

Acquire returns a struct value based on the request. If the dependencies are all static then these are already set-ed at the initialization of this Struct and the same struct value instance will be returned, ignoring the Context. Otherwise a new struct value with filled fields by its pre-calculated bindings will be returned instead.

func (*Struct) MethodHandler

func (s *Struct) MethodHandler(methodName string, paramsCount int) context.Handler

MethodHandler accepts a "methodName" that should be a valid an exported method of the struct and returns its converted Handler.

Second input is optional, even zero is a valid value and can resolve path parameters correctly if from root party.

type View

type View struct {
	Name   string
	Layout string
	Data   interface{} // map or a custom struct.
	Code   int
	Err    error
}

View completes the `hero.Result` interface. It's being used as an alternative return value which wraps the template file name, layout, (any) view data, status code and error. It's smart enough to complete the request and send the correct response to the client.

Example at: https://github.com/XpamAmAdEuS/iris/blob/master/_examples/dependency-injection/overview/web/routes/hello.go.

func (View) Dispatch

func (r View) Dispatch(ctx *context.Context)

Dispatch writes the template filename, template layout and (any) data to the client. Completes the `Result` interface.

Jump to

Keyboard shortcuts

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