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() {
|
||||
app := iris.New()
|
||||
app.ConfigureContainer(func(api *iris.APIContainer) {
|
||||
api.Post("/{id:int}", handler)
|
||||
})
|
||||
app.ConfigureContainer(configureAPI)
|
||||
app.Listen(":8080")
|
||||
}
|
||||
|
|
|
@ -2819,7 +2819,7 @@ func (ctx *context) ReadBody(ptr interface{}) error {
|
|||
}
|
||||
|
||||
switch ctx.GetContentTypeRequested() {
|
||||
case ContentXMLHeaderValue:
|
||||
case ContentXMLHeaderValue, ContentXMLUnreadableHeaderValue:
|
||||
return ctx.ReadXML(ptr)
|
||||
case ContentYAMLHeaderValue:
|
||||
return ctx.ReadYAML(ptr)
|
||||
|
|
|
@ -64,6 +64,14 @@ func (api *APIContainer) RegisterDependency(dependency interface{}) *hero.Depend
|
|||
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.
|
||||
func (api *APIContainer) convertHandlerFuncs(relativePath string, handlersFn ...interface{}) context.Handlers {
|
||||
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/core/host"
|
||||
"github.com/kataras/iris/v12/core/router"
|
||||
"github.com/kataras/iris/v12/hero"
|
||||
)
|
||||
|
||||
type (
|
||||
|
@ -88,6 +89,9 @@ type (
|
|||
//
|
||||
// A shortcut for the `core/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
|
||||
// `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.
|
||||
|
|
|
@ -33,6 +33,10 @@ type Container struct {
|
|||
// 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.
|
||||
|
||||
// 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.
|
||||
|
@ -103,6 +107,7 @@ func (c *Container) Clone() *Container {
|
|||
clonedDeps := make([]*Dependency, len(c.Dependencies))
|
||||
copy(clonedDeps, c.Dependencies)
|
||||
cloned.Dependencies = clonedDeps
|
||||
cloned.resultHandlers = c.resultHandlers
|
||||
return cloned
|
||||
}
|
||||
|
||||
|
@ -149,6 +154,14 @@ func (c *Container) Register(dependency interface{}) *Dependency {
|
|||
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
|
||||
// with the Container's `Dependencies` and any output result; like string, int (string,int),
|
||||
// custom structs, Result(View | Response) and anything you can imagine.
|
||||
|
|
|
@ -10,6 +10,46 @@ import (
|
|||
"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.
|
||||
// All types that complete this interface
|
||||
// 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...
|
||||
//
|
||||
// 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 {
|
||||
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
|
||||
// commonly used data to the response writer with a smart way.
|
||||
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
|
||||
// then skip everything and fire a not found,
|
||||
// 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 p, ok := v.(PreflightResult); ok {
|
||||
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
|
||||
}
|
||||
return handler(ctx, v)
|
||||
}
|
||||
|
||||
ctx.ContentType(contentType)
|
||||
// .Write even len(content) == 0 , this should be called in order to call the internal tryWriteHeader,
|
||||
// it will not cost anything.
|
||||
_, err := ctx.Write(content)
|
||||
|
@ -420,7 +427,7 @@ func (r Response) Dispatch(ctx context.Context) {
|
|||
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)
|
||||
}
|
||||
|
||||
|
|
|
@ -79,6 +79,11 @@ func makeHandler(fn interface{}, c *Container, paramsCount int) context.Handler
|
|||
|
||||
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) {
|
||||
inputs := make([]reflect.Value, numIn)
|
||||
|
||||
|
@ -102,7 +107,7 @@ func makeHandler(fn interface{}, c *Container, paramsCount int) context.Handler
|
|||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user