1
0
mirror of https://github.com/kataras/iris.git synced 2025-12-29 15:57:09 +00:00

fix #2158 and more

This commit is contained in:
Gerasimos (Makis) Maropoulos
2023-07-08 02:08:18 +03:00
parent 757e7fe61b
commit 6add1ba49b
79 changed files with 551 additions and 467 deletions

View File

@@ -253,7 +253,7 @@ func isPayloadType(in reflect.Type) bool {
func getBindingsForFunc(fn reflect.Value, dependencies []*Dependency, disablePayloadAutoBinding bool, paramsCount int) []*binding {
fnTyp := fn.Type()
if !isFunc(fnTyp) {
panic("bindings: unresolved: not a func type")
panic(fmt.Sprintf("bindings: unresolved: no a func type: %#+v", fn))
}
n := fnTyp.NumIn()
@@ -294,7 +294,7 @@ func getBindingsForFunc(fn reflect.Value, dependencies []*Dependency, disablePay
func getBindingsForStruct(v reflect.Value, dependencies []*Dependency, markExportedFieldsAsRequired bool, disablePayloadAutoBinding bool, matchDependency DependencyMatcher, paramsCount int, sorter Sorter) (bindings []*binding) {
typ := indirectType(v.Type())
if typ.Kind() != reflect.Struct {
panic("bindings: unresolved: no struct type")
panic(fmt.Sprintf("bindings: unresolved: not a struct type: %#+v", v))
}
// get bindings from any struct's non zero values first, including unexported.

View File

@@ -55,7 +55,7 @@ func TestContainerHandler(t *testing.T) {
e := httptest.New(t, app)
path := fmt.Sprintf("/%d", expectedOutput.ID)
e.POST(path).WithJSON(input).Expect().Status(httptest.StatusOK).JSON().Equal(expectedOutput)
e.POST(path).WithJSON(input).Expect().Status(httptest.StatusOK).JSON().IsEqual(expectedOutput)
}
func TestContainerInject(t *testing.T) {
@@ -127,5 +127,5 @@ func TestContainerUseResultHandler(t *testing.T) {
app.Get("/{id:int}", handler)
e := httptest.New(t, app)
e.GET("/42").Expect().Status(httptest.StatusOK).JSON().Equal(expectedResponse)
e.GET("/42").Expect().Status(httptest.StatusOK).JSON().IsEqual(expectedResponse)
}

View File

@@ -2,6 +2,7 @@ package hero
import (
"fmt"
"strings"
"reflect"
@@ -71,7 +72,7 @@ func newDependency(dependency interface{}, disablePayloadAutoBinding bool, match
}
if d, ok := dependency.(*Dependency); ok {
// already a *Dependency.
// already a *Dependency, do not continue (and most importatly do not call resolveDependency) .
return d
}
@@ -100,11 +101,12 @@ func newDependency(dependency interface{}, disablePayloadAutoBinding bool, match
// DependencyResolver func(v reflect.Value, dest *Dependency) bool
// Resolver DependencyResolver
func resolveDependency(v reflect.Value, disablePayloadAutoBinding bool, dest *Dependency, funcDependencies ...*Dependency) bool {
func resolveDependency(v reflect.Value, disablePayloadAutoBinding bool, dest *Dependency, prevDependencies ...*Dependency) bool {
return fromDependencyHandler(v, dest) ||
fromStructValue(v, dest) ||
fromBuiltinValue(v, dest) ||
fromStructValueOrDependentStructValue(v, disablePayloadAutoBinding, dest, prevDependencies) ||
fromFunc(v, dest) ||
len(funcDependencies) > 0 && fromDependentFunc(v, disablePayloadAutoBinding, dest, funcDependencies)
len(prevDependencies) > 0 && fromDependentFunc(v, disablePayloadAutoBinding, dest, prevDependencies)
}
func fromDependencyHandler(_ reflect.Value, dest *Dependency) bool {
@@ -131,20 +133,114 @@ func fromDependencyHandler(_ reflect.Value, dest *Dependency) bool {
return true
}
func fromStructValue(v reflect.Value, dest *Dependency) bool {
if !isFunc(v) {
// It's just a static value.
handler := func(*context.Context, *Input) (reflect.Value, error) {
return v, nil
}
dest.DestType = v.Type()
dest.Static = true
dest.Handle = handler
return true
func fromBuiltinValue(v reflect.Value, dest *Dependency) bool {
if !isBuiltinValue(v) {
return false
}
return false
// It's just a static builtin value.
handler := func(*context.Context, *Input) (reflect.Value, error) {
return v, nil
}
dest.DestType = v.Type()
dest.Static = true
dest.Handle = handler
return true
}
func fromStructValue(v reflect.Value, dest *Dependency) bool {
if !isStructValue(v) {
return false
}
// It's just a static struct value.
handler := func(*context.Context, *Input) (reflect.Value, error) {
return v, nil
}
dest.DestType = v.Type()
dest.Static = true
dest.Handle = handler
return true
}
func fromStructValueOrDependentStructValue(v reflect.Value, disablePayloadAutoBinding bool, dest *Dependency, prevDependencies []*Dependency) bool {
if !isStructValue(v) {
// It's not just a static struct value.
return false
}
if len(prevDependencies) == 0 { // As a non depedent struct.
// We must make this check so we can avoid the auto-filling of
// the dependencies from Iris builtin dependencies.
return fromStructValue(v, dest)
}
// Check if it's a builtin dependency (e.g an MVC Application (see mvc.go#newApp)),
// if it's and registered without a Dependency wrapper, like the rest builtin dependencies,
// then do NOT try to resolve its fields.
if strings.HasPrefix(indirectType(v.Type()).PkgPath(), "github.com/kataras/iris/v12") {
return fromStructValue(v, dest)
}
bindings := getBindingsForStruct(v, prevDependencies, false, disablePayloadAutoBinding, DefaultDependencyMatcher, -1, nil)
if len(bindings) == 0 {
return fromStructValue(v, dest) // same as above.
}
// As a depedent struct, however we may need to resolve its dependencies first
// so we can decide if it's really a depedent struct or not.
var (
handler = func(*context.Context, *Input) (reflect.Value, error) {
return v, nil
}
isStatic = true
)
for _, binding := range bindings {
if !binding.Dependency.Static {
isStatic = false
break
}
}
handler = func(ctx *context.Context, _ *Input) (reflect.Value, error) { // Called once per dependency on build-time if the dependency is static.
elem := v
if elem.Kind() == reflect.Ptr {
elem = elem.Elem()
}
for _, binding := range bindings {
field := elem.FieldByIndex(binding.Input.StructFieldIndex)
if !field.CanSet() || !field.IsZero() {
continue // already set.
}
// if !binding.Dependency.Match(field.Type()) { A check already happen in getBindingsForStruct.
// continue
// }
input, err := binding.Dependency.Handle(ctx, binding.Input)
if err != nil {
if err == ErrSeeOther {
continue
}
return emptyValue, err
}
// fmt.Printf("binding %s to %#+v\n", field.String(), input)
field.Set(input)
}
return v, nil
}
dest.DestType = v.Type()
dest.Static = isStatic
dest.Handle = handler
return true
}
func fromFunc(v reflect.Value, dest *Dependency) bool {

View File

@@ -35,7 +35,7 @@ func newSource(fn reflect.Value) Source {
fallthrough
default:
if callerFileName == "" {
callerFileName, callerLineNumber = getCaller()
callerFileName, callerLineNumber = GetCaller()
}
}
@@ -55,7 +55,7 @@ func newSource(fn reflect.Value) Source {
}
func getSource() Source {
filename, line := getCaller()
filename, line := GetCaller()
return Source{
File: filename,
Line: line,
@@ -67,7 +67,7 @@ func (s Source) String() string {
}
// https://golang.org/doc/go1.9#callersframes
func getCaller() (string, int) {
func GetCaller() (string, int) {
var pcs [32]uintptr
n := runtime.Callers(4, pcs[:])
frames := runtime.CallersFrames(pcs[:n])
@@ -76,11 +76,13 @@ func getCaller() (string, int) {
frame, more := frames.Next()
file := frame.File
if strings.HasSuffix(file, "_test.go") {
return file, frame.Line
if strings.Contains(file, "go/src/runtime/") {
continue
}
if !strings.Contains(file, "/kataras/iris") || strings.Contains(file, "/kataras/iris/_examples") || strings.Contains(file, "iris-contrib/examples") {
// funcName is something like "github.com/kataras/iris.SomeFunc"
funcName := frame.Function
if !strings.HasPrefix(funcName, "github.com/kataras/iris/v12") {
return file, frame.Line
}

View File

@@ -146,65 +146,65 @@ func TestFuncResult(t *testing.T) {
e := httptest.New(t, app)
e.GET("/text").Expect().Status(iris.StatusOK).
Body().Equal("text")
Body().IsEqual("text")
e.GET("/status").Expect().Status(iris.StatusBadGateway)
e.GET("/text/with/status/ok").Expect().Status(iris.StatusOK).
Body().Equal("OK")
Body().IsEqual("OK")
e.GET("/status/with/text/not/ok/first/second").Expect().Status(iris.StatusForbidden).
Body().Equal("NOT_OK_firstsecond")
Body().IsEqual("NOT_OK_firstsecond")
// Author's note: <-- if that fails means that the last binder called for both input args,
// see path_param_binder.go
e.GET("/text/and/content/type").Expect().Status(iris.StatusOK).
ContentType("text/html", "utf-8").
Body().Equal("<b>text</b>")
Body().IsEqual("<b>text</b>")
e.GET("/custom/response").Expect().Status(iris.StatusOK).
ContentType("text/html", "utf-8").
Body().Equal("<b>text</b>")
Body().IsEqual("<b>text</b>")
e.GET("/custom/response/with/status/ok").Expect().Status(iris.StatusOK).
ContentType("text/html", "utf-8").
Body().Equal("<b>OK</b>")
Body().IsEqual("<b>OK</b>")
e.GET("/custom/response/with/status/not/ok").Expect().Status(iris.StatusInternalServerError).
ContentType("text/html", "utf-8").
Body().Equal("<b>internal server error</b>")
Body().IsEqual("<b>internal server error</b>")
expectedResultFromCustomStruct := map[string]interface{}{
"name": "Iris",
"age": 2,
}
e.GET("/custom/struct").Expect().Status(iris.StatusOK).
JSON().Equal(expectedResultFromCustomStruct)
JSON().IsEqual(expectedResultFromCustomStruct)
e.GET("/custom/struct/with/status/not/ok").Expect().Status(iris.StatusInternalServerError).
JSON().Equal(expectedResultFromCustomStruct)
JSON().IsEqual(expectedResultFromCustomStruct)
e.GET("/custom/struct/with/content/type").Expect().Status(iris.StatusOK).
ContentType("text/xml", "utf-8")
e.GET("/custom/struct/with/error").Expect().Status(iris.StatusOK).
JSON().Equal(expectedResultFromCustomStruct)
JSON().IsEqual(expectedResultFromCustomStruct)
e.GET("/custom/struct/with/error").WithQuery("err", true).Expect().
Status(iris.StatusBadRequest). // the default status code if error is not nil
// the content should be not JSON it should be the status code's text
// it will fire the error's text
Body().Equal("omit return of testCustomStruct and fire error")
Body().IsEqual("omit return of testCustomStruct and fire error")
e.GET("/custom/error/as/dispatcher").Expect().
Status(iris.StatusBadRequest). // the default status code if error is not nil
// the content should be not JSON it should be the status code's text
// it will fire the error's text
JSON().Equal(err{iris.StatusBadRequest, "this is my error as json"})
JSON().IsEqual(err{iris.StatusBadRequest, "this is my error as json"})
// its result is nil should give an empty response but content-type is set correctly.
e.GET("/custom/nil/typed").Expect().
Status(iris.StatusOK).ContentType(context.ContentJSONHeaderValue).Body().Empty()
Status(iris.StatusOK).ContentType(context.ContentJSONHeaderValue).Body().IsEmpty()
e.GET("/custom/nil/typed/ptr").Expect().
Status(iris.StatusOK).ContentType(context.ContentJSONHeaderValue).Body().Empty()
Status(iris.StatusOK).ContentType(context.ContentJSONHeaderValue).Body().IsEmpty()
e.GET("/custom/nil/map").Expect().
Status(iris.StatusOK).ContentType(context.ContentJSONHeaderValue).Body().Empty()
Status(iris.StatusOK).ContentType(context.ContentJSONHeaderValue).Body().IsEmpty()
e.GET("/custom/nil/struct").Expect().
Status(iris.StatusOK).ContentType(context.ContentJSONHeaderValue).Body().Empty()
Status(iris.StatusOK).ContentType(context.ContentJSONHeaderValue).Body().IsEmpty()
}
type (
@@ -246,20 +246,20 @@ func TestPreflightResult(t *testing.T) {
expected1 := testPreflightResponse{Code: httptest.StatusOK, Message: "OK"}
e.POST("/").WithJSON(testPreflightRequest{FailCode: expected1.Code}).
Expect().Status(httptest.StatusOK).JSON().Equal(expected1)
Expect().Status(httptest.StatusOK).JSON().IsEqual(expected1)
expected2 := testPreflightResponse{Code: httptest.StatusBadRequest, Message: "Bad Request"}
e.POST("/").WithJSON(testPreflightRequest{FailCode: expected2.Code}).
Expect().Status(httptest.StatusBadRequest).JSON().Equal(expected2)
Expect().Status(httptest.StatusBadRequest).JSON().IsEqual(expected2)
// Test error returned from Preflight.
e.POST("/").WithJSON(testPreflightRequest{FailCode: httptest.StatusInternalServerError}).
Expect().Status(httptest.StatusBadRequest).Body().Equal("custom error")
Expect().Status(httptest.StatusBadRequest).Body().IsEqual("custom error")
// Can be done without Preflight as the second output argument can be a status code.
expected4 := testOutput{Name: "my_name"}
e.POST("/alternative").WithJSON(testInput{expected4.Name}).
Expect().Status(httptest.StatusAccepted).JSON().Equal(expected4)
Expect().Status(httptest.StatusAccepted).JSON().IsEqual(expected4)
}
func TestResponseErr(t *testing.T) {
@@ -280,5 +280,5 @@ func TestResponseErr(t *testing.T) {
})
e := httptest.New(t, app)
e.GET("/").Expect().Status(iris.StatusBadGateway).Body().Equal("response error")
e.GET("/").Expect().Status(iris.StatusBadGateway).Body().IsEqual("response error")
}

View File

@@ -93,13 +93,13 @@ func testAppWithHeroHandlers(t *testing.T, h1, h2, h3 iris.Handler) {
e := httptest.New(t, app)
// 1
e.GET(fmt.Sprintf("/%d/%s", expectedUser.ID, expectedUser.Username)).Expect().Status(httptest.StatusOK).
JSON().Equal(expectedUser)
JSON().IsEqual(expectedUser)
// 2
e.GET("/service").Expect().Status(httptest.StatusOK).
Body().Equal("say something")
Body().IsEqual("say something")
// 3
e.GET("/param/the_param_value").Expect().Status(httptest.StatusOK).
Body().Equal("param is: the_param_value")
Body().IsEqual("param is: the_param_value")
}
// TestBindFunctionAsFunctionInputArgument tests to bind
@@ -123,7 +123,7 @@ func TestBindFunctionAsFunctionInputArgument(t *testing.T) {
expectedUsername := "kataras"
e.POST("/").WithFormField("username", expectedUsername).
Expect().Status(iris.StatusOK).Body().Equal(expectedUsername)
Expect().Status(iris.StatusOK).Body().IsEqual(expectedUsername)
}
func TestPayloadBinding(t *testing.T) {
@@ -155,21 +155,21 @@ func TestPayloadBinding(t *testing.T) {
e := httptest.New(t, app)
// JSON
e.POST("/").WithJSON(iris.Map{"username": "makis"}).Expect().Status(httptest.StatusOK).Body().Equal("makis")
e.POST("/2").WithJSON(iris.Map{"username": "kataras"}).Expect().Status(httptest.StatusOK).Body().Equal("kataras")
e.POST("/").WithJSON(iris.Map{"username": "makis"}).Expect().Status(httptest.StatusOK).Body().IsEqual("makis")
e.POST("/2").WithJSON(iris.Map{"username": "kataras"}).Expect().Status(httptest.StatusOK).Body().IsEqual("kataras")
// FORM (url-encoded)
e.POST("/").WithFormField("username", "makis").Expect().Status(httptest.StatusOK).Body().Equal("makis")
e.POST("/").WithFormField("username", "makis").Expect().Status(httptest.StatusOK).Body().IsEqual("makis")
// FORM (multipart)
e.POST("/").WithMultipart().WithFormField("username", "makis").Expect().Status(httptest.StatusOK).Body().Equal("makis")
e.POST("/").WithMultipart().WithFormField("username", "makis").Expect().Status(httptest.StatusOK).Body().IsEqual("makis")
// FORM: test ErrorHandler skip the ErrPath.
e.POST("/").WithMultipart().WithFormField("username", "makis").WithFormField("unknown", "continue").
Expect().Status(httptest.StatusOK).Body().Equal("makis")
Expect().Status(httptest.StatusOK).Body().IsEqual("makis")
// POST URL query.
e.POST("/").WithQuery("username", "makis").Expect().Status(httptest.StatusOK).Body().Equal("makis")
e.POST("/").WithQuery("username", "makis").Expect().Status(httptest.StatusOK).Body().IsEqual("makis")
// GET URL query.
e.GET("/").WithQuery("username", "makis").Expect().Status(httptest.StatusOK).Body().Equal("makis")
e.GET("/").WithQuery("username", "makis").Expect().Status(httptest.StatusOK).Body().IsEqual("makis")
}
/* Author's notes:
@@ -231,9 +231,9 @@ func TestDependentDependencies(t *testing.T) {
app.Get("/h3", h3)
e := httptest.New(t, app)
e.GET("/h1").Expect().Status(httptest.StatusOK).Body().Equal("prefix: it is a deep dependency")
e.GET("/h2").Expect().Status(httptest.StatusOK).Body().Equal("prefix: message")
e.GET("/h3").Expect().Status(httptest.StatusOK).Body().Equal("value")
e.GET("/h1").Expect().Status(httptest.StatusOK).Body().IsEqual("prefix: it is a deep dependency")
e.GET("/h2").Expect().Status(httptest.StatusOK).Body().IsEqual("prefix: message")
e.GET("/h3").Expect().Status(httptest.StatusOK).Body().IsEqual("value")
}
func TestHandlerPathParams(t *testing.T) {
@@ -262,7 +262,7 @@ func TestHandlerPathParams(t *testing.T) {
e.GET("/editors/42"),
e.GET("/1/book/42"),
} {
testReq.Expect().Status(httptest.StatusOK).Body().Equal("42")
testReq.Expect().Status(httptest.StatusOK).Body().IsEqual("42")
}
}
@@ -301,8 +301,8 @@ func TestRegisterDependenciesFromContext(t *testing.T) {
e := httptest.New(t, app)
e.GET("/").Expect().Status(httptest.StatusOK).Body().Equal("kataras")
e.GET("/service").Expect().Status(httptest.StatusOK).Body().Equal("say hello")
e.GET("/both").Expect().Status(httptest.StatusOK).Body().Equal("say kataras")
e.GET("/non").Expect().Status(httptest.StatusOK).Body().Equal("nothing")
e.GET("/").Expect().Status(httptest.StatusOK).Body().IsEqual("kataras")
e.GET("/service").Expect().Status(httptest.StatusOK).Body().IsEqual("say hello")
e.GET("/both").Expect().Status(httptest.StatusOK).Body().IsEqual("say kataras")
e.GET("/non").Expect().Status(httptest.StatusOK).Body().IsEqual("nothing")
}

View File

@@ -42,6 +42,39 @@ func isFunc(kindable interface{ Kind() reflect.Kind }) bool {
return kindable.Kind() == reflect.Func
}
func isStructValue(v reflect.Value) bool {
return indirectType(v.Type()).Kind() == reflect.Struct
}
// isBuiltin reports whether a reflect.Value is a builtin type
func isBuiltinValue(v reflect.Value) bool {
switch v.Type().Kind() {
case reflect.Bool,
reflect.Int,
reflect.Int8,
reflect.Int16,
reflect.Int32,
reflect.Int64,
reflect.Uint,
reflect.Uint8,
reflect.Uint16,
reflect.Uint32,
reflect.Uint64,
reflect.Float32,
reflect.Float64,
reflect.Complex64,
reflect.Complex128,
reflect.Array,
reflect.Chan,
reflect.Map,
reflect.Slice,
reflect.String:
return true
default:
return false
}
}
var (
inputTyp = reflect.TypeOf((*Input)(nil))
errTyp = reflect.TypeOf((*error)(nil)).Elem()

View File

@@ -57,6 +57,8 @@ func makeStruct(structPtr interface{}, c *Container, partyParamsCount int) *Stru
// If static then Struct.Acquire will return the same "value" instance, otherwise it will create a new one.
singleton := true
elem := v.Elem()
// fmt.Printf("makeStruct: bindings length = %d\n", len(bindings))
for _, b := range bindings {
if b.Dependency.Static {
// Fill now.

View File

@@ -46,11 +46,11 @@ func TestStruct(t *testing.T) {
app.Get("/myHandler4", getHandler)
e := httptest.New(t, app)
e.POST("/" + input.Name).Expect().Status(httptest.StatusOK).JSON().Equal(expectedOutput)
e.POST("/" + input.Name).Expect().Status(httptest.StatusOK).JSON().IsEqual(expectedOutput)
path := fmt.Sprintf("/%d", expectedOutput.ID)
e.POST(path).WithJSON(input).Expect().Status(httptest.StatusOK).JSON().Equal(expectedOutput)
e.POST("/myHandler3").WithJSON(input).Expect().Status(httptest.StatusOK).JSON().Equal(expectedOutput)
e.GET("/myHandler4").Expect().Status(httptest.StatusOK).Body().Equal("MyHandler4")
e.POST(path).WithJSON(input).Expect().Status(httptest.StatusOK).JSON().IsEqual(expectedOutput)
e.POST("/myHandler3").WithJSON(input).Expect().Status(httptest.StatusOK).JSON().IsEqual(expectedOutput)
e.GET("/myHandler4").Expect().Status(httptest.StatusOK).Body().IsEqual("MyHandler4")
}
type testStructErrorHandler struct{}
@@ -72,7 +72,7 @@ func TestStructErrorHandler(t *testing.T) {
expectedErrText := "an error"
e := httptest.New(t, app)
e.GET("/" + expectedErrText).Expect().Status(httptest.StatusConflict).Body().Equal(expectedErrText)
e.GET("/" + expectedErrText).Expect().Status(httptest.StatusConflict).Body().IsEqual(expectedErrText)
}
type (
@@ -117,5 +117,5 @@ func TestStructFieldsSorter(t *testing.T) { // see https://github.com/kataras/ir
e := httptest.New(t, app)
expectedBody := `&hero_test.testServiceImpl1{inner:"parser"} | &hero_test.testServiceImpl2{tf:24}`
e.GET("/").Expect().Status(httptest.StatusOK).Body().Equal(expectedBody)
e.GET("/").Expect().Status(httptest.StatusOK).Body().IsEqual(expectedBody)
}