Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG]hero.Resulthandler can only handle custom struct #2448

Open
dydhyhwu opened this issue Apr 22, 2024 · 2 comments
Open

[BUG]hero.Resulthandler can only handle custom struct #2448

dydhyhwu opened this issue Apr 22, 2024 · 2 comments

Comments

@dydhyhwu
Copy link

hero.ResultHandler can only handle cases where the return value is a custom struct or dispatcher. After reviewing the code, I found that before ResultHandler processing, it needs to go through dispatchFuncResult processing, and then dispatchCommon decides whether to call handlers. I would like to know why it was designed this way.

In scenarios where Container is used to uniformly handle return values, I hope that the return values of all Handlers are uniformly wrapped and processed, so that a unified return value structure can be obtained. However, the framework has specialized handling for some basic types, which forces me to call ctx.JSON(map[string]interface{} {...}) in some handlers.

The code and expected result I hope to achieve is as follows:

type Response struct {
	Code int         `json:"code"`
	Data interface{} `json:"data,omitempty"`
	Msg  string      `json:"msg,omitempty"`
}

type testOutput struct {
	ID   int    `json:"id"`
	Name string `json:"name"`
}

func resultFormatter(next hero.ResultHandler) hero.ResultHandler {
	return func(ctx iris.Context, v interface{}) error {
		log.Infof("use result handler\n")

		return next(ctx, map[string]interface{}{
			"code": 200,
			"data": v,
			"msg":  "",
		})
	}
}

func emptyResHandler(ctx iris.Context) {
	log.Info("hello")
}

func boolResHandler(ctx iris.Context) bool {
	return true
}

func stringResHandler(ctx iris.Context) string {
	return "hello"
}

func intResHandler(ctx iris.Context) int {
	return 1
}

func int64ResHandler(ctx iris.Context) int64 {
	return 1
}

func structResHandler(ctx iris.Context) testOutput {
	return testOutput{
		ID:   1,
		Name: "hello",
	}
}

func configureApi(api *iris.APIContainer) {
	api.UseResultHandler(resultFormatter)
	api.Get("/empty", emptyResHandler)
	api.Get("/bool", boolResHandler)
	api.Get("/string", stringResHandler)
	api.Get("/int", intResHandler)
	api.Get("/int64", int64ResHandler)
	api.Get("/struct", structResHandler)
}

when the return value is empty, the expected result is:

{
    "code": 200,
    "data": null,
    "msg": ""
}

when the return value is string, the expected result is:

{
    "code": 200,
    "data": "string",
    "msg": ""
}

when the return value is custom struct, the expected result is :

{
    "code": 200,
    "data": {
        "id": 1,
        "name": "test"
    },
    "msg": ""
}

and so on...

@dydhyhwu dydhyhwu changed the title hero.Resulthandler can only handle custom struct [BUG]hero.Resulthandler can only handle custom struct Apr 22, 2024
@dydhyhwu
Copy link
Author

related code:

iris/hero/func_result.go

Lines 264 to 331 in b904793

switch value := f.(type) {
case bool:
found = value
if !found {
// skip everything, skip other values, we don't care about other return values,
// this boolean is the higher in order.
break
}
case int:
statusCode = value
case string:
// a string is content type when it contains a slash and
// content or custom struct is being calculated already;
// (string -> content, string-> content type)
// (customStruct, string -> content type)
if (len(content) > 0 || custom != nil) && strings.IndexByte(value, slashB) > 0 {
contentType = value
} else {
// otherwise is content
contentType = context.ContentTextHeaderValue
content = []byte(value)
}
case []byte:
// it's raw content, get the latest
content = value
case compatibleErr:
if value == nil || isNil(v) {
continue
}
if statusCode < 400 && value != ErrStopExecution {
statusCode = DefaultErrStatusCode
}
ctx.StatusCode(statusCode)
return value
default:
// else it's a custom struct or a dispatcher, we'll decide later
// because content type and status code matters
// do that check in order to be able to correctly dispatch:
// (customStruct, error) -> customStruct filled and error is nil
if custom == nil {
// if it's a pointer to struct/map.
if isNil(v) {
// if just a ptr to struct with no content type given
// then try to get the previous response writer's content type,
// and if that is empty too then force-it to application/json
// as the default content type we use for structs/maps.
if contentType == "" {
contentType = ctx.GetContentType()
if contentType == "" {
contentType = context.ContentJSONHeaderValue
}
}
continue
}
if value != nil {
custom = value // content type will be take care later on.
}
}
}
}
return dispatchCommon(ctx, statusCode, contentType, content, custom, handler, found)

iris/hero/func_result.go

Lines 366 to 373 in b904793

if v != nil {
return handler(ctx, v)
}
// .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)
return err

@dydhyhwu
Copy link
Author

@kataras need some help

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant