mirror of
https://github.com/kataras/iris.git
synced 2026-01-09 04:51:56 +00:00
implement a way to add controller functions as handlers with the existing rules respected but it's a bit dirty I will change the implementation and move the mvc2 to mvc and make the api builder's PartyFunc to be a critical part of the controller and the mvc2.Mvc bind values should be also respected to the controller and more
Former-commit-id: e452a916da80d886535b8ae9625d0ba8e2b58d6e
This commit is contained in:
@@ -1,22 +1,13 @@
|
||||
package activator
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// CallOnActivate simply calls the "controller"'s `OnActivate(*ActivatePayload)` function,
|
||||
// CallOnActivate simply calls the "controller"'s `OnActivate(*TController)` function,
|
||||
// if any.
|
||||
//
|
||||
// Look `activator.go#Register` and `ActivateListener` for more.
|
||||
func CallOnActivate(controller interface{},
|
||||
bindValues *[]interface{}, registerFunc RegisterFunc) {
|
||||
func CallOnActivate(controller interface{}, tController *TController) {
|
||||
|
||||
if ac, ok := controller.(ActivateListener); ok {
|
||||
p := &ActivatePayload{
|
||||
BindValues: bindValues,
|
||||
Handle: registerFunc,
|
||||
}
|
||||
ac.OnActivate(p)
|
||||
ac.OnActivate(tController)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,52 +18,13 @@ func CallOnActivate(controller interface{},
|
||||
// then the `OnActivate` function will be called ONCE, NOT in every request
|
||||
// but ONCE at the application's lifecycle.
|
||||
type ActivateListener interface {
|
||||
// OnActivate accepts a pointer to the `ActivatePayload`.
|
||||
// OnActivate accepts a pointer to the `TController`.
|
||||
//
|
||||
// The `Controller` can make use of the `OnActivate` function
|
||||
// to register custom routes
|
||||
// or modify the provided values that will be binded to the
|
||||
// controller later on.
|
||||
//
|
||||
// Look `ActivatePayload` for more.
|
||||
OnActivate(*ActivatePayload)
|
||||
}
|
||||
|
||||
// ActivatePayload contains the necessary information and the ability
|
||||
// to alt a controller's registration options, i.e the binder.
|
||||
//
|
||||
// With `ActivatePayload` the `Controller` can register custom routes
|
||||
// or modify the provided values that will be binded to the
|
||||
// controller later on.
|
||||
type ActivatePayload struct {
|
||||
BindValues *[]interface{}
|
||||
Handle RegisterFunc
|
||||
}
|
||||
|
||||
// EnsureBindValue will make sure that this "bindValue"
|
||||
// will be registered to the controller's binder
|
||||
// if its type is not already passed by the caller..
|
||||
//
|
||||
// For example, on `SessionController` it looks if *sessions.Sessions
|
||||
// has been binded from the caller and if not then the "bindValue"
|
||||
// will be binded and used as a default sessions manager instead.
|
||||
//
|
||||
// At general, if the caller has already provided a value with the same Type
|
||||
// then the "bindValue" will be ignored and not be added to the controller's bind values.
|
||||
//
|
||||
// Returns true if the caller has NOT already provided a value with the same Type
|
||||
// and "bindValue" is NOT ignored therefore is appended to the controller's bind values.
|
||||
func (i *ActivatePayload) EnsureBindValue(bindValue interface{}) bool {
|
||||
valueTyp := reflect.TypeOf(bindValue)
|
||||
localBindValues := *i.BindValues
|
||||
|
||||
for _, bindedValue := range localBindValues {
|
||||
// type already exists, remember: binding here is per-type.
|
||||
if reflect.TypeOf(bindedValue) == valueTyp {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
*i.BindValues = append(localBindValues, bindValue)
|
||||
return true
|
||||
// Look `TController` for more.
|
||||
OnActivate(*TController)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/kataras/iris/core/router/macro"
|
||||
"github.com/kataras/iris/mvc/activator/methodfunc"
|
||||
"github.com/kataras/iris/mvc/activator/model"
|
||||
"github.com/kataras/iris/mvc/activator/persistence"
|
||||
@@ -32,6 +33,12 @@ type (
|
||||
// we need this to collect and save the persistence fields' values.
|
||||
Value reflect.Value
|
||||
|
||||
valuePtr reflect.Value
|
||||
// // Methods and handlers, available after the Activate, can be seted `OnActivate` event as well.
|
||||
// Methods []methodfunc.MethodFunc
|
||||
|
||||
Router RegisterFunc
|
||||
|
||||
binder *binder // executed even before the BeginRequest if not nil.
|
||||
modelController *model.Controller
|
||||
persistenceController *persistence.Controller
|
||||
@@ -69,37 +76,33 @@ type BaseController interface {
|
||||
}
|
||||
|
||||
// ActivateController returns a new controller type info description.
|
||||
func ActivateController(base BaseController, bindValues []interface{}) (TController, error) {
|
||||
func newController(base BaseController, router RegisterFunc) (*TController, error) {
|
||||
// get and save the type.
|
||||
typ := reflect.TypeOf(base)
|
||||
if typ.Kind() != reflect.Ptr {
|
||||
typ = reflect.PtrTo(typ)
|
||||
}
|
||||
|
||||
valPointer := reflect.ValueOf(base) // or value raw
|
||||
|
||||
// first instance value, needed to validate
|
||||
// the actual type of the controller field
|
||||
// and to collect and save the instance's persistence fields'
|
||||
// values later on.
|
||||
val := reflect.Indirect(reflect.ValueOf(base))
|
||||
val := reflect.Indirect(valPointer)
|
||||
|
||||
ctrlName := val.Type().Name()
|
||||
pkgPath := val.Type().PkgPath()
|
||||
fullName := pkgPath[strings.LastIndexByte(pkgPath, '/')+1:] + "." + ctrlName
|
||||
|
||||
// set the binder, can be nil this check at made at runtime.
|
||||
binder := newBinder(typ.Elem(), bindValues)
|
||||
if binder != nil {
|
||||
for _, bf := range binder.fields {
|
||||
golog.Debugf("MVC %s: binder loaded for '%s' with value:\n%#v",
|
||||
fullName, bf.GetFullName(), bf.GetValue())
|
||||
}
|
||||
}
|
||||
|
||||
t := TController{
|
||||
t := &TController{
|
||||
Name: ctrlName,
|
||||
FullName: fullName,
|
||||
Type: typ,
|
||||
Value: val,
|
||||
binder: binder,
|
||||
valuePtr: valPointer,
|
||||
Router: router,
|
||||
binder: &binder{elemType: typ.Elem()},
|
||||
modelController: model.Load(typ),
|
||||
persistenceController: persistence.Load(typ, val),
|
||||
}
|
||||
@@ -107,12 +110,35 @@ func ActivateController(base BaseController, bindValues []interface{}) (TControl
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// BindValueTypeExists returns true if at least one type of "bindValue"
|
||||
// is already binded to this `TController`.
|
||||
func (t *TController) BindValueTypeExists(bindValue interface{}) bool {
|
||||
valueTyp := reflect.TypeOf(bindValue)
|
||||
for _, bindedValue := range t.binder.values {
|
||||
// type already exists, remember: binding here is per-type.
|
||||
if typ := reflect.TypeOf(bindedValue); typ == valueTyp ||
|
||||
(valueTyp.Kind() == reflect.Interface && typ.Implements(valueTyp)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// BindValue binds a value to a controller's field when request is served.
|
||||
func (t *TController) BindValue(bindValues ...interface{}) {
|
||||
for _, bindValue := range bindValues {
|
||||
t.binder.bind(bindValue)
|
||||
}
|
||||
}
|
||||
|
||||
// HandlerOf builds the handler for a type based on the specific method func.
|
||||
func (t TController) HandlerOf(methodFunc methodfunc.MethodFunc) context.Handler {
|
||||
func (t *TController) HandlerOf(methodFunc methodfunc.MethodFunc) context.Handler {
|
||||
var (
|
||||
// shared, per-controller
|
||||
elem = t.Type.Elem()
|
||||
ctrlName = t.Name
|
||||
elem = t.Type.Elem()
|
||||
ctrlName = t.Name
|
||||
hasBinder = !t.binder.isEmpty()
|
||||
|
||||
hasPersistenceData = t.persistenceController != nil
|
||||
hasModels = t.modelController != nil
|
||||
@@ -123,7 +149,7 @@ func (t TController) HandlerOf(methodFunc methodfunc.MethodFunc) context.Handler
|
||||
return func(ctx context.Context) {
|
||||
// create a new controller instance of that type(>ptr).
|
||||
c := reflect.New(elem)
|
||||
if t.binder != nil {
|
||||
if hasBinder {
|
||||
t.binder.handle(c)
|
||||
}
|
||||
|
||||
@@ -163,29 +189,38 @@ func (t TController) HandlerOf(methodFunc methodfunc.MethodFunc) context.Handler
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterFunc used by the caller to register the result routes.
|
||||
type RegisterFunc func(relPath string, httpMethod string, handler ...context.Handler)
|
||||
|
||||
// RegisterMethodHandlers receives a `TController`, description of the
|
||||
// user's controller, and calls the "registerFunc" for each of its
|
||||
// method handlers.
|
||||
//
|
||||
// Not useful for the end-developer, but may needed for debugging
|
||||
// at the future.
|
||||
func RegisterMethodHandlers(t TController, registerFunc RegisterFunc) {
|
||||
func (t *TController) registerMethodFunc(m methodfunc.MethodFunc) {
|
||||
var middleware context.Handlers
|
||||
|
||||
if t.binder != nil {
|
||||
if !t.binder.isEmpty() {
|
||||
if m := t.binder.middleware; len(m) > 0 {
|
||||
middleware = m
|
||||
}
|
||||
}
|
||||
|
||||
h := t.HandlerOf(m)
|
||||
if h == nil {
|
||||
golog.Warnf("MVC %s: nil method handler found for %s", t.FullName, m.Name)
|
||||
return
|
||||
}
|
||||
|
||||
registeredHandlers := append(middleware, h)
|
||||
t.Router(m.HTTPMethod, m.RelPath, registeredHandlers...)
|
||||
|
||||
golog.Debugf("MVC %s: %s %s maps to function[%d] '%s'", t.FullName,
|
||||
m.HTTPMethod,
|
||||
m.RelPath,
|
||||
m.Index,
|
||||
m.Name)
|
||||
}
|
||||
|
||||
func (t *TController) resolveAndRegisterMethods() {
|
||||
// the actual method functions
|
||||
// i.e for "GET" it's the `Get()`.
|
||||
methods, err := methodfunc.Resolve(t.Type)
|
||||
if err != nil {
|
||||
golog.Errorf("MVC %s: %s", t.FullName, err.Error())
|
||||
// don't stop here.
|
||||
return
|
||||
}
|
||||
// range over the type info's method funcs,
|
||||
// build a new handler for each of these
|
||||
@@ -194,35 +229,118 @@ func RegisterMethodHandlers(t TController, registerFunc RegisterFunc) {
|
||||
// responsible to convert these into routes
|
||||
// and add them to router via the APIBuilder.
|
||||
for _, m := range methods {
|
||||
h := t.HandlerOf(m)
|
||||
if h == nil {
|
||||
golog.Warnf("MVC %s: nil method handler found for %s", t.FullName, m.Name)
|
||||
continue
|
||||
}
|
||||
registeredHandlers := append(middleware, h)
|
||||
registerFunc(m.RelPath, m.HTTPMethod, registeredHandlers...)
|
||||
|
||||
golog.Debugf("MVC %s: %s %s maps to function[%d] '%s'", t.FullName,
|
||||
m.HTTPMethod,
|
||||
m.RelPath,
|
||||
m.Index,
|
||||
m.Name)
|
||||
t.registerMethodFunc(m)
|
||||
}
|
||||
}
|
||||
|
||||
// Handle registers a method func but with a custom http method and relative route's path,
|
||||
// it respects the rest of the controller's rules and guidelines.
|
||||
func (t *TController) Handle(httpMethod, path, handlerFuncName string) bool {
|
||||
cTyp := t.Type // with the pointer.
|
||||
m, exists := cTyp.MethodByName(handlerFuncName)
|
||||
if !exists {
|
||||
golog.Errorf("MVC: function '%s' doesn't exist inside the '%s' controller",
|
||||
handlerFuncName, t.FullName)
|
||||
return false
|
||||
}
|
||||
|
||||
info := methodfunc.FuncInfo{
|
||||
Name: m.Name,
|
||||
Trailing: m.Name,
|
||||
Type: m.Type,
|
||||
Index: m.Index,
|
||||
HTTPMethod: httpMethod,
|
||||
}
|
||||
|
||||
tmpl, err := macro.Parse(path, macro.NewMap())
|
||||
if err != nil {
|
||||
golog.Errorf("MVC: fail to parse the path for '%s.%s': %v", t.FullName, handlerFuncName, err)
|
||||
return false
|
||||
}
|
||||
|
||||
paramKeys := make([]string, len(tmpl.Params), len(tmpl.Params))
|
||||
for i, param := range tmpl.Params {
|
||||
paramKeys[i] = param.Name
|
||||
}
|
||||
|
||||
methodFunc, err := methodfunc.ResolveMethodFunc(info, paramKeys...)
|
||||
if err != nil {
|
||||
golog.Errorf("MVC: function '%s' inside the '%s' controller: %v", handlerFuncName, t.FullName, err)
|
||||
return false
|
||||
}
|
||||
|
||||
methodFunc.RelPath = path
|
||||
|
||||
t.registerMethodFunc(methodFunc)
|
||||
return true
|
||||
}
|
||||
|
||||
// func (t *TController) getMethodFuncByName(funcName string) (methodfunc.MethodFunc, bool) {
|
||||
// cVal := t.Value
|
||||
// cTyp := t.Type // with the pointer.
|
||||
// m, exists := cTyp.MethodByName(funcName)
|
||||
// if !exists {
|
||||
// golog.Errorf("MVC: function '%s' doesn't exist inside the '%s' controller",
|
||||
// funcName, cTyp.String())
|
||||
// return methodfunc.MethodFunc{}, false
|
||||
// }
|
||||
|
||||
// fn := cVal.MethodByName(funcName)
|
||||
// if !fn.IsValid() {
|
||||
// golog.Errorf("MVC: function '%s' inside the '%s' controller has not a valid value",
|
||||
// funcName, cTyp.String())
|
||||
// return methodfunc.MethodFunc{}, false
|
||||
// }
|
||||
|
||||
// info, ok := methodfunc.FetchFuncInfo(m)
|
||||
// if !ok {
|
||||
// golog.Errorf("MVC: could not resolve the func info from '%s'", funcName)
|
||||
// return methodfunc.MethodFunc{}, false
|
||||
// }
|
||||
|
||||
// methodFunc, err := methodfunc.ResolveMethodFunc(info)
|
||||
// if err != nil {
|
||||
// golog.Errorf("MVC: %v", err)
|
||||
// return methodfunc.MethodFunc{}, false
|
||||
// }
|
||||
|
||||
// return methodFunc, true
|
||||
// }
|
||||
|
||||
// // RegisterName registers a function by its name
|
||||
// func (t *TController) RegisterName(funcName string) bool {
|
||||
// methodFunc, ok := t.getMethodFuncByName(funcName)
|
||||
// if !ok {
|
||||
// return false
|
||||
// }
|
||||
// t.registerMethodFunc(methodFunc)
|
||||
// return true
|
||||
// }
|
||||
|
||||
// RegisterFunc used by the caller to register the result routes.
|
||||
type RegisterFunc func(httpMethod string, relPath string, handler ...context.Handler)
|
||||
|
||||
// Register receives a "controller",
|
||||
// a pointer of an instance which embeds the `Controller`,
|
||||
// the value of "baseControllerFieldName" should be `Controller`.
|
||||
func Register(controller BaseController, bindValues []interface{},
|
||||
registerFunc RegisterFunc) error {
|
||||
|
||||
CallOnActivate(controller, &bindValues, registerFunc)
|
||||
|
||||
t, err := ActivateController(controller, bindValues)
|
||||
t, err := newController(controller, registerFunc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
RegisterMethodHandlers(t, registerFunc)
|
||||
t.BindValue(bindValues...)
|
||||
|
||||
CallOnActivate(controller, t)
|
||||
|
||||
for _, bf := range t.binder.fields {
|
||||
golog.Debugf("MVC %s: binder loaded for '%s' with value:\n%#v",
|
||||
t.FullName, bf.GetFullName(), bf.GetValue())
|
||||
}
|
||||
|
||||
t.resolveAndRegisterMethods()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -8,7 +8,15 @@ import (
|
||||
"github.com/kataras/iris/context"
|
||||
)
|
||||
|
||||
// binder accepts a value of something
|
||||
// and tries to find its equalivent type
|
||||
// inside the controller and sets that to it,
|
||||
// after that each new instance of the controller will have
|
||||
// this value on the specific field, like persistence data control does.
|
||||
|
||||
type binder struct {
|
||||
elemType reflect.Type
|
||||
// values and fields are matched on the `match`.
|
||||
values []interface{}
|
||||
fields []field.Field
|
||||
|
||||
@@ -17,28 +25,24 @@ type binder struct {
|
||||
middleware context.Handlers
|
||||
}
|
||||
|
||||
// binder accepts a value of something
|
||||
// and tries to find its equalivent type
|
||||
// inside the controller and sets that to it,
|
||||
// after that each new instance of the controller will have
|
||||
// this value on the specific field, like persistence data control does.
|
||||
//
|
||||
// returns a nil binder if values are not valid bindable data to the controller type.
|
||||
func newBinder(elemType reflect.Type, values []interface{}) *binder {
|
||||
if len(values) == 0 {
|
||||
return nil
|
||||
func (b *binder) bind(value interface{}) {
|
||||
if value == nil {
|
||||
return
|
||||
}
|
||||
|
||||
b := &binder{values: values}
|
||||
b.fields = b.lookup(elemType)
|
||||
b.values = append(b.values, value) // keep values.
|
||||
|
||||
b.match(value)
|
||||
}
|
||||
|
||||
func (b *binder) isEmpty() bool {
|
||||
// if nothing valid found return nil, so the caller
|
||||
// can omit the binder.
|
||||
if len(b.fields) == 0 && len(b.middleware) == 0 {
|
||||
return nil
|
||||
return true
|
||||
}
|
||||
|
||||
return b
|
||||
return false
|
||||
}
|
||||
|
||||
func (b *binder) storeValueIfMiddleware(value reflect.Value) bool {
|
||||
@@ -55,41 +59,38 @@ func (b *binder) storeValueIfMiddleware(value reflect.Value) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (b *binder) lookup(elem reflect.Type) (fields []field.Field) {
|
||||
for _, v := range b.values {
|
||||
value := reflect.ValueOf(v)
|
||||
// handlers will be recognised as middleware, not struct fields.
|
||||
// End-Developer has the option to call any handler inside
|
||||
// the controller's `BeginRequest` and `EndRequest`, the
|
||||
// state is respected from the method handler already.
|
||||
if b.storeValueIfMiddleware(value) {
|
||||
// stored as middleware, continue to the next field, we don't have
|
||||
// to bind anything here.
|
||||
continue
|
||||
}
|
||||
|
||||
matcher := func(elemField reflect.StructField) bool {
|
||||
// If the controller's field is interface then check
|
||||
// if the given binded value implements that interface.
|
||||
// i.e MovieController { Service services.MovieService /* interface */ }
|
||||
// app.Controller("/", new(MovieController),
|
||||
// services.NewMovieMemoryService(...))
|
||||
//
|
||||
// `services.NewMovieMemoryService` returns a `*MovieMemoryService`
|
||||
// that implements the `MovieService` interface.
|
||||
if elemField.Type.Kind() == reflect.Interface {
|
||||
return value.Type().Implements(elemField.Type)
|
||||
}
|
||||
return elemField.Type == value.Type()
|
||||
}
|
||||
|
||||
handler := func(f *field.Field) {
|
||||
f.Value = value
|
||||
}
|
||||
|
||||
fields = append(fields, field.LookupFields(elem, matcher, handler)...)
|
||||
func (b *binder) match(v interface{}) {
|
||||
value := reflect.ValueOf(v)
|
||||
// handlers will be recognised as middleware, not struct fields.
|
||||
// End-Developer has the option to call any handler inside
|
||||
// the controller's `BeginRequest` and `EndRequest`, the
|
||||
// state is respected from the method handler already.
|
||||
if b.storeValueIfMiddleware(value) {
|
||||
// stored as middleware, continue to the next field, we don't have
|
||||
// to bind anything here.
|
||||
return
|
||||
}
|
||||
return
|
||||
|
||||
matcher := func(elemField reflect.StructField) bool {
|
||||
// If the controller's field is interface then check
|
||||
// if the given binded value implements that interface.
|
||||
// i.e MovieController { Service services.MovieService /* interface */ }
|
||||
// app.Controller("/", new(MovieController),
|
||||
// services.NewMovieMemoryService(...))
|
||||
//
|
||||
// `services.NewMovieMemoryService` returns a `*MovieMemoryService`
|
||||
// that implements the `MovieService` interface.
|
||||
if elemField.Type.Kind() == reflect.Interface {
|
||||
return value.Type().Implements(elemField.Type)
|
||||
}
|
||||
return elemField.Type == value.Type()
|
||||
}
|
||||
|
||||
handler := func(f *field.Field) {
|
||||
f.Value = value
|
||||
}
|
||||
|
||||
b.fields = append(b.fields, field.LookupFields(b.elemType, matcher, handler)...)
|
||||
}
|
||||
|
||||
func (b *binder) handle(c reflect.Value) {
|
||||
|
||||
@@ -53,38 +53,49 @@ func fetchInfos(typ reflect.Type) (methods []FuncInfo) {
|
||||
// and add that.
|
||||
for i, n := 0, typ.NumMethod(); i < n; i++ {
|
||||
m := typ.Method(i)
|
||||
name := m.Name
|
||||
|
||||
for _, method := range availableMethods {
|
||||
possibleMethodFuncName := methodTitle(method)
|
||||
|
||||
if strings.Index(name, possibleMethodFuncName) == 0 {
|
||||
trailing := ""
|
||||
// if has chars after the method itself
|
||||
if lname, lmethod := len(name), len(possibleMethodFuncName); lname > lmethod {
|
||||
ch := rune(name[lmethod])
|
||||
// if the next char is upper, otherise just skip the whole func info.
|
||||
if unicode.IsUpper(ch) {
|
||||
trailing = name[lmethod:]
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
methodInfo := FuncInfo{
|
||||
Name: name,
|
||||
Trailing: trailing,
|
||||
Type: m.Type,
|
||||
HTTPMethod: method,
|
||||
Index: m.Index,
|
||||
}
|
||||
methods = append(methods, methodInfo)
|
||||
}
|
||||
if method, ok := FetchFuncInfo(m); ok {
|
||||
methods = append(methods, method)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// FetchFuncInfo returns a FuncInfo based on the method of the controller.
|
||||
func FetchFuncInfo(m reflect.Method) (FuncInfo, bool) {
|
||||
name := m.Name
|
||||
|
||||
for _, method := range availableMethods {
|
||||
possibleMethodFuncName := methodTitle(method)
|
||||
|
||||
if strings.Index(name, possibleMethodFuncName) == 0 {
|
||||
trailing := ""
|
||||
// if has chars after the method itself
|
||||
if lname, lmethod := len(name), len(possibleMethodFuncName); lname > lmethod {
|
||||
ch := rune(name[lmethod])
|
||||
// if the next char is upper, otherise just skip the whole func info.
|
||||
if unicode.IsUpper(ch) {
|
||||
trailing = name[lmethod:]
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
info := FuncInfo{
|
||||
Name: name,
|
||||
Trailing: trailing,
|
||||
Type: m.Type,
|
||||
HTTPMethod: method,
|
||||
Index: m.Index,
|
||||
}
|
||||
return info, true
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return FuncInfo{}, false
|
||||
}
|
||||
|
||||
func methodTitle(httpMethod string) string {
|
||||
httpMethodFuncName := strings.Title(strings.ToLower(httpMethod))
|
||||
return httpMethodFuncName
|
||||
|
||||
@@ -182,6 +182,7 @@ func (a *ast) paramValues(ctx context.Context) []reflect.Value {
|
||||
|
||||
l := len(a.paramKeys)
|
||||
values := make([]reflect.Value, l, l)
|
||||
|
||||
for i := 0; i < l; i++ {
|
||||
paramKey := a.paramKeys[i]
|
||||
paramType := a.paramTypes[i]
|
||||
|
||||
@@ -31,20 +31,38 @@ func Resolve(typ reflect.Type) ([]MethodFunc, error) {
|
||||
var methodFuncs []MethodFunc
|
||||
infos := fetchInfos(typ)
|
||||
for _, info := range infos {
|
||||
parser := newFuncParser(info)
|
||||
a, err := parser.parse()
|
||||
methodFunc, err := ResolveMethodFunc(info)
|
||||
if r.AddErr(err) {
|
||||
continue
|
||||
}
|
||||
|
||||
methodFunc := MethodFunc{
|
||||
RelPath: a.relPath,
|
||||
FuncInfo: info,
|
||||
MethodCall: buildMethodCall(a),
|
||||
}
|
||||
|
||||
methodFuncs = append(methodFuncs, methodFunc)
|
||||
}
|
||||
|
||||
return methodFuncs, r.Return()
|
||||
}
|
||||
|
||||
// ResolveMethodFunc resolves a single `MethodFunc` from a single `FuncInfo`.
|
||||
func ResolveMethodFunc(info FuncInfo, paramKeys ...string) (MethodFunc, error) {
|
||||
parser := newFuncParser(info)
|
||||
a, err := parser.parse()
|
||||
if err != nil {
|
||||
return MethodFunc{}, err
|
||||
}
|
||||
|
||||
if len(paramKeys) > 0 {
|
||||
a.paramKeys = paramKeys
|
||||
}
|
||||
|
||||
methodFunc := MethodFunc{
|
||||
RelPath: a.relPath,
|
||||
FuncInfo: info,
|
||||
MethodCall: buildMethodCall(a),
|
||||
}
|
||||
|
||||
/* TODO: split the method path and ast param keys, and all that
|
||||
because now we want to use custom param keys but 'paramfirst' is set-ed.
|
||||
|
||||
*/
|
||||
|
||||
return methodFunc, nil
|
||||
}
|
||||
|
||||
@@ -306,6 +306,7 @@ func (t *testControllerBindDeep) Get() {
|
||||
}
|
||||
func TestControllerBind(t *testing.T) {
|
||||
app := iris.New()
|
||||
// app.Logger().SetLevel("debug")
|
||||
|
||||
t1, t2 := "my pointer title", "val title"
|
||||
// test bind pointer to pointer of the correct type
|
||||
@@ -505,8 +506,8 @@ type testControllerActivateListener struct {
|
||||
TitlePointer *testBindType
|
||||
}
|
||||
|
||||
func (c *testControllerActivateListener) OnActivate(p *activator.ActivatePayload) {
|
||||
p.EnsureBindValue(&testBindType{
|
||||
func (c *testControllerActivateListener) OnActivate(t *activator.TController) {
|
||||
t.BindValue(&testBindType{
|
||||
title: "default title",
|
||||
})
|
||||
}
|
||||
|
||||
11
mvc/go19.go
11
mvc/go19.go
@@ -13,15 +13,14 @@ type (
|
||||
// in order to be marked as safe content, to be rendered as html and not escaped.
|
||||
HTML = template.HTML
|
||||
|
||||
// ActivatePayload contains the necessary information and the ability
|
||||
// to alt a controller's registration options, i.e the binder.
|
||||
// TController contains the necessary controller's pre-serve information.
|
||||
//
|
||||
// With `ActivatePayload` the `Controller` can register custom routes
|
||||
// With `TController` the `Controller` can register custom routes
|
||||
// or modify the provided values that will be binded to the
|
||||
// controller later on.
|
||||
//
|
||||
// Look the `mvc/activator#ActivatePayload` for its implementation.
|
||||
// Look the `mvc/activator#TController` for its implementation.
|
||||
//
|
||||
// A shortcut for the `mvc/activator#ActivatePayload`, useful when `OnActivate` is being used.
|
||||
ActivatePayload = activator.ActivatePayload
|
||||
// A shortcut for the `mvc/activator#TController`, useful when `OnActivate` is being used.
|
||||
TController = activator.TController
|
||||
)
|
||||
|
||||
@@ -24,8 +24,9 @@ type SessionController struct {
|
||||
// every single time the dev registers a specific SessionController-based controller.
|
||||
// It makes sure that its "Manager" field is filled
|
||||
// even if the caller didn't provide any sessions manager via the `app.Controller` function.
|
||||
func (s *SessionController) OnActivate(p *activator.ActivatePayload) {
|
||||
if p.EnsureBindValue(defaultManager) {
|
||||
func (s *SessionController) OnActivate(t *activator.TController) {
|
||||
if !t.BindValueTypeExists(defaultManager) {
|
||||
t.BindValue(defaultManager)
|
||||
golog.Warnf(`MVC SessionController: couldn't find any "*sessions.Sessions" bindable value to fill the "Manager" field,
|
||||
therefore this controller is using the default sessions manager instead.
|
||||
Please refer to the documentation to learn how you can provide the session manager`)
|
||||
|
||||
Reference in New Issue
Block a user