自定义 JSON 架构未正确验证
Custom built JSON schema not validating properly
我有一个自定义构建的 JSON 架构,它只有几个顶层。这里的问题是它没有将所有内容都验证到 100%。例如,它只检测 4 个字段中的 2 个,并且必填字段根本不起作用,附加属性也不起作用,等等。我正在为我的 json 模式使用 this library。
{
"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
}
}
}
我正在像这样解析所有内容:
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))
}
我这样做的原因是我正在构建一个 REST API,如果 api/auth/user
收到 PUT 请求,我希望能够指定数据要求具体来说 "users" 使用 PUT 方法的部分。
知道如何实现吗?
编辑:
我的 json 数据:
{
"DisplayName": "1234",
"Username": "1234",
"Email": "test@gmail.com",
"Password": "123456"
}
编辑 2:
此数据应该因架构而失败。
{
"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
}
如果我使用 gojsonschema
手动加载模式和数据,它会按预期工作。我怀疑由于您以某种复杂的方式加载模式,因此您放入的模式最终会与您期望的有所不同,但由于您的代码示例都是基于 HTTP 的,所以我自己无法真正测试它.
我有一个自定义构建的 JSON 架构,它只有几个顶层。这里的问题是它没有将所有内容都验证到 100%。例如,它只检测 4 个字段中的 2 个,并且必填字段根本不起作用,附加属性也不起作用,等等。我正在为我的 json 模式使用 this library。
{
"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
}
}
}
我正在像这样解析所有内容:
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))
}
我这样做的原因是我正在构建一个 REST API,如果 api/auth/user
收到 PUT 请求,我希望能够指定数据要求具体来说 "users" 使用 PUT 方法的部分。
知道如何实现吗?
编辑: 我的 json 数据:
{
"DisplayName": "1234",
"Username": "1234",
"Email": "test@gmail.com",
"Password": "123456"
}
编辑 2: 此数据应该因架构而失败。
{
"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
}
如果我使用 gojsonschema
手动加载模式和数据,它会按预期工作。我怀疑由于您以某种复杂的方式加载模式,因此您放入的模式最终会与您期望的有所不同,但由于您的代码示例都是基于 HTTP 的,所以我自己无法真正测试它.