Removing fields from struct or hiding them in JSON Response

后端 未结 13 1395
孤街浪徒
孤街浪徒 2020-12-12 09:37

I\'ve created an API in Go that, upon being called, performs a query, creates an instance of a struct, and then encodes that struct as JSON before sending back to the caller

13条回答
  •  不思量自难忘°
    2020-12-12 10:22

    I didn't have the same problem but similar. Below code solves your problem too, of course if you don't mind performance issue. Before implement that kind of solution to your system I recommend you to redesign your structure if you can. Sending variable structure response is over-engineering. I believe a response structure represents a contract between a request and resource and it should't be depend requests.(you can make un-wanted fields null, I do). In some cases we have to implement this design, if you believe you are in that cases here is the play link and code I use.

    type User2 struct {
        ID       int    `groups:"id" json:"id,omitempty"`
        Username string `groups:"username" json:"username,omitempty"`
        Nickname string `groups:"nickname" json:"nickname,omitempty"`
    }
    
    type User struct {
        ID       int    `groups:"private,public" json:"id,omitempty"`
        Username string `groups:"private" json:"username,omitempty"`
        Nickname string `groups:"public" json:"nickname,omitempty"`
    }
    
    var (
        tagName = "groups"
    )
    
    //OmitFields sets fields nil by checking their tag group value and access control tags(acTags)
    func OmitFields(obj interface{}, acTags []string) {
        //nilV := reflect.Value{}
        sv := reflect.ValueOf(obj).Elem()
        st := sv.Type()
        if sv.Kind() == reflect.Struct {
            for i := 0; i < st.NumField(); i++ {
                fieldVal := sv.Field(i)
                if fieldVal.CanSet() {
                    tagStr := st.Field(i).Tag.Get(tagName)
                    if len(tagStr) == 0 {
                        continue
                    }
                    tagList := strings.Split(strings.Replace(tagStr, " ", "", -1), ",")
                    //fmt.Println(tagList)
                    // ContainsCommonItem checks whether there is at least one common item in arrays
                    if !ContainsCommonItem(tagList, acTags) {
                        fieldVal.Set(reflect.Zero(fieldVal.Type()))
                    }
                }
            }
        }
    }
    
    //ContainsCommonItem checks if arrays have at least one equal item
    func ContainsCommonItem(arr1 []string, arr2 []string) bool {
        for i := 0; i < len(arr1); i++ {
            for j := 0; j < len(arr2); j++ {
                if arr1[i] == arr2[j] {
                    return true
                }
            }
        }
        return false
    }
    func main() {
        u := User{ID: 1, Username: "very secret", Nickname: "hinzir"}
        //assume authenticated user doesn't has permission to access private fields
        OmitFields(&u, []string{"public"}) 
        bytes, _ := json.Marshal(&u)
        fmt.Println(string(bytes))
    
    
        u2 := User2{ID: 1, Username: "very secret", Nickname: "hinzir"}
        //you want to filter fields by field names
        OmitFields(&u2, []string{"id", "nickname"}) 
        bytes, _ = json.Marshal(&u2)
        fmt.Println(string(bytes))
    
    }
    

提交回复
热议问题