mirror of
https://github.com/kataras/iris.git
synced 2025-01-23 02:31:04 +01:00
Implement ResultHandler as requested at: https://github.com/kataras/iris/issues/1465
Former-commit-id: 9d76c2f00766afd53cf6e591c25f861f179dd817
This commit is contained in:
parent
68c5883bce
commit
dcf02480b3
|
@ -20,10 +20,21 @@ func handler(id int, in testInput) testOutput {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func configureAPI(api *iris.APIContainer) {
|
||||||
|
/* Here is how you can inject a return value from a handler,
|
||||||
|
in this case the "testOutput":
|
||||||
|
api.UseResultHandler(func(next iris.ResultHandler) iris.ResultHandler {
|
||||||
|
return func(ctx iris.Context, v interface{}) error {
|
||||||
|
return next(ctx, map[string]interface{}{"injected": true})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
*/
|
||||||
|
|
||||||
|
api.Post("/{id:int}", handler)
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
app := iris.New()
|
app := iris.New()
|
||||||
app.ConfigureContainer(func(api *iris.APIContainer) {
|
app.ConfigureContainer(configureAPI)
|
||||||
api.Post("/{id:int}", handler)
|
|
||||||
})
|
|
||||||
app.Listen(":8080")
|
app.Listen(":8080")
|
||||||
}
|
}
|
||||||
|
|
|
@ -2819,7 +2819,7 @@ func (ctx *context) ReadBody(ptr interface{}) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ctx.GetContentTypeRequested() {
|
switch ctx.GetContentTypeRequested() {
|
||||||
case ContentXMLHeaderValue:
|
case ContentXMLHeaderValue, ContentXMLUnreadableHeaderValue:
|
||||||
return ctx.ReadXML(ptr)
|
return ctx.ReadXML(ptr)
|
||||||
case ContentYAMLHeaderValue:
|
case ContentYAMLHeaderValue:
|
||||||
return ctx.ReadYAML(ptr)
|
return ctx.ReadYAML(ptr)
|
||||||
|
|
|
@ -64,6 +64,14 @@ func (api *APIContainer) RegisterDependency(dependency interface{}) *hero.Depend
|
||||||
return api.Container.Register(dependency)
|
return api.Container.Register(dependency)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UseResultHandler adds a result handler to the Container.
|
||||||
|
// A result handler can be used to inject the struct value
|
||||||
|
// or to replace the default renderer.
|
||||||
|
func (api *APIContainer) UseResultHandler(handler func(next hero.ResultHandler) hero.ResultHandler) *APIContainer {
|
||||||
|
api.Container.UseResultHandler(handler)
|
||||||
|
return api
|
||||||
|
}
|
||||||
|
|
||||||
// convertHandlerFuncs accepts Iris hero handlers and returns a slice of native Iris handlers.
|
// convertHandlerFuncs accepts Iris hero handlers and returns a slice of native Iris handlers.
|
||||||
func (api *APIContainer) convertHandlerFuncs(relativePath string, handlersFn ...interface{}) context.Handlers {
|
func (api *APIContainer) convertHandlerFuncs(relativePath string, handlersFn ...interface{}) context.Handlers {
|
||||||
fullpath := api.Self.GetRelPath() + relativePath
|
fullpath := api.Self.GetRelPath() + relativePath
|
||||||
|
|
4
go19.go
4
go19.go
|
@ -6,6 +6,7 @@ import (
|
||||||
"github.com/kataras/iris/v12/context"
|
"github.com/kataras/iris/v12/context"
|
||||||
"github.com/kataras/iris/v12/core/host"
|
"github.com/kataras/iris/v12/core/host"
|
||||||
"github.com/kataras/iris/v12/core/router"
|
"github.com/kataras/iris/v12/core/router"
|
||||||
|
"github.com/kataras/iris/v12/hero"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
@ -88,6 +89,9 @@ type (
|
||||||
//
|
//
|
||||||
// A shortcut for the `core/router#APIContainer`.
|
// A shortcut for the `core/router#APIContainer`.
|
||||||
APIContainer = router.APIContainer
|
APIContainer = router.APIContainer
|
||||||
|
// ResultHandler describes the function type which should serve the "v" struct value.
|
||||||
|
// See `APIContainer.UseResultHandler`.
|
||||||
|
ResultHandler = hero.ResultHandler
|
||||||
// DirOptions contains the optional settings that
|
// DirOptions contains the optional settings that
|
||||||
// `FileServer` and `Party#HandleDir` can use to serve files and assets.
|
// `FileServer` and `Party#HandleDir` can use to serve files and assets.
|
||||||
// A shortcut for the `router.DirOptions`, useful when `FileServer` or `HandleDir` is being used.
|
// A shortcut for the `router.DirOptions`, useful when `FileServer` or `HandleDir` is being used.
|
||||||
|
|
|
@ -33,6 +33,10 @@ type Container struct {
|
||||||
// GetErrorHandler should return a valid `ErrorHandler` to handle bindings AND handler dispatch errors.
|
// GetErrorHandler should return a valid `ErrorHandler` to handle bindings AND handler dispatch errors.
|
||||||
// Defaults to a functon which returns the `DefaultErrorHandler`.
|
// Defaults to a functon which returns the `DefaultErrorHandler`.
|
||||||
GetErrorHandler func(context.Context) ErrorHandler // cannot be nil.
|
GetErrorHandler func(context.Context) ErrorHandler // cannot be nil.
|
||||||
|
|
||||||
|
// resultHandlers is a list of functions that serve the return struct value of a function handler.
|
||||||
|
// Defaults to "defaultResultHandler" but it can be overridden.
|
||||||
|
resultHandlers []func(next ResultHandler) ResultHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
// BuiltinDependencies is a list of builtin dependencies that are added on Container's initilization.
|
// BuiltinDependencies is a list of builtin dependencies that are added on Container's initilization.
|
||||||
|
@ -103,6 +107,7 @@ func (c *Container) Clone() *Container {
|
||||||
clonedDeps := make([]*Dependency, len(c.Dependencies))
|
clonedDeps := make([]*Dependency, len(c.Dependencies))
|
||||||
copy(clonedDeps, c.Dependencies)
|
copy(clonedDeps, c.Dependencies)
|
||||||
cloned.Dependencies = clonedDeps
|
cloned.Dependencies = clonedDeps
|
||||||
|
cloned.resultHandlers = c.resultHandlers
|
||||||
return cloned
|
return cloned
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,6 +154,14 @@ func (c *Container) Register(dependency interface{}) *Dependency {
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UseResultHandler adds a result handler to the Container.
|
||||||
|
// A result handler can be used to inject the struct value
|
||||||
|
// or to replace the default renderer.
|
||||||
|
func (c *Container) UseResultHandler(handler func(next ResultHandler) ResultHandler) *Container {
|
||||||
|
c.resultHandlers = append(c.resultHandlers, handler)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
// Handler accepts a "handler" function which can accept any input arguments that match
|
// 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),
|
// with the Container's `Dependencies` and any output result; like string, int (string,int),
|
||||||
// custom structs, Result(View | Response) and anything you can imagine.
|
// custom structs, Result(View | Response) and anything you can imagine.
|
||||||
|
|
|
@ -10,6 +10,46 @@ import (
|
||||||
"github.com/fatih/structs"
|
"github.com/fatih/structs"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ResultHandler describes the function type which should serve the "v" struct value.
|
||||||
|
type ResultHandler func(ctx context.Context, v interface{}) error
|
||||||
|
|
||||||
|
func defaultResultHandler(ctx context.Context, v interface{}) error {
|
||||||
|
if p, ok := v.(PreflightResult); ok {
|
||||||
|
if err := p.Preflight(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if d, ok := v.(Result); ok {
|
||||||
|
d.Dispatch(ctx)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch context.TrimHeaderValue(ctx.GetContentType()) {
|
||||||
|
case context.ContentXMLHeaderValue, context.ContentXMLUnreadableHeaderValue:
|
||||||
|
_, err := ctx.XML(v)
|
||||||
|
return err
|
||||||
|
case context.ContentYAMLHeaderValue:
|
||||||
|
_, err := ctx.YAML(v)
|
||||||
|
return err
|
||||||
|
case context.ContentProtobufHeaderValue:
|
||||||
|
msg, ok := v.(proto.Message)
|
||||||
|
if !ok {
|
||||||
|
return context.ErrContentNotSupported
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := ctx.Protobuf(msg)
|
||||||
|
return err
|
||||||
|
case context.ContentMsgPackHeaderValue, context.ContentMsgPack2HeaderValue:
|
||||||
|
_, err := ctx.MsgPack(v)
|
||||||
|
return err
|
||||||
|
default:
|
||||||
|
// otherwise default to JSON.
|
||||||
|
_, err := ctx.JSON(v)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Result is a response dispatcher.
|
// Result is a response dispatcher.
|
||||||
// All types that complete this interface
|
// All types that complete this interface
|
||||||
// can be returned as values from the method functions.
|
// can be returned as values from the method functions.
|
||||||
|
@ -114,7 +154,7 @@ func dispatchErr(ctx context.Context, status int, err error) bool {
|
||||||
// Result or (Result, error) and so on...
|
// Result or (Result, error) and so on...
|
||||||
//
|
//
|
||||||
// where Get is an HTTP METHOD.
|
// where Get is an HTTP METHOD.
|
||||||
func dispatchFuncResult(ctx context.Context, values []reflect.Value) error {
|
func dispatchFuncResult(ctx context.Context, values []reflect.Value, handler ResultHandler) error {
|
||||||
if len(values) == 0 {
|
if len(values) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -278,13 +318,13 @@ func dispatchFuncResult(ctx context.Context, values []reflect.Value) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return dispatchCommon(ctx, statusCode, contentType, content, custom, found)
|
return dispatchCommon(ctx, statusCode, contentType, content, custom, handler, found)
|
||||||
}
|
}
|
||||||
|
|
||||||
// dispatchCommon is being used internally to send
|
// dispatchCommon is being used internally to send
|
||||||
// commonly used data to the response writer with a smart way.
|
// commonly used data to the response writer with a smart way.
|
||||||
func dispatchCommon(ctx context.Context,
|
func dispatchCommon(ctx context.Context,
|
||||||
statusCode int, contentType string, content []byte, v interface{}, found bool) error {
|
statusCode int, contentType string, content []byte, v interface{}, handler ResultHandler, found bool) error {
|
||||||
// if we have a false boolean as a return value
|
// if we have a false boolean as a return value
|
||||||
// then skip everything and fire a not found,
|
// then skip everything and fire a not found,
|
||||||
// we even don't care about the given status code or the object or the content.
|
// we even don't care about the given status code or the object or the content.
|
||||||
|
@ -310,46 +350,13 @@ func dispatchCommon(ctx context.Context,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// write the content type now (internal check for empty value)
|
||||||
|
ctx.ContentType(contentType)
|
||||||
|
|
||||||
if v != nil {
|
if v != nil {
|
||||||
if p, ok := v.(PreflightResult); ok {
|
return handler(ctx, v)
|
||||||
if err := p.Preflight(ctx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if d, ok := v.(Result); ok {
|
|
||||||
// write the content type now (internal check for empty value)
|
|
||||||
ctx.ContentType(contentType)
|
|
||||||
d.Dispatch(ctx)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
switch context.TrimHeaderValue(contentType) {
|
|
||||||
case context.ContentXMLHeaderValue:
|
|
||||||
_, err := ctx.XML(v)
|
|
||||||
return err
|
|
||||||
case context.ContentYAMLHeaderValue:
|
|
||||||
_, err := ctx.YAML(v)
|
|
||||||
return err
|
|
||||||
case context.ContentProtobufHeaderValue:
|
|
||||||
msg, ok := v.(proto.Message)
|
|
||||||
if !ok {
|
|
||||||
return context.ErrContentNotSupported
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := ctx.Protobuf(msg)
|
|
||||||
return err
|
|
||||||
case context.ContentMsgPackHeaderValue, context.ContentMsgPack2HeaderValue:
|
|
||||||
_, err := ctx.MsgPack(v)
|
|
||||||
return err
|
|
||||||
default:
|
|
||||||
// otherwise default to JSON.
|
|
||||||
_, err := ctx.JSON(v)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.ContentType(contentType)
|
|
||||||
// .Write even len(content) == 0 , this should be called in order to call the internal tryWriteHeader,
|
// .Write even len(content) == 0 , this should be called in order to call the internal tryWriteHeader,
|
||||||
// it will not cost anything.
|
// it will not cost anything.
|
||||||
_, err := ctx.Write(content)
|
_, err := ctx.Write(content)
|
||||||
|
@ -420,7 +427,7 @@ func (r Response) Dispatch(ctx context.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err := dispatchCommon(ctx, r.Code, r.ContentType, r.Content, r.Object, true)
|
err := dispatchCommon(ctx, r.Code, r.ContentType, r.Content, r.Object, defaultResultHandler, true)
|
||||||
dispatchErr(ctx, r.Code, err)
|
dispatchErr(ctx, r.Code, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -79,6 +79,11 @@ func makeHandler(fn interface{}, c *Container, paramsCount int) context.Handler
|
||||||
|
|
||||||
bindings := getBindingsForFunc(v, c.Dependencies, paramsCount)
|
bindings := getBindingsForFunc(v, c.Dependencies, paramsCount)
|
||||||
|
|
||||||
|
resultHandler := defaultResultHandler
|
||||||
|
for i, lidx := 0, len(c.resultHandlers)-1; i <= lidx; i++ {
|
||||||
|
resultHandler = c.resultHandlers[lidx-i](resultHandler)
|
||||||
|
}
|
||||||
|
|
||||||
return func(ctx context.Context) {
|
return func(ctx context.Context) {
|
||||||
inputs := make([]reflect.Value, numIn)
|
inputs := make([]reflect.Value, numIn)
|
||||||
|
|
||||||
|
@ -102,7 +107,7 @@ func makeHandler(fn interface{}, c *Container, paramsCount int) context.Handler
|
||||||
}
|
}
|
||||||
|
|
||||||
outputs := v.Call(inputs)
|
outputs := v.Call(inputs)
|
||||||
if err := dispatchFuncResult(ctx, outputs); err != nil {
|
if err := dispatchFuncResult(ctx, outputs, resultHandler); err != nil {
|
||||||
c.GetErrorHandler(ctx).HandleError(ctx, err)
|
c.GetErrorHandler(ctx).HandleError(ctx, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user