jio is a json schema validator similar to joi

jio

jio

Make validation simple and efficient !

Travis branch Coverage Status Go Report Card License GoDoc License

中文文档

Why use jio?

Parameter validation in Golang is really a cursing problem. Defining tags on structs is not easy to extend rules, handwritten validation code makes logic code cumbersome, and the initial zero value of the struct field will also interfere with the validation.

jio tries validate json raw data before deserialization to avoid these problems. Defining validation rules as Schema is easy to read and easy to extend (Inspired by Hapi.js joi library). Rules within Schema can be validated in the order of registration, and context can be used to exchange data between rules, and can access other field data even within a single rule, etc.

jio provides a flexible enough way to make your validation simple and efficient!

How to use?

Validate json string

package main

import (
    "log"

    "github.com/faceair/jio"
)

func main() {
    data := []byte(`{
        "debug": "on",
        "window": {
            "title": "Sample Widget",
            "size": [500, 500]
        }
    }`)
    _, err := jio.ValidateJSON(&data, jio.Object().Keys(jio.K{
        "debug": jio.Bool().Truthy("on").Required(),
        "window": jio.Object().Keys(jio.K{
            "title": jio.String().Min(3).Max(18),
            "size":  jio.Array().Items(jio.Number().Integer()).Length(2).Required(),
        }).Without("name", "title").Required(),
    }))
    if err != nil {
        panic(err)
    }
    log.Printf("%s", data) // {"debug":true,"window":{"size":[500,500],"title":"Sample Widget"}}
}

The above schema defines the following constraints:

  • debug
    • not empty, must be a boolean value when validation end
    • allow on string instead of true
  • window
    • not empty, object
    • not allowed for both name and title
    • The following elements exist
      • title
        • string, can be empty
        • length is between 3 and 18 when not empty
      • size
        • array, not empty
        • there are two child elements of the integer type

Using middleware to validate request body

Take chi as an example, the other frameworks are similar.

package main

import (
    "io/ioutil"
    "net/http"

    "github.com/faceair/jio"
    "github.com/go-chi/chi"
)

func main() {
    r := chi.NewRouter()
    r.Route("/people", func(r chi.Router) {
        r.With(jio.ValidateBody(jio.Object().Keys(jio.K{
            "name":  jio.String().Min(3).Max(10).Required(),
            "age":   jio.Number().Integer().Min(0).Max(100).Required(),
            "phone": jio.String().Regex(`^1[34578]\d{9}$`).Required(),
        }), jio.DefaultErrorHandler)).Post("/", func(w http.ResponseWriter, r *http.Request) {
            body, err := ioutil.ReadAll(r.Body)
            if err != nil {
                panic(err)
            }
            w.Header().Set("Content-Type", "application/json; charset=utf-8")
            w.WriteHeader(http.StatusOK)
            w.Write(body)
        })
    })
    http.ListenAndServe(":8080", r)
}

The second parameter of jio.ValidateBody is called for error handling when the validation fails.

Validate the query parameter with middleware

package main

import (
    "encoding/json"
    "net/http"

    "github.com/faceair/jio"
    "github.com/go-chi/chi"
)

func main() {
    r := chi.NewRouter()
    r.Route("/people", func(r chi.Router) {
        r.With(jio.ValidateQuery(jio.Object().Keys(jio.K{
            "keyword":  jio.String(),
            "is_adult": jio.Bool().Truthy("true", "yes").Falsy("false", "no"),
            "starts_with": jio.Number().ParseString().Integer(),
        }), jio.DefaultErrorHandler)).Get("/", func(w http.ResponseWriter, r *http.Request) {
            query := r.Context().Value(jio.ContextKeyQuery).(map[string]interface{})
            body, err := json.Marshal(query)
            if err != nil {
                panic(err)
            }
            w.Header().Set("Content-Type", "application/json; charset=utf-8")
            w.WriteHeader(http.StatusOK)
            w.Write(body)
        })
    })
    http.ListenAndServe(":8080", r)
}

Note that the original value of the query parameter is string, you may need to convert the value type first (for example, jio.Number().ParseString() or jio.Bool().Truthy(values)).

API Documentation

https://godoc.org/github.com/faceair/jio

Advanced usage

Workflow

Each Schema is made up of a series of rules, for example:

jio.String().Min(5).Max(10).Alphanum().Lowercase()

In this example, String Schema has 4 rules, which are Min(5) Max(10) Alphanum() Lowercase(), will also validate in order Min(5) Max(10) Alphanum() Lowercase(). If a rule validation fails, the Schema's validation stops and throws an error.

In order to improve the readability of the code, these three built-in rules will validate first.

  • Required()
  • Optional()
  • Default(value)

For example:

jio.String().Min(5).Max(10).Alphanum().Lowercase().Required()

The actual validation order will be Required() Min(5) Max(10) Alphanum() Lowercase().

After validate all the rules, finally we check if the basic type of the data is the type of Schema. If not, the Schema will throw an error.

Validator Context

Data transfer in the workflow depends on context, the structure is like this:

Type Context struct {
    Value interface{} // Raw data, you can also reassign to change the result
}
func (ctx *Context) Ref(refPath string) (value interface{}, ok bool) { // Reference other field data
}
func (ctx *Context) Abort(err error) { // Terminate the validation and throw an error
  ...
}
func (ctx *Context) Skip() { // Skip subsequent rules
  ...
}

Let's try to customize a validation rule. Add a rule to use the Transform method:

jio.String().Transform(func(ctx *jio.Context) {
    If ctx.Value != "faceair" {
        ctx.Abort(errors.New("you are not faceair"))
    }
})

The custom rule we added means throwing a you are not faceair error when the original data is not equal to faceair.

In fact, the built-in validation rules work in a similar way. For example, the core code of Optional() is:

If ctx.Value == nil {
  ctx.Skip()
}

You can also reassign ctx.Value to change the original data. For example, the built-in Lowercase() converts the original string to lowercase. The core code is:

ctx.Value = strings.ToLower(ctx.Value)

References and Priority

In most cases, the rules only use the data of the current field, but sometimes it needs to work with other fields. For example:

{
    "type": "ip", // enumeration value, `ip` or `domain`
    "value": "8.8.8.8"
}

The validation rules of this value is determined by the value of type and can be written as

jio.Object().Keys(jio.K{
        "type": jio.String().Valid("ip", "domain").SetPriority(1).Default("ip"),
        "value": jio.String().
            When("type", "ip", jio.String().Regex(`^\d+\.\d+\.\d+\.\d+$`)).
            When("type", "domain", jio.String().Regex(`^[a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0 -9]\.[a-zA-Z]{2,}$`)).Required(),
})

The When function can reference other field data, and if it is successful, apply the new validation rule to the current data.

In addition, you may notice that there is a SetPriority method in the rules of type. If the input data is:

{
    "value": "8.8.8.8"
}

When the priority is not set, the validation rule of value may be executed first. At this time, the value of the reference type will be null, and the validation will fail. Because there are validation rules that refer to each other, there may be a validation sequence requirement. When we want a field under the same Object to be validated first, we can set it to a larger priority value (default value 0).

If you want to reference data from other fields in your custom rules, you can use the Ref method on the context. If the referenced data is a nested object, the path to the referenced field needs to be concatenated with . . For example, if you want to reference name under people object then the reference path is people.name:

{
    "type": "people",
    "people": {
        "name": "faceair"
    }
}

License

MIT

Similar Resources

A demo project shows how to use validator to validate parameters

validator-demo This project is a demo project shows how to use validator to validate parameters use case install requirements go get github.com/favadi

Jan 10, 2022

Iran National Id, Bank Card Number, Mobile Number Validator for golang

Iran IDValidator Iran National Id, Bank Card Number, Mobile Number Validator for golang Installation go get -u github.com/mohammadv184/idvalidator Us

Dec 20, 2021

Validator - Replace the validation framework used by gin

validator Replace the validation framework used by gin replace mod:replace githu

Jan 18, 2022

A lightweight model validator written in Go.

validator A lightweight model validator written in Go. quickstart package main import ( "fmt" v "github.com/go-the-way/validator" ) func main() {

Sep 27, 2022

Simple JSON type checking.

go-map-schema Table of Contents Overview Use Case Do I Really Need This? Examples Usage Full Code Output Universal Type Names Overview go-map-schema i

Sep 3, 2022

Schema - JSON Schema rules plugin

This plugin allows to configure JSON Schema validations rules ensuring user-submitted records adhere to a pre-defined data schema.

Feb 16, 2022

go-playground-converter is formatter error response inspiration like express-validator in nodejs build on top go-playground-validator.

Go Playground Converter go-playground-converter is formatter error response inspiration like express-validator in nodejs build on top in go-playground

Dec 9, 2022

Traefik config validator: a CLI tool to (syntactically) validate your Traefik configuration filesTraefik config validator: a CLI tool to (syntactically) validate your Traefik configuration files

Traefik config validator: a CLI tool to (syntactically) validate your Traefik configuration filesTraefik config validator: a CLI tool to (syntactically) validate your Traefik configuration files

Traefik Config Validator Note This is currently pre-release software. traefik-config-validator is a CLI tool to (syntactically) validate your Traefik

Dec 16, 2021

Snake-validator - Snake validator with golang

snake-validator Overview The concept of Snake (the video game) has been around s

Jan 22, 2022

A tool to compare if terraform provider migration schema snapshot is equal to schema defined in resource code

migration schema comparer for Terraform When develop Terraform provider sometimes we need do some state migration(not schema migration) via StateUpgra

Nov 18, 2021

Fast JSON parser and validator for Go. No custom structs, no code generation, no reflection

fastjson - fast JSON parser and validator for Go Features Fast. As usual, up to 15x faster than the standard encoding/json. See benchmarks. Parses arb

Jan 5, 2023

Gin Middleware to extract json tag value from playground validator's errors validation

Json Tag Extractor for Go-Playground Validator This is Gin Middleware that aim to extract json tag and than store it to FieldError.Field() object. Ins

Jan 14, 2022

One of the fastest alternative JSON parser for Go that does not require schema

Alternative JSON parser for Go (10x times faster standard library) It does not require you to know the structure of the payload (eg. create structs),

Jan 2, 2023

:balloon: A lightweight struct validator for Go

gody Go versions supported Installation go get github.com/guiferpa/gody/v2 Usage package main import ( "encoding/json" "fmt" "net/http"

Nov 19, 2022

A norms and conventions validator for Terraform

This tool will help you ensure that a terraform folder answer to your norms and conventions rules. This can be really useful in several cases : You're

Nov 29, 2022

Clean architecture validator for go, like a The Dependency Rule and interaction between packages in your Go projects.

Clean architecture validator for go, like a The Dependency Rule and interaction between packages in your Go projects.

Clean Architecture checker for Golang go-cleanarch was created to keep Clean Architecture rules, like a The Dependency Rule and interaction between mo

Dec 31, 2022

Golang parameter validation, which can replace go-playground/validator, includes ncluding Cross Field, Map, Slice and Array diving, provides readable,flexible, configurable validation.

Golang parameter validation, which can replace go-playground/validator, includes ncluding Cross Field, Map, Slice and Array diving, provides readable,flexible, configurable validation.

Checker 中文版本 Checker is a parameter validation package, can be use in struct/non-struct validation, including cross field validation in struct, elemen

Dec 16, 2022
Comments
  • `ObjectSchema` keys are unconditionally added to the JSON object validated

    `ObjectSchema` keys are unconditionally added to the JSON object validated

    package main
    
    import (
    	"bytes"
    	"log"
    
    	"github.com/faceair/jio"
    )
    
    func main() {
    	orig := []byte(`{}`)
    	data := []byte(`{}`)
    	schema := jio.Object().Keys(jio.K{
    		"foo": jio.String(),
    	})
    
    	if _, err := jio.ValidateJSON(&data, schema); err != nil {
    		log.Fatal(err)
    	}
    
    	if !bytes.Equal(orig, data) {
    		log.Printf("Want %s, got %s.", orig, data)
    	}
    }
    

    prints

    Want {}, got {"foo":null}.
    
Gin Middleware to extract json tag value from playground validator's errors validation

Json Tag Extractor for Go-Playground Validator This is Gin Middleware that aim to extract json tag and than store it to FieldError.Field() object. Ins

Jan 14, 2022
:balloon: A lightweight struct validator for Go

gody Go versions supported Installation go get github.com/guiferpa/gody/v2 Usage package main import ( "encoding/json" "fmt" "net/http"

Nov 19, 2022
A norms and conventions validator for Terraform

This tool will help you ensure that a terraform folder answer to your norms and conventions rules. This can be really useful in several cases : You're

Nov 29, 2022
Golang parameter validation, which can replace go-playground/validator, includes ncluding Cross Field, Map, Slice and Array diving, provides readable,flexible, configurable validation.
Golang parameter validation, which can replace go-playground/validator, includes ncluding Cross Field, Map, Slice and Array diving, provides readable,flexible, configurable validation.

Checker 中文版本 Checker is a parameter validation package, can be use in struct/non-struct validation, including cross field validation in struct, elemen

Dec 16, 2022
golang request validator

validator Golang 参数验证器,目前只支持POST请求,JSON格式参数验证 亮点 1、验证时只要有一个错误,错误信息立即返回 2、可自定义参数别名显示错误信息;详情见_example文件 使用 go mod -u github.com/one-gold-coin/validator

Sep 30, 2021
golang rule-based string validator

gosv golang rule-based string validator usage import "github.com/s0rg/gosv" var MyRules = []gosv.Rule{ gosv.MinLen(8), gosv.MaxLen(64), gosv.MinLo

Nov 20, 2021
Checker/validator for Hong Kong IDs

hkidchecker Checker/validator for Hong Kong IDs Description This Go package validates Hong Kong ID card IDs. Useful for example for validating form in

Oct 13, 2021
The Hyperscale InputFilter library provides a simple inputfilter chaining mechanism by which multiple filters and validator may be applied to a single datum in a user-defined order.

Hyperscale InputFilter Branch Status Coverage master The Hyperscale InputFilter library provides a simple inputfilter chaining mechanism by which mult

Oct 20, 2021
OpenShift OLM Catalog Validator

OpenShift OLM Catalog Validator Overview It is an external validator which can be used with Operator-SDK to check the vendor-like criteria to publish

Nov 22, 2021
Vat ID Validator for Germany

German Vat Validator service This project has been developed using Hexagonal architecture and repository pattern. How to run? docker-compose up -d Cal

Nov 4, 2021