1
0
mirror of https://github.com/kataras/iris.git synced 2025-12-18 18:37:05 +00:00

Add new x/errors/validation package to make your life even more easier (using Generics)

This commit is contained in:
Gerasimos (Makis) Maropoulos
2024-01-07 15:08:03 +02:00
parent 8f2deb6873
commit 104bea0a58
14 changed files with 443 additions and 282 deletions

View File

@@ -1,119 +0,0 @@
package main
import (
"fmt"
"time"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/x/errors"
)
func main() {
app := newApp()
app.Listen(":8080")
}
func newApp() *iris.Application {
app := iris.New()
app.Get("/", fireCustomValidationError)
app.Get("/multi", fireCustomValidationErrors)
app.Get("/invalid", fireInvalidError)
return app
}
type MyValidationError struct {
Field string `json:"field"`
Value interface{} `json:"value"`
Reason string `json:"reason"`
Timestamp int64 `json:"timestamp"`
}
func (err MyValidationError) Error() string {
return fmt.Sprintf("field %q got invalid value of %v: reason: %s", err.Field, err.Value, err.Reason)
}
// Error, GetField, GetValue and GetReason completes
// the x/errors.ValidationError interface which can be used
// for faster rendering without the necessity of registering a custom
// type (see at the end of the example).
//
// func (err MyValidationError) GetField() string {
// return err.Field
// }
//
// func (err MyValidationError) GetValue() interface{} {
// return err.Value
// }
//
// func (err MyValidationError) GetReason() string {
// return err.Reason
// }
const shouldFail = true
func fireCustomValidationError(ctx iris.Context) {
if shouldFail {
err := MyValidationError{
Field: "username",
Value: "",
Reason: "empty string",
Timestamp: time.Now().Unix(),
}
// The "validation" field, when used, is always rendering as
// a JSON array, NOT a single object.
errors.InvalidArgument.Err(ctx, err)
return
}
ctx.WriteString("OK")
}
// Optionally register custom types that you may need
// to be rendered as validation errors if the given "ErrorCodeName.Err.err"
// input parameter is matched with one of these. Register once, at initialiation.
func init() {
mapper := errors.NewValidationErrorTypeMapper(MyValidationError{} /*, OtherCustomType{} */)
errors.RegisterValidationErrorMapper(mapper)
}
// A custom type of the example validation error type
// in order to complete the error interface, so it can be
// pass through the errors.InvalidArgument.Err method.
type MyValidationErrors []MyValidationError
func (m MyValidationErrors) Error() string {
return "to be an error"
}
func fireCustomValidationErrors(ctx iris.Context) {
if shouldFail {
errs := MyValidationErrors{
{
Field: "username",
Value: "",
Reason: "empty string",
Timestamp: time.Now().Unix(),
},
{
Field: "birth_date",
Value: "2022-01-01",
Reason: "too young",
Timestamp: time.Now().Unix(),
},
}
errors.InvalidArgument.Err(ctx, errs)
return
}
ctx.WriteString("OK")
}
func fireInvalidError(ctx iris.Context) {
if shouldFail {
errors.InvalidArgument.Err(ctx, fmt.Errorf("just a custom error text"))
return
}
ctx.WriteString("OK")
}

View File

@@ -6,12 +6,13 @@ import (
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/x/errors"
"github.com/kataras/iris/v12/x/errors/validation"
)
func main() {
app := iris.New()
service := new(myService)
app.Post("/", createHandler(service))
app.Get("/", listHandler(service))
app.Delete("/{id:string}", deleteHandler(service))
@@ -70,16 +71,65 @@ type (
myService struct{}
CreateRequest struct {
Fullname string
Fullname string `json:"fullname"`
Age int `json:"age"`
Hobbies []string `json:"hobbies"`
}
CreateResponse struct {
ID string
Firstname string
Lastname string
ID string `json:"id"`
Firstname string `json:"firstname"`
Lastname string `json:"lastname"`
Age int `json:"age"`
Hobbies []string `json:"hobbies"`
}
)
// ValidateContext implements the errors.ContextValidator interface.
// It validates the request body and returns an error if the request body is invalid.
// You can also alter the "r" CreateRequest before calling the service method,
// e.g. give a default value to a field if it's empty or set an ID based on a path parameter.
func (r *CreateRequest) ValidateContext(ctx iris.Context) error {
// To pass custom validation functions:
// return validation.Join(
// validation.String("fullname", r.Fullname).Func(customStringFuncHere),
// OR
// validation.Field("any_field", r.AnyFieldValue).Func(customAnyFuncHere))
return validation.Join(
validation.String("fullname", r.Fullname).NotEmpty().Fullname().Length(3, 50),
validation.Number("age", r.Age).InRange(18, 130),
validation.Slice("hobbies", r.Hobbies).Length(1, 10),
)
/* Example Output:
{
"http_error_code": {
"canonical_name": "INVALID_ARGUMENT",
"status": 400
},
"message": "validation failure",
"details": "fields were invalid",
"validation": [
{
"field": "fullname",
"value": "",
"reason": "must not be empty, must contain first and last name, must be between 3 and 50 characters"
},
{
"field": "age",
"value": 0,
"reason": "must be in range of [18, 130]"
},
{
"field": "hobbies",
"value": null,
"reason": "must be between 1 and 10 elements"
}
]
}
*/
}
func (s *myService) Create(ctx context.Context, in CreateRequest) (CreateResponse, error) {
arr := strings.Split(in.Fullname, " ")
firstname, lastname := arr[0], arr[1]
@@ -89,6 +139,8 @@ func (s *myService) Create(ctx context.Context, in CreateRequest) (CreateRespons
ID: id,
Firstname: firstname,
Lastname: lastname,
Age: in.Age,
Hobbies: in.Hobbies,
}
return resp, nil // , errors.New("create: test error")
}