1
0
mirror of https://github.com/kataras/iris.git synced 2026-01-21 10:56:01 +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:
kataras
2017-11-27 21:39:57 +02:00
parent 9d63e3194f
commit dd5de52f34
16 changed files with 496 additions and 206 deletions

96
mvc2/controller.go Normal file
View File

@@ -0,0 +1,96 @@
package mvc2
import (
// "reflect"
// "github.com/kataras/golog"
// "github.com/kataras/iris/context"
// // "github.com/kataras/iris/core/router"
// "github.com/kataras/iris/mvc/activator"
// "github.com/kataras/iris/mvc/activator/methodfunc"
)
// no, we will not make any changes to the controller's implementation
// let's no re-write the godlike code I wrote two months ago
// , just improve it by implementing the only one missing feature:
// bind/map/handle custom controller's functions to a custom router path
// like regexed.
//
// // BaseController is the interface that all controllers should implement.
// type BaseController interface {
// BeginRequest(ctx context.Context)
// EndRequest(ctx context.Context)
// }
// // type ControllerInitializer interface {
// // Init(r router.Party)
// // }
// // type activator struct {
// // Router router.Party
// // container *Mvc
// // }
// func registerController(m *Mvc, r router.Party, c BaseController) {
// }
// // ControllerHandler is responsible to dynamically bind a controller's functions
// // to the controller's http mechanism, can be used on the controller's `OnActivate` event.
// func ControllerHandler(controller activator.BaseController, funcName string) context.Handler {
// // we use funcName instead of an interface{} which can be safely binded with something like:
// // myController.HandleThis because we want to make sure that the end-developer
// // will make use a function of that controller that owns it because if not then
// // the BeginRequest and EndRequest will be called from other handler and also
// // the first input argument, which should be the controller itself may not be binded
// // to the current controller, all that are solved if end-dev knows what to do
// // but we can't bet on it.
// cVal := reflect.ValueOf(controller)
// elemTyp := reflect.TypeOf(controller) // with the pointer.
// m, exists := elemTyp.MethodByName(funcName)
// if !exists {
// golog.Errorf("mvc controller handler: function '%s' doesn't exist inside the '%s' controller",
// funcName, elemTyp.String())
// return nil
// }
// fn := cVal.MethodByName(funcName)
// if !fn.IsValid() {
// golog.Errorf("mvc controller handler: function '%s' inside the '%s' controller has not a valid value",
// funcName, elemTyp.String())
// return nil
// }
// info, ok := methodfunc.FetchFuncInfo(m)
// if !ok {
// golog.Errorf("mvc controller handler: could not resolve the func info from '%s'", funcName)
// return nil
// }
// methodFunc, err := methodfunc.ResolveMethodFunc(info)
// if err != nil {
// golog.Errorf("mvc controller handler: %v", err)
// return nil
// }
// m := New()
// m.In(controller) // bind the controller itself?
// /// TODO: first we must enable interface{} to be used as 'servetime input binder'
// // because it will try to match the type and add to its input if the
// // func input is that, and this binder will be available to every handler after that,
// // so it will be included to its 'in'.
// // MakeFuncInputBinder(func(ctx context.Context) interface{} {
// // // job here.
// // return controller
// // })
// h := m.Handler(fn.Interface())
// return func(ctx context.Context) {
// controller.BeginRequest(ctx)
// h(ctx)
// controller.EndRequest(ctx)
// }
// }

View File

@@ -0,0 +1,81 @@
package mvc2_test
import (
"testing"
"github.com/kataras/iris"
"github.com/kataras/iris/httptest"
"github.com/kataras/iris/mvc"
// "github.com/kataras/iris/mvc/activator/methodfunc"
//. "github.com/kataras/iris/mvc2"
)
type testController struct {
mvc.C
Service *TestServiceImpl
reqField string
}
func (c *testController) Get() string {
return "index"
}
func (c *testController) BeginRequest(ctx iris.Context) {
c.C.BeginRequest(ctx)
c.reqField = ctx.URLParam("reqfield")
}
func (c *testController) OnActivate(t *mvc.TController) {
t.Handle("GET", "/histatic", "HiStatic")
t.Handle("GET", "/hiservice", "HiService")
t.Handle("GET", "/hiparam/{ps:string}", "HiParamBy")
t.Handle("GET", "/hiparamempyinput/{ps:string}", "HiParamEmptyInputBy")
}
func (c *testController) HiStatic() string {
return c.reqField
}
func (c *testController) HiService() string {
return c.Service.Say("hi")
}
func (c *testController) HiParamBy(v string) string {
return v
}
func (c *testController) HiParamEmptyInputBy() string {
return "empty in but served with ctx.Params.Get('ps')=" + c.Ctx.Params().Get("ps")
}
func TestControllerHandler(t *testing.T) {
app := iris.New()
app.Controller("/", new(testController), &TestServiceImpl{prefix: "service:"})
e := httptest.New(t, app, httptest.LogLevel("debug"))
// test the index, is not part of the current package's implementation but do it.
e.GET("/").Expect().Status(httptest.StatusOK).Body().Equal("index")
// the important things now.
// this test ensures that the BeginRequest of the controller will be
// called correctly and also the controller is binded to the first input argument
// (which is the function's receiver, if any, in this case the *testController in go).
expectedReqField := "this is a request field filled by this url param"
e.GET("/histatic").WithQuery("reqfield", expectedReqField).Expect().Status(httptest.StatusOK).
Body().Equal(expectedReqField)
// this test makes sure that the binded values of the controller is handled correctly
// and can be used in a user-defined, dynamic "mvc handler".
e.GET("/hiservice").Expect().Status(httptest.StatusOK).
Body().Equal("service: hi")
// this worked with a temporary variadic on the resolvemethodfunc which is not
// correct design, I should split the path and params with the rest of implementation
// in order a simple template.Src can be given.
e.GET("/hiparam/value").Expect().Status(httptest.StatusOK).
Body().Equal("value")
e.GET("/hiparamempyinput/value").Expect().Status(httptest.StatusOK).
Body().Equal("empty in but served with ctx.Params.Get('ps')=value")
}

View File

@@ -28,22 +28,25 @@ func testBinderFunc(ctx iris.Context) testUserStruct {
// service
type (
testService interface {
// these TestService and TestServiceImpl could be in lowercase, unexported
// but the `Say` method should be exported however we have those exported
// because of the controller handler test.
TestService interface {
Say(string) string
}
testServiceImpl struct {
TestServiceImpl struct {
prefix string
}
)
func (s *testServiceImpl) Say(message string) string {
func (s *TestServiceImpl) Say(message string) string {
return s.prefix + " " + message
}
var (
// binders, as user-defined
testBinderFuncUserStruct = testBinderFunc
testBinderService = &testServiceImpl{prefix: "say"}
testBinderService = &TestServiceImpl{prefix: "say"}
testBinderFuncParam = func(ctx iris.Context) string {
return ctx.Params().Get("param")
}
@@ -56,7 +59,7 @@ var (
}
// just one input arg, the service which is binded by the #2 service binder.
testConsumeServiceHandler = func(service testService) string {
testConsumeServiceHandler = func(service TestService) string {
return service.Say("something")
}
// just one input arg, a standar string which is binded by the #3 func(ctx) any binder.

View File

@@ -35,7 +35,7 @@ func (m *Mvc) Child() *Mvc {
return child
}
func (m *Mvc) In(binders ...interface{}) {
func (m *Mvc) In(binders ...interface{}) *Mvc {
for _, binder := range binders {
typ := resolveBinderType(binder)
@@ -58,6 +58,8 @@ func (m *Mvc) In(binders ...interface{}) {
m.binders = append(m.binders, b)
}
return m
}
func (m *Mvc) Handler(handler interface{}) context.Handler {

View File

@@ -9,8 +9,7 @@ import (
)
func TestMvcInAndHandler(t *testing.T) {
m := New()
m.In(testBinderFuncUserStruct, testBinderService, testBinderFuncParam)
m := New().In(testBinderFuncUserStruct, testBinderService, testBinderFuncParam)
var (
h1 = m.Handler(testConsumeUserHandler)