feat: complete contract-driven business development and MySQL integration
This commit is contained in:
105
contract/policy.go
Normal file
105
contract/policy.go
Normal file
@@ -0,0 +1,105 @@
|
||||
package contract
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// RequestPolicy describes protocol strictness, independently of business
|
||||
// fields. Empty values inherit App defaults, then the historical binder policy.
|
||||
type RequestPolicy struct {
|
||||
UnknownQuery string `json:"unknownQuery"` // ignore or reject
|
||||
UTF8 string `json:"utf8"` // replace or reject
|
||||
Charset string `json:"charset"` // any or utf8
|
||||
Body string `json:"body"` // auto, required, optional, none, emptyObject
|
||||
}
|
||||
|
||||
func (p RequestPolicy) Validate() error {
|
||||
for _, item := range []struct {
|
||||
name, value string
|
||||
allowed []string
|
||||
}{
|
||||
{"unknownQuery", p.UnknownQuery, []string{"ignore", "reject"}},
|
||||
{"utf8", p.UTF8, []string{"replace", "reject"}},
|
||||
{"charset", p.Charset, []string{"any", "utf8"}},
|
||||
{"body", p.Body, []string{"auto", "required", "optional", "none", "emptyObject"}},
|
||||
} {
|
||||
if item.value == "" {
|
||||
continue
|
||||
}
|
||||
ok := false
|
||||
for _, value := range item.allowed {
|
||||
ok = ok || value == item.value
|
||||
}
|
||||
if !ok {
|
||||
return fmt.Errorf("request policy: invalid %s", item.name)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func MergePolicy(base, override RequestPolicy) RequestPolicy {
|
||||
if override.UnknownQuery != "" {
|
||||
base.UnknownQuery = override.UnknownQuery
|
||||
}
|
||||
if override.UTF8 != "" {
|
||||
base.UTF8 = override.UTF8
|
||||
}
|
||||
if override.Charset != "" {
|
||||
base.Charset = override.Charset
|
||||
}
|
||||
if override.Body != "" {
|
||||
base.Body = override.Body
|
||||
}
|
||||
return base
|
||||
}
|
||||
|
||||
type policyKey struct{}
|
||||
|
||||
func WithRequestPolicy(ctx context.Context, p RequestPolicy) context.Context {
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
return context.WithValue(ctx, policyKey{}, p)
|
||||
}
|
||||
func RequestPolicyFromContext(ctx context.Context) RequestPolicy {
|
||||
if ctx == nil {
|
||||
return RequestPolicy{}
|
||||
}
|
||||
p, _ := ctx.Value(policyKey{}).(RequestPolicy)
|
||||
return p
|
||||
}
|
||||
|
||||
// utf8Reader checks original bytes in one pass, including runes crossing Read
|
||||
// boundaries. It never replaces invalid bytes or buffers the complete body.
|
||||
type utf8Reader struct {
|
||||
r io.Reader
|
||||
pending [4]byte
|
||||
n int
|
||||
}
|
||||
|
||||
var errUTF8 = errors.New("invalid UTF-8 in JSON body")
|
||||
|
||||
func (r *utf8Reader) Read(p []byte) (int, error) {
|
||||
n, err := r.r.Read(p)
|
||||
for _, b := range p[:n] {
|
||||
if r.n == 0 && b < utf8.RuneSelf {
|
||||
continue
|
||||
}
|
||||
r.pending[r.n] = b
|
||||
r.n++
|
||||
if utf8.FullRune(r.pending[:r.n]) {
|
||||
ch, size := utf8.DecodeRune(r.pending[:r.n])
|
||||
if ch == utf8.RuneError && size == 1 {
|
||||
return n, errUTF8
|
||||
}
|
||||
r.n = 0
|
||||
}
|
||||
}
|
||||
if err == io.EOF && r.n != 0 {
|
||||
return n, errUTF8
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
Reference in New Issue
Block a user