douzhantao2857 2018-08-22 22:27
浏览 25
已采纳

自定义构建的JSON模式无法正确验证

I have a custom built JSON schema that only has a few more top-levels. The problem here is that it doesn't validate everything to 100%. For example, it only detects 2 out of 4 fields, and the required fields do not work at all, neither does additionalproperties, etc. I'm using this library for my json schema.

{
    "users": {
        "PUT": {
          "definitions": {},
          "$schema": "http://json-schema.org/draft-07/schema#",
          "$id": "http://example.com/root.json",
          "type": "object",
          "title": "The Root Schema",
          "required": [
            "DisplayName",
            "Username",
            "Email",
            "Password"
          ],
          "properties": {
            "DisplayName": {
              "$id": "#/properties/DisplayName",
              "type": "string",
              "title": "The Displayname Schema",
              "default": "",
              "examples": [
                ""
              ],
              "minLength": 3,
              "maxLength": 24,
              "pattern": "^(.*)$"
            },
            "Username": {
              "$id": "#/properties/Username",
              "type": "string",
              "title": "The Username Schema",
              "default": "",
              "examples": [
                ""
              ],
              "minLength": 3,
              "maxLength": 15,
              "pattern": "^(.*)$"
            },
            "Email": {
              "$id": "#/properties/Email",
              "type": "string",
              "title": "The Email Schema",
              "default": "",
              "examples": [
                ""
              ],
              "minLength": 7,
              "pattern": "^(.*)$",
              "format": "email"
            },
            "Password": {
              "$id": "#/properties/Password",
              "type": "string",
              "title": "The Password Schema",
              "default": "",
              "examples": [
                ""
              ],
              "pattern": "^(.*)$"
            }
        },
        "additionalProperties": false
        }
    }
}

I'm parsing everything like this:

func Validate(data interface{}, r *http.Request) (interface{}, error) {
    // Convert the data struct to a readable JSON bytes
    JSONparams, err := json.Marshal(data)
    if err != nil {
        return nil, err
    }

    // Split URL segments so we know what part of the API they are accessing
    modules := strings.Split(r.URL.String(), "/")
    modules = modules[(len(modules) - 1):]

    // Read the schema file
    fileSchema, _ := ioutil.ReadFile("config/schema/schema.json")
    var object interface{}

    // Unmarshal it so we can choose what schema we specifically want
    err = json.Unmarshal(fileSchema, &object)
    if err != nil {
        log.Fatal(err)
    }

    // Choose the preferred schema
    encodedJSON, err := json.Marshal(object.(map[string]interface{})[strings.Join(modules, "") + "s"].(map[string]interface{})[r.Method])
    if err != nil {
        log.Fatal(err)
    }

    // Load the JSON schema
    schema := gojsonschema.NewStringLoader(string(encodedJSON))

    // Load the JSON params
    document := gojsonschema.NewStringLoader(string(JSONparams))

    // Validate the document
    result, err := gojsonschema.Validate(schema, document)
    if err != nil {
        return nil, err
    }

    if !result.Valid() {
        // Map the errors into a new array
        var errors = make(map[string]string)
        for _, err := range result.Errors() {
            errors[err.Field()] = err.Description()
        }

        // Convert the array to an interface that we can convert to JSON
        resultMap := map[string]interface{}{
            "success": false,
            "result": map[string]interface{}{},
            "errors": errors,
        }

        // Convert the interface to a JSON object
        errorObject, err := json.Marshal(resultMap)
        if err != nil {
            return nil, err
        }

        return errorObject, nil
    }

    return nil, nil
}

type CreateParams struct {
    DisplayName     string
    Username        string
    Email           string
    Password        string
}

var (
    response interface{}
    status int = 0
)

func Create(w http.ResponseWriter, r *http.Request) {
    status = 0

    // Parse the request so we can access the query parameters
    r.ParseForm()

    // Assign them to the interface variables
    data := &CreateParams{
        DisplayName: r.Form.Get("DisplayName"),
        Username: r.Form.Get("Username"),
        Email: r.Form.Get("Email"),
        Password: r.Form.Get("Password"),
    }

    // Validate the JSON data
    errors, err := schema.Validate(data, r)

    if err != nil {
        responseJSON  := map[string]interface{}{
            "success": false,
            "result": map[string]interface{}{},
        }

        log.Fatal(err.Error())

        response, err = json.Marshal(responseJSON)
        status = http.StatusInternalServerError
    }

    // Catch any errors generated by the validator and assign them to the response interface
    if errors != nil {
        response = errors
        status = http.StatusBadRequest
    }

    // Status has not been set yet, so it's safe to assume that everything went fine
    if status == 0 {
        responseJSON  := map[string]interface{}{
            "success": true,
            "result": map[string]interface{} {
                "DisplayName": data.DisplayName,
                "Username": data.Username,
                "Email": data.Email,
                "Password": nil,
            },
        }

        response, err = json.Marshal(responseJSON)
        status = http.StatusOK
    }

    // We are going to respond with JSON, so set the appropriate header
    w.Header().Set("Content-Type", "application/json")

    // Write the header and the response
    w.WriteHeader(status)
    w.Write(response.([]byte))
}

The reason to why I'm doing it like this is I'm building a REST API and if api/auth/user gets a PUT request, I want to be able to specify the data requirements for specifically the "users" parts with the PUT method.

Any idea how this can be achieved?

EDIT: My json data:

{
  "DisplayName": "1234",
  "Username": "1234",
  "Email": "test@gmail.com",
  "Password": "123456"
}

EDIT 2: This data should fail with the schema.

{
  "DisplayName": "1", // min length is 3
  "Username": "", // this field is required but is empty here
  "Email": "testgmail.com", // not following the email format
  "Password": "123456111111111111111111111111111111111111111111111" // too long
}
  • 写回答

1条回答 默认 最新

  • dqy0707 2018-08-23 13:34
    关注

    If I manually load the schema and data using gojsonschema it works as expected. I suspect that since you're loading the schema in a somewhat complicated fashion the schema you put in ends up being something different than what you'd expect, but since your code samples are all HTTP based I can't really test it out myself.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 一个服务器已经有一个系统了如果用usb再装一个系统,原来的系统会被覆盖掉吗
  • ¥15 使用esm_msa1_t12_100M_UR50S蛋白质语言模型进行零样本预测时,终端显示出了sequence handled的进度条,但是并不出结果就自动终止回到命令提示行了是怎么回事:
  • ¥15 前置放大电路与功率放大电路相连放大倍数出现问题
  • ¥30 关于<main>标签页面跳转的问题
  • ¥80 部署运行web自动化项目
  • ¥15 腾讯云如何建立同一个项目中物模型之间的联系
  • ¥30 VMware 云桌面水印如何添加
  • ¥15 用ns3仿真出5G核心网网元
  • ¥15 matlab答疑 关于海上风电的爬坡事件检测
  • ¥88 python部署量化回测异常问题