dplm47571 2018-03-24 01:41
浏览 25
已采纳

递归结构反射错误:紧急:反射:非结构类型的字段

Trying to make a function that walks a struct recursively, and modifies any fields that are strings, based on on a certain tag.

Reflection is very tedious to work with. First time using it and having some troubles.

I'm getting a panic from one of my lines of code:

panic: reflect: Field of non-struct type

The panic comes from this line:

tf := vf.Type().Field(i)

I'm trying to get the type field so I can get a tag from it.

Here is the full function:

func Sanitize(s interface{}) error {
    v := reflect.ValueOf(s)

    // It's a pointer struct, convert to the value that it points to.
    if v.Kind() == reflect.Ptr && !v.IsNil() {
        v = v.Elem()
    }

    // Not a struct, return an error.
    if v.Kind() != reflect.Struct {
        return &InvalidSanitizerError{Type: reflect.TypeOf(s)}
    }

    for i := 0; i < v.NumField(); i++ {
        vf := v.Field(i)

        if vf.Kind() == reflect.Struct {
            // Recurse.
            err := Sanitize(v.Field(i).Interface())
            if err != nil {
                return err
            }

            // Move onto the next field.
            continue
        }


        if vf.Kind() == reflect.String {
            tf := vf.Type().Field(i) // <-- TROUBLE MAKER

            // Get the field tag value
            tag := tf.Tag.Get("sanitize")

            // Skip if tag is not defined or ignored
            if tag == "" || tag == "-" {
                continue
            }
            shouldSanitize, err := strconv.ParseBool(tag)
            if err != nil {
                return err
            }

            if shouldSanitize && vf.CanSet() {
                vf.SetString(policy.Sanitize(vf.String()))
            }
        }
    }

    return nil
}

This is an example of how the function should be used:

type User struct {
    Name string `sanitize:"true"`
    Bio *Bio
}

type Bio struct {
    Text string `sanitize:"true"`
}

func main() {
    user := &User{
        Name: "Lansana<script>alert('rekt');</script>",
        Bio: &Bio{
            Text: "Hello world</script>alert('rekt');</script>",
        },
    }
    if err := Sanitize(user); err != nil {
        panic(err)
    }

    fmt.Println(user.Name) // Lansana
    fmt.Println(user.Bio.Text) // Hello world
}

Any insight on the panic would be greatly appreciated.

  • 写回答

1条回答 默认 最新

  • dt246813579 2018-03-25 01:44
    关注

    After working all day on this, this was the solution I finally came up with to walk a struct recursively and modify the value of all string values that have a specific tag.

    The Sanitize function only allows pointer to structs, but the nested structs can be either pointers or values; it doesn't matter.

    The example in my question will work with the function below, and it passes all my tests.

    func Sanitize(s interface{}) error {
        if s == nil {
            return nil
        }
    
        val := reflect.ValueOf(s)
    
        // If it's an interface or a pointer, unwrap it.
        if val.Kind() == reflect.Ptr && val.Elem().Kind() == reflect.Struct {
            val = val.Elem()
        } else {
            return &InvalidStructError{message: "s must be a struct"}
        }
    
        valNumFields := val.NumField()
    
        for i := 0; i < valNumFields; i++ {
            field := val.Field(i)
            fieldKind := field.Kind()
    
            // Check if it's a pointer to a struct.
            if fieldKind == reflect.Ptr && field.Elem().Kind() == reflect.Struct {
                if field.CanInterface() {
                    // Recurse using an interface of the field.
                    err := Sanitize(field.Interface())
                    if err != nil {
                        return err
                    }
                }
    
                // Move onto the next field.
                continue
            }
    
            // Check if it's a struct value.
            if fieldKind == reflect.Struct {
                if field.CanAddr() && field.Addr().CanInterface() {
                    // Recurse using an interface of the pointer value of the field.
                    err := Sanitize(field.Addr().Interface())
                    if err != nil {
                        return err
                    }
                }
    
                // Move onto the next field.
                continue
            }
    
            // Check if it's a string or a pointer to a string.
            if fieldKind == reflect.String || (fieldKind == reflect.Ptr && field.Elem().Kind() == reflect.String) {
                typeField := val.Type().Field(i)
    
                // Get the field tag value.
                tag := typeField.Tag.Get("sanitize")
    
                // Skip if tag is not defined or ignored.
                if tag == "" || tag == "-" {
                    continue
                }
    
                // Check if the tag is allowed.
                if tag != "true" && tag != "false" {
                    return &InvalidTagError{message: "tag must be either 'true' or 'false'."}
                }
    
                // Parse it to a bool.
                shouldSanitize, err := strconv.ParseBool(tag)
                if err != nil {
                    return err
                } else if !shouldSanitize {
                    continue
                }
    
                // Set the string value to the sanitized string if it's allowed.
                // It should always be allowed at this point.
                if field.CanSet() {
                    field.SetString(policy.Sanitize(field.String()))
                }
    
                continue
            }
        }
    
        return nil
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥60 版本过低apk如何修改可以兼容新的安卓系统
  • ¥25 由IPR导致的DRIVER_POWER_STATE_FAILURE蓝屏
  • ¥50 有数据,怎么建立模型求影响全要素生产率的因素
  • ¥50 有数据,怎么用matlab求全要素生产率
  • ¥15 TI的insta-spin例程
  • ¥15 完成下列问题完成下列问题
  • ¥15 C#算法问题, 不知道怎么处理这个数据的转换
  • ¥15 YoloV5 第三方库的版本对照问题
  • ¥15 请完成下列相关问题!
  • ¥15 drone 推送镜像时候 purge: true 推送完毕后没有删除对应的镜像,手动拷贝到服务器执行结果正确在样才能让指令自动执行成功删除对应镜像,如何解决?