caicloud / nirvana

Golang Restful API Framework for Productivity
https://caicloud.github.io/nirvana/
Apache License 2.0
520 stars 105 forks source link

[Umbrella] API validation #3

Closed ddysher closed 6 years ago

ddysher commented 7 years ago

Umbrella issue for tracking API validation tasks, scopes:

@walktall

walktall commented 7 years ago

Research on existing solutions

repo stars import by tag support unique feature
go-playground/validator 1193 54 yes inborn cross fields validate; i18n
go-validator/validator 664 204 yes support regexp
go-ozzo/ozzo-validation 431 12 no no struct tags
asaskevich/govalidator 2076 334 yes with some helper functions; support regexp
jimexist commented 7 years ago

also need support for query validation

walktall commented 7 years ago
package validator

// Validatable can be implemented by custom structs.
// Custom structs then are able to be checked by validator.

// Exp:

// type User struct {
//  Name string
//  Age  int
// }

// func (u User) Validate() *ValidateError {
//  if u.Name != "me" {
//      return &ValidateError{Field: "name", Value: u.Name, Reason: "not me"}
//  }
//  return nil
// }
type Validatable interface {
    Validate() *ValidateError
}

// ValidateError is used to keep error format consistent with otheres.
// Should we add a method to convert it to nirvana.Error?
type ValidateError struct {
    Field  string
    Value  interface{}
    Reason string
}
walktall commented 7 years ago
package validator

import (
    "net/http"
    // 2 helpful binding pkg
    // "github.com/go-playground/form"
    // "github.com/gorilla/schema"
)

// WithBinding works like json.Unmarshal. It will parse the content to v and
// then validate v.
// Questions:
// If r.Body has content, then it must be json bytes? Or yaml? Or guess from content?
// What is the query param format? exp: how to define array in query params?
func WithBinding(r *http.Request, v interface{}) error {
    return nil
}
walktall commented 7 years ago

@Jimexist Do you mean query param validation? If so, yes.

ddysher commented 6 years ago

@walktall Action Items after second meeting:

ddysher commented 6 years ago

@walktall Action Items after 4th meeting:

walktall commented 6 years ago

How to implement sample:

// Package validator usage:

// type Application struct {
//  Name      string `json:"name" validate:"required,printascii"`
//  Namespace string `json:"namespace"`
// }

// Definitions: []definition.Definition{
//  {
//      Method:   definition.Create,
//      Function: Handle,
//      Parameters: []definition.Parameter{
//          {
//              Source:    definition.Query,
//              Name:      "target1",
//              Operators: []definition.Operator{validator.Var("gt=0,lt=10")},
//          },
//          {
//              Source: definition.Body,
//              Name:   "app",
//              // Should we call this automatically?
//              Operators: []definition.Operator{validator.Struct()},
//          },
//      },
//  },
// },

package validator

import (
    "context"

    "gopkg.in/go-playground/validator.v9"
)

var std = validator.New()

func Var(tag string) func(ctx context.Context, object interface{}) (interface{}, error) {
    return func(ctx context.Context, object interface{}) (interface{}, error) {
        // TODO: error format
        return object, std.VarCtx(ctx, object, tag)
    }
}

func VarWithValue(other interface{}, tag string) func(ctx context.Context, object interface{}) (interface{}, error) {
    return func(ctx context.Context, object interface{}) (interface{}, error) {
        err := std.VarWithValueCtx(ctx, object, other, tag)
        return object, err
    }
}

func Struct() func(ctx context.Context, object interface{}) (interface{}, error) {
    return func(ctx context.Context, object interface{}) (interface{}, error) {
        return object, std.StructCtx(ctx, object)
    }
}
ddysher commented 6 years ago

Action Items after 5th meeting:

ddysher commented 6 years ago

Done 🚀