-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: url redirection, add tag validation
- Loading branch information
call3hudson
committed
Dec 20, 2023
1 parent
f74cb9b
commit 084735f
Showing
11 changed files
with
288 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package handling | ||
|
||
import ( | ||
"fmt" | ||
"net/http" | ||
"platform/http/actionresults" | ||
"platform/services" | ||
"reflect" | ||
"regexp" | ||
) | ||
|
||
func (rc *RouterComponent) AddMethodAlias(srcUrl string, method interface{}, data ...interface{}) *RouterComponent { | ||
var urlgen URLGenerator | ||
services.GetService(&urlgen) | ||
url, err := urlgen.GenerateUrl(method, data...) | ||
if err == nil { | ||
return rc.AddUrlAlias(srcUrl, url) | ||
} else { | ||
panic(err) | ||
} | ||
} | ||
|
||
func (rc *RouterComponent) AddUrlAlias(srcUrl, targetUrl string) *RouterComponent { | ||
aliasFunc := func(interface{}) actionresults.ActionResult { | ||
return actionresults.NewRedirectAction(targetUrl) | ||
} | ||
alias := Route{ | ||
httpMethod: http.MethodGet, | ||
handlerName: "Alias", | ||
actionName: "Redirect", | ||
expression: *regexp.MustCompile(fmt.Sprintf("^%v[/]?$", srcUrl)), | ||
handlerMethod: reflect.Method{ | ||
Type: reflect.TypeOf(aliasFunc), | ||
Func: reflect.ValueOf(aliasFunc), | ||
}, | ||
} | ||
rc.routes = append([]Route{alias}, rc.routes...) | ||
return rc | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
package handling | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"net/http" | ||
"reflect" | ||
"strings" | ||
) | ||
|
||
type URLGenerator interface { | ||
GenerateUrl(method interface{}, data ...interface{}) (string, error) | ||
GenerateURLByName(handlerName, methodName string, data ...interface{}) (string, error) | ||
AddRoutes(routes []Route) | ||
} | ||
|
||
type routeUrlGenerator struct { | ||
routes []Route | ||
} | ||
|
||
func (gen *routeUrlGenerator) AddRoutes(routes []Route) { | ||
if gen.routes == nil { | ||
gen.routes = routes | ||
} else { | ||
gen.routes = append(gen.routes, routes...) | ||
} | ||
} | ||
|
||
func (gen *routeUrlGenerator) GenerateUrl(method interface{}, data ...interface{}) (string, error) { | ||
methodVal := reflect.ValueOf(method) | ||
if methodVal.Kind() == reflect.Func && methodVal.Type().In(0).Kind() == reflect.Struct { | ||
for _, route := range gen.routes { | ||
if route.handlerMethod.Func.Pointer() == methodVal.Pointer() { | ||
return generateUrl(route, data...) | ||
} | ||
} | ||
} | ||
return "", errors.New("No matching route") | ||
} | ||
|
||
func (gen *routeUrlGenerator) GenerateURLByName(handlerName, methodName string, data ...interface{}) (string, error) { | ||
for _, route := range gen.routes { | ||
if strings.EqualFold(route.handlerName, handlerName) && strings.EqualFold(route.httpMethod+route.actionName, methodName) { | ||
return generateUrl(route, data...) | ||
} | ||
} | ||
return "", errors.New("No matching route") | ||
} | ||
|
||
func generateUrl(route Route, data ...interface{}) (url string, err error) { | ||
url = "/" + route.prefix | ||
if !strings.HasPrefix(url, "/") { | ||
url = "/" + url | ||
} | ||
if !strings.HasSuffix(url, "/") { | ||
url += "/" | ||
} | ||
url += strings.ToLower(route.actionName) | ||
if len(data) > 0 && !strings.EqualFold(route.httpMethod, http.MethodGet) { | ||
err = errors.New("Only GET handler can have data values") | ||
} else if strings.EqualFold(route.httpMethod, http.MethodGet) && len(data) != route.handlerMethod.Type.NumIn()-1 { | ||
err = errors.New("Number of data values doesn't match method params") | ||
} else { | ||
for _, val := range data { | ||
url = fmt.Sprintf("%v/%v", url, val) | ||
} | ||
} | ||
return | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
{{ layout "layout.html" }} | ||
|
||
<form method="POST" action="{{ . }}"> | ||
<div style="padding: 5px;"> | ||
<label>Name:</label> | ||
<input name="name" /> | ||
</div> | ||
<div style="padding: 5px;"> | ||
<label>Insert At Front:</label> | ||
<input name="insertatstart" type="checkbox" value="true" /> | ||
</div> | ||
<div style="padding: 5px;"> | ||
<button type="submit">Submit</button> | ||
</div> | ||
</form> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
{{ layout "layout.html" }} | ||
|
||
<h3>Validation Errors</h3> | ||
|
||
<ul> | ||
{{ range . }} | ||
<li>{{.FieldName}}: {{ .Error }}</li> | ||
{{ end }} | ||
</ul> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package validation | ||
|
||
import ( | ||
"reflect" | ||
"strings" | ||
) | ||
|
||
func NewDefaultValidator(validators map[string]ValidatorFunc) Validator { | ||
return &TagValidator{DefaultValidators()} | ||
} | ||
|
||
type TagValidator struct { | ||
validators map[string]ValidatorFunc | ||
} | ||
|
||
func (tv *TagValidator) Validate(data interface{}) (ok bool, errs []ValidationError) { | ||
errs = []ValidationError{} | ||
dataVal := reflect.ValueOf(data) | ||
if dataVal.Kind() == reflect.Ptr { | ||
dataVal = dataVal.Elem() | ||
} | ||
if dataVal.Kind() != reflect.Struct { | ||
panic("Only structs can be validated") | ||
} | ||
for i := 0; i < dataVal.NumField(); i++ { | ||
fieldType := dataVal.Type().Field(i) | ||
validationTag, found := fieldType.Tag.Lookup("validation") | ||
if found { | ||
for _, v := range strings.Split(validationTag, ",") { | ||
var name, arg string = "", "" | ||
if strings.Contains(v, ":") { | ||
nameAndArgs := strings.SplitN(v, ":", 2) | ||
name = nameAndArgs[0] | ||
arg = nameAndArgs[1] | ||
} else { | ||
name = v | ||
} | ||
if validator, ok := tv.validators[name]; ok { | ||
valid, err := validator(fieldType.Name, dataVal.Field(i).Interface(), arg) | ||
if !valid { | ||
errs = append(errs, ValidationError{ | ||
FieldName: fieldType.Name, | ||
Error: err, | ||
}) | ||
} | ||
} else { | ||
panic("Unknown validator: " + name) | ||
} | ||
} | ||
} | ||
} | ||
ok = len(errs) == 0 | ||
return | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package validation | ||
|
||
type Validator interface { | ||
Validate(data interface{}) (ok bool, errs []ValidationError) | ||
} | ||
|
||
type ValidationError struct { | ||
FieldName string | ||
Error error | ||
} | ||
|
||
type ValidatorFunc func(fieldName string, value interface{}, arg string) (bool, error) | ||
|
||
func DefaultValidators() map[string]ValidatorFunc { | ||
return map[string]ValidatorFunc{ | ||
"required": required, | ||
"min": min, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package validation | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"strconv" | ||
) | ||
|
||
func required(fieldName string, value interface{}, arg string) (valid bool, err error) { | ||
if str, ok := value.(string); ok { | ||
valid = str != "" | ||
err = fmt.Errorf("A value is required") | ||
} else { | ||
err = errors.New("The required validator is for strings") | ||
} | ||
return | ||
} | ||
|
||
func min(fieldName string, value interface{}, arg string) (valid bool, err error) { | ||
minVal, err := strconv.Atoi(arg) | ||
if err != nil { | ||
panic("Invalid arguments for validator: " + arg) | ||
} | ||
err = fmt.Errorf("The minimum value is %v", minVal) | ||
if iVal, iValOk := value.(int); iValOk { | ||
valid = iVal >= minVal | ||
} else if fVal, fValOk := value.(float64); fValOk { | ||
valid = fVal >= float64(minVal) | ||
} else if strVal, strValOk := value.(string); strValOk { | ||
err = fmt.Errorf("The minimum length is %v characters", minVal) | ||
valid = len(strVal) >= minVal | ||
} else { | ||
err = errors.New("The min validator is for int, float64, and str values") | ||
} | ||
return | ||
} |