根据用户角色控制字段可见性

时间:2017-03-15 18:41:47

标签: json rest go annotations response

我想根据用户角色隐藏/显示模型的某些字段。

实施它的最惯用方法是什么?

我真的不想创建N种不同类型的相同模型(其中N是用户角色的数量)。喜欢: UserEmployee,AdminEmployee,WhateverEmployee。

如果有一些解决方案使用标签,那将是完美的:

type Employee struct {
   ID string `visibility:"admin,user"`
   Name string `visibility:"admin,user"`
   Salary int `visibility:"admin"`
}

jsonBytes, _ := someLib.Marshal(Employee{"1", "John", 5000}, "user")

fmt.Println(string(jsonBytes)) // {"id":"1","name":"John"}

这个问题非常广泛。我只想知道你是如何处理这种情况的,或者在Go社区中最常用的方法是什么。我想要干净和集中(对所有型号都相同)的解决方案,不需要生成大量的重复代码。

我以前尝试过什么:我只是试图为所有情况使用单独的模型并在它们之间进行投射。

2 个答案:

答案 0 :(得分:2)

  1. 创建一个类型的空结构(此问题中的Employee),它将保存过滤后的数据。
  2. 使用reflect包来比较字段标记是否包含所需的标记值(可见性角色)。
  3. 当我们找到标记匹配并将json marshal输出结构复制时,将基础结构的值复制到我们的过滤器结构中:
  4. package main
    
    import (
        "encoding/json"
        "fmt"
        "reflect"
        "strings"
    )
    
    type Employee struct {
        ID       string          `visibility:"admin, hr, user" json:"id,omitempty"`
        Name     string          `visibility:"admin, hr, user" json:"name,omitempty"`
        Salary   int             `visibility:"admin, hr" json:"salary,omitempty"`
        Password string          `visibility:"admin" json:"password,omitempty"`
        Rights   map[string]bool `visibility:"admin" json:"rights,omitempty"`
        Boss     *Employee       `visibility:"admin, hr" json:"boss,omitempty"`
    }
    
    func filterEmployee(emp Employee, role string) Employee {
        var fEmployee Employee
        ev := reflect.ValueOf(emp)
        et := reflect.TypeOf(emp)
    
        // Iterate through each field within the struct
        for i := 0; i < ev.NumField(); i++ {
            v := ev.Field(i)
            t := et.Field(i)
            roles := t.Tag.Get("visibility")
    
            if strings.Contains(roles, role) {
                switch i {
                case 0: // ID
                    fEmployee.ID = v.String()
                case 1: // Name
                    fEmployee.Name = v.String()
                case 2: // Salary
                    fEmployee.Salary = int(v.Int())
                case 3: // Password
                    fEmployee.Password = v.String()
                case 4: // Rights
                    fEmployee.Rights = v.Interface().(map[string]bool)
                case 5: // Boss
                    fEmployee.Boss = v.Interface().(*Employee)
                }
            }
        }
        return fEmployee
    }
    
    func main() {
    
        e := Employee{
            "1",
            "Jack",
            100000,
            "password321",
            map[string]bool{"create": false, "update": false},
            &Employee{
                "2",
                "John",
                120000,
                "pwd",
                map[string]bool{"create": true, "update": true},
                nil,
            },
        }
    
        fuser := filterEmployee(e, "user")
        fhr := filterEmployee(e, "hr")
        fadmin := filterEmployee(e, "admin")
    
        buser, err := json.MarshalIndent(fuser, "", "  ")
        if err != nil {
            fmt.Println(err)
        }
        fmt.Println("Filtering with role user: ")
        fmt.Println(string(buser))
    
        bhr, err := json.MarshalIndent(fhr, "", "  ")
        if err != nil {
            fmt.Println(err)
        }
        fmt.Println("\nFiltering with role hr: ")
        fmt.Println(string(bhr))
    
        badmin, err := json.MarshalIndent(fadmin, "", "  ")
        if err != nil {
            fmt.Println(err)
        }
        fmt.Println("\nFiltering with role admin: ")
        fmt.Println(string(badmin))
    }
    

    输出:

    Filtering with role user: 
    {
      "id": "1",
      "name": "Jack"
    }
    
    Filtering with role hr: 
    {
      "id": "1",
      "name": "Jack",
      "salary": 100000,
      "boss": {
        "id": "2",
        "name": "John",
        "salary": 120000,
        "password": "pwd",
        "rights": {
          "create": true,
          "update": true
        }
      }
    }
    
    Filtering with role admin: 
    {
      "id": "1",
      "name": "Jack",
      "salary": 100000,
      "password": "password321",
      "rights": {
        "create": false,
        "update": false
      },
      "boss": {
        "id": "2",
        "name": "John",
        "salary": 120000,
        "password": "pwd",
        "rights": {
          "create": true,
          "update": true
        }
      }
    }
    

    Playground

    编辑:更新了提问者请求的答案。

    查看以前回答问题的旧游乐场。

    Old Playground

答案 1 :(得分:1)

使用“省略空”

type Employee struct {
    ID string `json:",omitempty"`
    Name string `json:",omitempty"`
    Salary int `json:",omitempty"`
}

您的功能可能看起来像

func MarshallEmployee(e Employee, permission string) {
    if permission == "user"{
        e.Salary = 0
    }
    ....marshall it
}

或者您也可以不首先将值添加到结构中。有关详细信息,请参阅the docs