go-macaron / binding Goto Github PK
View Code? Open in Web Editor NEWPackage binding is a middleware that provides request data binding and validation for Macaron.
License: Apache License 2.0
Package binding is a middleware that provides request data binding and validation for Macaron.
License: Apache License 2.0
I would like to be able to apply validation rules to each item in a slice.
eg
type MyStruct struct {
Id int
MyNumbers []int `json:"my_numbers" binding:"range(1, 10)"`
}
The following should then fail validation as one of the items in the list is outside of the range.
{
"id": 1,
"my_numbers": [1,2,3,4,100]
}
This would require replacing MinSize and MaxSize from being applied to slice lengths, so that rules can instead be used for length of each item in a slice of strings. New rules specific to slice length would then need to be introduced.
Validation does not currently work for slices of structs.
eg.
type Person struct {
FirstName string `json:"first_name" binding:"Required"`
LastName string `json:"last_name" binding:"Required"`
}
type Group struct {
Name string `json:"name" binding:"Required"`
People []*Person `json:"people"`
}
sending a payload with the following is currently validated as correct, though it should raise an issue for the missing FirstName
{
"name": "Group1",
"people": [
{"first_name": "anthony", "last_name": "woods"},
{"first_name": "", "last_name": "invalid"}
]
}
type ConsoleForm struct {
Operation string `form:"operation"`
Command string `form:"command"`
Args []string `form:"args"`
}
发送参数operation=test&command=run&args[]=1&args[]=2
args无法映射到struct中
type ContactForm struct {
Email string `form:"email"`
}
We hope that "Email" property in Form struct can be automap to form:email
, We don't need to type 'form:email' by default.
This big feature should be used to simplify a lot of work.
Validate 和Error 对指针的处理方式不统一, 前者接受指针类型而后者不接受
// 这样的方法不被识别
func (form *VariantForm) Error(ctx *macaron.Context, errs binding.Errors) {}
// macaron 识别这样的方法
func (form *VariantForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {}
color *string `binding:"OmitEmpty;Size(7)"`
$ go get github.com/go-macaron/binding
../gopkg.in/macaron.v1/context.go:34:2: case-insensitive import collision: "github.com/unknwon/com" and "github.com/Unknwon/com"
Errors not modified in custom rules since the parameter is passed as a value
binding.go
line 348
field.Tag.Get("mytag")
In Go's net/http ParseForm() method, the following checks are done:
https://github.com/golang/go/blob/700e969d5b23732179ea86cfe67e8d1a0a1cc10a/src/net/http/request.go#L1176
// For other HTTP methods, or when the Content-Type is not
// application/x-www-form-urlencoded, the request Body is not read, and
// r.PostForm is initialized to a non-nil, empty value.
//
// If the request Body's size has not already been limited by MaxBytesReader,
// the size is capped at 10MB.
Should the similar checks in Request body be done in Json() method, too?
Have been trying to set up a JSON Post Request using the Macaron Example
type ContactForm struct {
Name string `json:"name" binding:"Required"`
}
r.Post("/api/journals", binding.Bind(ContactForm{}), func(contact ContactForm) string {
return fmt.Sprintf("Name: %s", contact.Name)
})
However calling this with the following JSON request from within jquery:
$.ajax({
type: 'POST',
url: '/api/journals',
data: '{"name":"hello"}',
success: function(data) { alert('data: ' + data); },
contentType: "application/json",
dataType: 'json'
});
returns the following error when the request is sent:
*Called inject.InterfaceOf with a value that is not a pointer to an interface. (MyInterface)(nil)
with the following stack trace:
/home/sean/go/pkg/mod/github.com/go-macaron/[email protected]/inject.go:113 (0x792c6d)
InterfaceOf: panic("Called inject.InterfaceOf with a value that is not a pointer to an interface. (*MyInterface)(nil)")
/home/sean/go/pkg/mod/github.com/go-macaron/[email protected]/inject.go:222 (0x793a7e)
(*injector).MapTo: i.values[InterfaceOf(ifacePtr)] = reflect.ValueOf(val)
/home/sean/go/pkg/mod/github.com/go-macaron/[email protected]/binding.go:752 (0x8258c4)
validateAndMap: ctx.MapTo(obj.Elem().Interface(), ifacePtr[0])
/home/sean/go/pkg/mod/github.com/go-macaron/[email protected]/binding.go:210 (0x826edb)
Json.func1: validateAndMap(jsonStruct, ctx, errors, ifacePtr...)
/usr/local/go/src/reflect/value.go:475 (0x49af66)
Value.call: call(frametype, fn, args, uint32(frametype.size), uint32(retOffset))
/usr/local/go/src/reflect/value.go:336 (0x49a458)
Value.Call: return v.call("Call", in)
/home/sean/go/pkg/mod/github.com/go-macaron/[email protected]/inject.go:177 (0x793379)
(*injector).callInvoke: return reflect.ValueOf(f).Call(in), nil
/home/sean/go/pkg/mod/github.com/go-macaron/[email protected]/inject.go:137 (0x792d4a)
(*injector).Invoke: return inj.callInvoke(f, t, t.NumIn())
/home/sean/go/pkg/mod/github.com/go-macaron/[email protected]/binding.go:45 (0x81e5be)
bind: ctx.Invoke(Json(obj, ifacePtr...))
/home/sean/go/pkg/mod/github.com/go-macaron/[email protected]/binding.go:104 (0x8260c5)
Bind.func1: bind(ctx, obj, ifacePtr...)
/home/sean/go/pkg/mod/gopkg.in/[email protected]/context.go:79 (0x7c5a72)
ContextInvoker.Invoke: invoke(params[0].(*Context))
/home/sean/go/pkg/mod/github.com/go-macaron/[email protected]/inject.go:157 (0x793094)
(*injector).fastInvoke: return f.Invoke(in)
/home/sean/go/pkg/mod/github.com/go-macaron/[email protected]/inject.go:135 (0x792e39)
(*injector).Invoke: return inj.fastInvoke(v, t, t.NumIn())
/home/sean/go/pkg/mod/gopkg.in/[email protected]/context.go:121 (0x7c5bfc)
(*Context).run: vals, err := c.Invoke(c.handler())
/home/sean/go/pkg/mod/gopkg.in/[email protected]/context.go:112 (0x7d6da5)
(*Context).Next: c.run()
/home/sean/go/pkg/mod/gopkg.in/[email protected]/recovery.go:161 (0x7d6d98)
Recovery.func1: c.Next()
/home/sean/go/pkg/mod/gopkg.in/[email protected]/logger.go:40 (0x7c97b7)
LoggerInvoker.Invoke: invoke(params[0].(*Context), params[1].(*log.Logger))
/home/sean/go/pkg/mod/github.com/go-macaron/[email protected]/inject.go:157 (0x793094)
(*injector).fastInvoke: return f.Invoke(in)
/home/sean/go/pkg/mod/github.com/go-macaron/[email protected]/inject.go:135 (0x792e39)
(*injector).Invoke: return inj.fastInvoke(v, t, t.NumIn())
/home/sean/go/pkg/mod/gopkg.in/[email protected]/context.go:121 (0x7c5bfc)
(*Context).run: vals, err := c.Invoke(c.handler())
/home/sean/go/pkg/mod/gopkg.in/[email protected]/router.go:187 (0x7d7ff0)
(*Router).Handle.func1: c.run()
/home/sean/go/pkg/mod/gopkg.in/[email protected]/router.go:303 (0x7d2125)
(*Router).ServeHTTP: h(rw, req, p)
/home/sean/go/pkg/mod/gopkg.in/[email protected]/macaron.go:220 (0x7cab0d)
(*Macaron).ServeHTTP: m.Router.ServeHTTP(rw, req)
/usr/local/go/src/net/http/server.go:2843 (0x767bc2)
serverHandler.ServeHTTP: handler.ServeHTTP(rw, req)
/usr/local/go/src/net/http/server.go:1925 (0x7633cc)
(*conn).serve: serverHandler{c.server}.ServeHTTP(w, w.req)
/usr/local/go/src/runtime/asm_amd64.s:1374 (0x470480)
goexit: BYTE $0x90 // NOP
This works:
type UploadForm struct {
Title string `form:"Title"`
TextUpload *multipart.FileHeader `form:"TextUpload"`
}
But this produces an empty struct:
type UploadForm struct {
Title string
TextUpload *multipart.FileHeader
}
比如,我想在错误处理中实现这样的逻辑:
flash.Error(err.Error())
目前的方法不能实现
ErrorHandler interface {
// Error handles validation errors with custom process.
Error(*macaron.Context, Errors)
}
可否修改为 Error(...interface{})
Seee #32
Hi @unknwon .
I need to do file uploading (import formated data to a config database) and I have been testing with your example here (https://go-macaron.com/docs/middlewares/binding)
My code is exactly this, and the export API is working fine.
package webui
import (
"bytes"
"encoding/json"
"github.com/go-macaron/binding"
"github.com/toni-moreno/snmpcollector/pkg/data/impexp"
"gopkg.in/macaron.v1"
"io/ioutil"
"mime/multipart"
"os"
)
type UploadForm struct {
Title string `form:"Title"`
TextUpload *multipart.FileHeader `form:"TextUpload"`
}
func NewImportExport(m *macaron.Macaron) error {
bind := binding.Bind
m.Group("/api/cfg/export", func() {
m.Get("/:objtype/:id", reqSignedIn, ExportMeasurementGroup)
m.Post("/:objtype/:id", reqSignedIn, bind(impexp.ExportInfo{}), ExportMeasurementGroupFile)
})
m.Group("/api/cfg/import", func() {
m.Post("/", reqSignedIn, binding.MultipartForm(UploadForm{}), UploadHandler)
})
return nil
}
/****************/
/*IMPORT*/
/*****************/
func UploadHandler( /*ctx *Context,*/ uf UploadForm) {
if (UploadForm{}) == uf {
log.Error("Error no data in expected struct")
return
}
log.Debugf("Uploaded file : %s", uf.Title)
log.Debugf("Uploaded File : %+v", uf)
file, err := uf.TextUpload.Open()
if err != nil {
log.Warningf("Error on Open Uploaded File: %s", err)
// ctx.JSON(404, err.Error())
}
buf := new(bytes.Buffer)
buf.ReadFrom(file)
s := buf.String()
log.Debug("DATA: %s", s)
}
/****************/
/*EXPORT*/
/****************/
func Export(ctx *Context) {
....
//more code working ok here
}
func ExportFile(ctx *Context, info impexp.ExportInfo) {
....
//more code working ok here
}
This is the ouput when a upload have been done
ERRO[2017-02-18 08:03:40] Error no data in expected struct
IT seems like no data is sent to the UploadForm struct.
I've got HTTP real data with wireshark and this i exactly what we are sending to the macaron handler.
POST /api/cfg/import HTTP/1.1
Host: localhost:8090
Connection: keep-alive
Content-Length: 7275
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36
Origin: chrome-extension://apcedakaoficjlofohhcmkkljehnmebp
Content-Type: multipart/form-data
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: es,ca;q=0.8,en;q=0.6,en-US;q=0.4
Cookie: MacaronSession=127714bd60ec66e0; snmpcollector-sess-lan=9da867813c641552
------WebKitFormBoundaryfin1vPdBlN5ZQrnv
Content-Disposition: form-data; name="fileUpload1"; filename="blob"
Content-Type: text/plain
{"Info":{"FileName":"","Description":"","Author":"","Tags":""},"AgentVersion":"","ExportVersion":"1.0","CreationDate":"2017-02-18T06:37:28.159948966+01:00","Objects":[{"ObjectTypeID":"influxcfg","ObjectID":"default","ObjectPtr":{"ID":"default","Host":"127.0.0.1","Port":8086,"DB":"snmp","User":"snmpuser","Password":"snmppass","Retention":"autogen","Timeout":5,"UserAgent":"snmpcollector_agent_00","Description":""}},{"ObjectTypeID":"oidconditioncfg","ObjectID":"test_multiple","ObjectPtr":{"ID":"test_multiple","IsMultiple":true,"OIDCond":"filter_port_if_status_up \u0026\u0026 filter_port_if_name_match_eth0","CondType":"","CondValue":"","Description":"adf"}},{"ObjectTypeID":"measfiltercfg","ObjectID":"filter_multiple_test","ObjectPtr":{"ID":"filter_multiple_test","IDMeasurementCfg":"linux_ports","FType":"OIDCondition","FilterName":"test_multiple","EnableAlias":false,"Description":"test"}},{"ObjectTypeID":"snmpmetriccfg","ObjectID":"ifOutErrors","ObjectPtr":{"ID":"ifOutErrors","FieldName":"ifOutErrors","Description":"","BaseOID":".1.3.6.1.2.1.2.2.1.20","DataSrcType":"COUNTER32","GetRate":false,"Scale":0,"Shift":0,"IsTag":false,"ExtraData":""}},{"ObjectTypeID":"snmpmetriccfg","ObjectID":"ifInErrors","ObjectPtr":{"ID":"ifInErrors","FieldName":"ifInErrors","Description":"","BaseOID":".1.3.6.1.2.1.2.2.1.14","DataSrcType":"COUNTER32","GetRate":false,"Scale":0,"Shift":0,"IsTag":false,"ExtraData":""}},{"ObjectTypeID":"snmpmetriccfg","ObjectID":"evaluated_prefix_for_ifname","ObjectPtr":{"ID":"evaluated_prefix_for_ifname","FieldName":"prefixed_ifname","Description":"Add Prefix for ifname","BaseOID":"","DataSrcType":"STRINGEVAL","GetRate":false,"Scale":0,"Shift":0,"IsTag":true,"ExtraData":"'myprefix_'+IfName"}},{"ObjectTypeID":"snmpmetriccfg","ObjectID":"ifName","ObjectPtr":{"ID":"ifName","FieldName":"IfName","Description":"Tags","BaseOID":".1.3.6.1.2.1.31.1.1.1.1","DataSrcType":"OCTETSTRING","GetRate":false,"Scale":0,"Shift":0,"IsTag":true,"ExtraData":""}},{"ObjectTypeID":"snmpmetriccfg","ObjectID":"IfH_out_percent_utilization","ObjectPtr":{"ID":"IfH_out_percent_utilization","FieldName":"out_percent_utilization","Description":"% utlization out","BaseOID":"","DataSrcType":"STRINGEVAL","GetRate":false,"Scale":0,"Shift":0,"IsTag":false,"ExtraData":"(out_octets * 8) / ( ifhspeed * 10000)"}},{"ObjectTypeID":"snmpmetriccfg","ObjectID":"ifH_in_percent_utilization","ObjectPtr":{"ID":"ifH_in_percent_utilization","FieldName":"in_percent_utilization","Description":"% utlization in","BaseOID":"","DataSrcType":"STRINGEVAL","GetRate":false,"Scale":0,"Shift":0,"IsTag":false,"ExtraData":"(in_octets * 8 ) / (ifhspeed * 10000)"}},{"ObjectTypeID":"snmpmetriccfg","ObjectID":"ifHighSpeed","ObjectPtr":{"ID":"ifHighSpeed","FieldName":"ifhspeed","Description":"Bytes Out - 64-bit Counters","BaseOID":".1.3.6.1.2.1.31.1.1.1.15","DataSrcType":"Gauge32","GetRate":false,"Scale":0,"Shift":0,"IsTag":false,"ExtraData":"user + system + idle"}},{"ObjectTypeID":"snmpmetriccfg","ObjectID":"ifHCOutOctets","ObjectPtr":{"ID":"ifHCOutOctets","FieldName":"out_octets","Description":"Bytes Out - 64-bit Counters","BaseOID":".1.3.6.1.2.1.31.1.1.1.10","DataSrcType":"COUNTER64","GetRate":true,"Scale":0,"Shift":0,"IsTag":false,"ExtraData":"user + system + idle"}},{"ObjectTypeID":"snmpmetriccfg","ObjectID":"ifHCInOctets","ObjectPtr":{"ID":"ifHCInOctets","FieldName":"in_octets","Description":"Bytes In - 64-bit Counters","BaseOID":".1.3.6.1.2.1.31.1.1.1.6","DataSrcType":"COUNTER64","GetRate":true,"Scale":0,"Shift":0,"IsTag":false,"ExtraData":"user + system + idle"}},{"ObjectTypeID":"measurementcfg","ObjectID":"linux_ports","ObjectPtr":{"ID":"linux_ports","Name":"linux_ports","GetMode":"indexed","IndexOID":".1.3.6.1.2.1.31.1.1.1.1","TagOID":"","IndexTag":"portName","IndexTagFormat":"","IndexAsValue":false,"Fields":[{"ID":"ifHCInOctets","Report":1},{"ID":"ifHCOutOctets","Report":1},{"ID":"ifHighSpeed","Report":1},{"ID":"ifH_in_percent_utilization","Report":1},{"ID":"IfH_out_percent_utilization","Report":1},{"ID":"ifName","Report":1},{"ID":"evaluated_prefix_for_ifname","Report":1},{"ID":"ifInErrors","Report":2},{"ID":"ifOutErrors","Report":2}],"Description":""}},{"ObjectTypeID":"oidconditioncfg","ObjectID":"filter_port_if_status_up","ObjectPtr":{"ID":"filter_port_if_status_up","IsMultiple":false,"OIDCond":".1.3.6.1.2.1.2.2.1.8","CondType":"neq","CondValue":"1","Description":""}},{"ObjectTypeID":"snmpmetriccfg","ObjectID":"test_cond_oid","ObjectPtr":{"ID":"test_cond_oid","FieldName":"oidtest","Description":"afaf","BaseOID":"","DataSrcType":"CONDITIONEVAL","GetRate":false,"Scale":0,"Shift":0,"IsTag":false,"ExtraData":"filter_port_if_status_up"}},{"ObjectTypeID":"snmpmetriccfg","ObjectID":"Linux_user_CPU_total","ObjectPtr":{"ID":"Linux_user_CPU_total","FieldName":"cpu_total","Description":"calculo de CPU total","BaseOID":"","DataSrcType":"STRINGEVAL","GetRate":false,"Scale":0,"Shift":0,"IsTag":false,"ExtraData":"user + system + idle"}},{"ObjectTypeID":"snmpmetriccfg","ObjectID":"Linux_idle_CPU_percent","ObjectPtr":{"ID":"Linux_idle_CPU_percent","FieldName":"idle","Description":"percentage of Idle CPU time","BaseOID":".1.3.6.1.4.1.2021.11.11.0","DataSrcType":"INTEGER","GetRate":false,"Scale":0,"Shift":0,"IsTag":false,"ExtraData":""}},{"ObjectTypeID":"snmpmetriccfg","ObjectID":"Linux_system_CPU_percent","ObjectPtr":{"ID":"Linux_system_CPU_percent","FieldName":"system","Description":"percentage of system CPU time","BaseOID":".1.3.6.1.4.1.2021.11.10.0","DataSrcType":"INTEGER","GetRate":false,"Scale":0,"Shift":0,"IsTag":false,"ExtraData":""}},{"ObjectTypeID":"snmpmetriccfg","ObjectID":"Linux_user_CPU_percent","ObjectPtr":{"ID":"Linux_user_CPU_percent","FieldName":"user","Description":"percentage of user CPU time","BaseOID":".1.3.6.1.4.1.2021.11.9.0","DataSrcType":"INTEGER","GetRate":false,"Scale":0,"Shift":0,"IsTag":false,"ExtraData":""}},{"ObjectTypeID":"measurementcfg","ObjectID":"linux_cpu","ObjectPtr":{"ID":"linux_cpu","Name":"linux.cpu","GetMode":"value","IndexOID":"","TagOID":"","IndexTag":"","IndexTagFormat":"","IndexAsValue":false,"Fields":[{"ID":"Linux_user_CPU_percent","Report":1},{"ID":"Linux_system_CPU_percent","Report":1},{"ID":"Linux_idle_CPU_percent","Report":1},{"ID":"Linux_user_CPU_total","Report":1},{"ID":"test_cond_oid","Report":1}],"Description":""}},{"ObjectTypeID":"measgroupscfg","ObjectID":"Linux","ObjectPtr":{"ID":"Linux","Measurements":["linux_cpu","linux_ports"],"Description":""}},{"ObjectTypeID":"snmpdevicecfg","ObjectID":"hostsnmpv3a","ObjectPtr":{"ID":"hostsnmpv3a","Host":"hostsnmpv3a","Port":161,"Retries":5,"Timeout":20,"Repeat":0,"Active":true,"SnmpVersion":"3","Community":"public","V3SecLevel":"NoAuthNoPriv","V3AuthUser":"v3usernoauth","V3AuthPass":"v3passauth","V3AuthProt":"MD5","V3PrivPass":"","V3PrivProt":"","DisableBulk":false,"Freq":30,"UpdateFltFreq":1,"OutDB":"default","LogLevel":"debug","LogFile":"","SnmpDebug":false,"DeviceTagName":"router","DeviceTagValue":"id","ExtraTags":["tagA=4","tagB=5","tagC=6"],"Description":"","MeasurementGroups":["Linux"],"MeasFilters":["filter_multiple_test"]}}]}
There is no any warning error , and I've done exact as you did in the above example. I will be happy if you can help me.
Thank you very much
Hi,
what's the proper pattern to validate related entities?
A minimal example of what I would want to achieve:
type ProfileForm struct {
Bio string `binding:"Required"`
}
type UserForm struct {
Username string `binding:"Required;MaxSize(30)"`
Email string `binding:"Required;Email"`
Profile *ProfileForm
}
<form ...>
<input type="text" name="username">
<input type="email" name="email">
<input type="text" name="profile.bio">
</form>
Thank you!
I've working with macaron/binding to validate input data from a API REST . But also could import data in JSON format from a file with lots of json formated objects of diferent type (https://github.com/toni-moreno/snmpcollector/blob/master/pkg/data/impexp/import.go#L91) . In this use case I would like also validate as input in the same way than if inserted via API REST and validated with macaron/binding.
Would be interesting if we can use the same binding.Validate method decoupled of the http request.
hello,
a GET request has no body, yet the HTTP RFC allows to set content-type on GET requests
(https://tools.ietf.org/html/rfc7231#section-3.1.1.5)
in this case it simply does not apply.
We see this commonly with customers that switch requests between GET and POST, but leaving the content-type header specified. For GET requests, it should simply have no effect.
However, the binding middleware will, upon detection of a non-zero content-type header, switch to trying to use the json decoder on the body, even when there is no body such as in a GET request.
This results in errors such as grafana/metrictank#1465 :
http 'http://localhost:6060/render?target=stats.docker-env.response.200&from=-6s' 'Content-Type:application/json'
HTTP/1.1 422 Unprocessable Entity
Content-Encoding: gzip
Content-Length: 96
Content-Type: application/json; charset=utf-8
Date: Wed, 18 Sep 2019 14:29:00 GMT
Vary: Accept-Encoding
[
{
"classification": "RequiredError",
"fieldNames": [
"target"
],
"message": "Required"
}
]
the correct behavior would be to process the request based on its method (e.g. GET requests use GET parameters)
It seems that the JSON part supports having multiple "sub-forms" (slices of structs ?).
I would like to have something like (not File Uploads here, plain HTML input forms):
type ItemFile struct {
Filename string
Content string `binding:"Required;Text"`
}
type Item struct {
Description string `binding:"Required"`
Files []ItemFile // ?
}
Is it possible to do that ? how should I use the "ItemFile" and naming in html for fields names ?
When I'm defining an enum validation , I would like also give a default value.
SnmpDebug bool `xorm:"snmpdebug" binding:"Default(0);In(0,1)"`
but when omitting the "snmpdebug" parameter the following error is shown
[
{
"fieldNames": [
"SnmpDebug"
],
"classification": "InError",
"message": "In"
}
]
But I've set the Default value as 0, so validation has been done erroneously.
The custom validation on the example is not working.
func init() {
// Min(1) set de minimun integer value
binding.AddRule(&binding.Rule{
IsMatch: func(rule string) bool {
return strings.HasPrefix(rule, "Min(")
},
IsValid: func(errs binding.Errors, name string, v interface{}) (bool, binding.Errors) {
num, ok := v.(int)
if !ok {
return false, errs
}
min, _ := strconv.Atoi(rule[4 : len(rule)-1])
if num < min {
errs.Add([]string{name}, "MinimumValue", "Value is too small")
return false, errs
}
return true, errs
},
})
}
On compile we have this error.
pkg/webui/extravalidations.go:22: undefined: rule
There is no "rule" variable defined inside IsValid function. But I need build custom rules similars to the Min(10) example . How can I access to the rule definition in the IsValid function?
Hey! i use macaron with binding and after update packages i see error:
go: finding github.com/Unknwon/com latest
go: github.com/Unknwon/[email protected]: parsing go.mod: unexpected module path "github.com/unknwon/com"
go: error loading module requirements
Aand... i see two packages:
Unknwon and unknwon... and error :(
常见的一种开发场景是作为api服务,有统一的数据返回格式,比如:
{"code":0,"message":"成功","data":{}}
binding默认的errorHandler
返回的数据格式不符合要求,现在也不能修改默认的errorHandler
实现方法。binding虽然提供了两种自定义错误处理机制,但一种是需要为每一个绑定的struct定义Error(ctx *macaron.Context, errs binding.Errors)
方法,比较麻烦,另一种是使用 binding.BindIgnErr
函数来忽略对错误的自动处理,然后再定义一个中间件统一处理binding的错误,同样需要在调用binding.BindIgnErr
中间件之后再加上这个自定义中间件的调用,也有点麻烦。
所以,请问是否可以增加自定义默认的errorHandler的功能?
最后说一句,非常喜欢macaron和gogs,感谢Unkown为开源社区的无私奉献。
https://github.com/go-macaron/binding/blob/master/binding.go#L564
setWithProperType takes the Errors slice and appends to it, but never passes it back. As the slice is passed by value and not by reference the errors appended within setWithProperType are not visible to the caller.
Is it possible to change the pattern binding.go#L247 to "[^\d\p{L}-_\.]" ? (also L246)
I want to use Unicode names for repositories in gogs.
192.168.0.1这种纯ip格式可以通过binding Url校验
I've looked for example on validate a a binded struct like that, (https://github.com/toni-moreno/snmpcollector/blob/master/pkg/config/sqlconfig.go#L6-L46).
This is all what I've found (https://gowalker.org/github.com/go-macaron/binding#Validate). Could you show me an example on how should i use validation ,type validation with regex , by example ?
Thank you very much
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.