{"id":13412874,"url":"https://github.com/mholt/binding","last_synced_at":"2025-12-30T15:45:30.975Z","repository":{"id":17232254,"uuid":"20001290","full_name":"mholt/binding","owner":"mholt","description":"Reflectionless data binding for Go's net/http (not actively maintained)","archived":true,"fork":false,"pushed_at":"2018-03-28T23:47:34.000Z","size":258,"stargazers_count":796,"open_issues_count":8,"forks_count":84,"subscribers_count":31,"default_branch":"master","last_synced_at":"2024-05-18T20:46:44.134Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"http://mholt.github.io/binding","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mholt.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-05-20T23:35:14.000Z","updated_at":"2024-04-07T14:35:53.000Z","dependencies_parsed_at":"2022-08-10T16:50:09.712Z","dependency_job_id":null,"html_url":"https://github.com/mholt/binding","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mholt%2Fbinding","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mholt%2Fbinding/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mholt%2Fbinding/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mholt%2Fbinding/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mholt","download_url":"https://codeload.github.com/mholt/binding/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":221495313,"owners_count":16832458,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-30T20:01:30.462Z","updated_at":"2025-12-17T18:33:24.504Z","avatar_url":"https://github.com/mholt.png","language":"Go","funding_links":[],"categories":["Forms","Misc","Go","表单","表單","表单`表单解析与绑定`","Relational Databases","\u003cspan id=\"表单-forms\"\u003e表单 Forms\u003c/span\u003e"],"sub_categories":["Search and Analytic Databases","Advanced Console UIs","SQL 查询语句构建库","检索及分析资料库","高級控制台界面","高级控制台界面","\u003cspan id=\"高级控制台用户界面-advanced-console-uis\"\u003e高级控制台用户界面 Advanced Console UIs\u003c/span\u003e"],"readme":"[\u003cimg src=\"http://mholt.github.io/binding/resources/images/binding-sm.png\" height=\"250\" alt=\"binding is reflectionless data binding for Go\"\u003e\u003c/a\u003e](http://mholt.github.io/binding)\n\n[![GoDoc](https://godoc.org/github.com/mholt/binding?status.svg)](https://godoc.org/github.com/mholt/binding)\n\nbinding\n=======\n\nReflectionless data binding for Go's net/http\n\n\n\nFeatures\n---------\n\n- HTTP request data binding\n- Data validation (custom and built-in)\n- Error handling\n\n\n\nBenefits\n---------\n\n- Moves data binding, validation, and error handling out of your application's handler\n- Reads Content-Type to deserialize form, multipart form, and JSON data from requests\n- No middleware: just a function call\n- Usable in any setting where `net/http` is present (Negroni, gocraft/web, std lib, etc.)\n- No reflection\n\n\n\nUsage example\n--------------\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\n\t\"github.com/mholt/binding\"\n)\n\n// First define a type to hold the data\n// (If the data comes from JSON, see: http://mholt.github.io/json-to-go)\ntype ContactForm struct {\n\tUser struct {\n\t\tID int\n\t}\n\tEmail   string\n\tMessage string\n}\n\n// Then provide a field mapping (pointer receiver is vital)\nfunc (cf *ContactForm) FieldMap(req *http.Request) binding.FieldMap {\n\treturn binding.FieldMap{\n\t\t\u0026cf.User.ID: \"user_id\",\n\t\t\u0026cf.Email:   \"email\",\n\t\t\u0026cf.Message: binding.Field{\n\t\t\tForm:     \"message\",\n\t\t\tRequired: true,\n\t\t},\n\t}\n}\n\n// Now your handlers can stay clean and simple\nfunc handler(resp http.ResponseWriter, req *http.Request) {\n\tcontactForm := new(ContactForm)\n\tif errs := binding.Bind(req, contactForm); errs != nil {\n\t\thttp.Error(resp, errs.Error(), http.StatusBadRequest)\n\t\treturn\n\t}\n\tfmt.Fprintf(resp, \"From:    %d\\n\", contactForm.User.ID)\n\tfmt.Fprintf(resp, \"Message: %s\\n\", contactForm.Message)\n}\n\nfunc main() {\n\thttp.HandleFunc(\"/contact\", handler)\n\thttp.ListenAndServe(\":3000\", nil)\n}\n```\n\nMultipart/form-data usage example\n---------------------------------\n\n```go\npackage main\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"github.com/mholt/binding\"\n\t\"io\"\n\t\"log\"\n\t\"mime/multipart\"\n\t\"net/http\"\n)\n\n// We expect a multipart/form-data upload with\n// a file field named 'data'\ntype MultipartForm struct {\n\tData *multipart.FileHeader `json:\"data\"`\n}\n\nfunc (f *MultipartForm) FieldMap(req *http.Request) binding.FieldMap {\n\treturn binding.FieldMap{\n\t\t\u0026f.Data: \"data\",\n\t}\n}\n\n// Handlers are still clean and simple\nfunc handler(resp http.ResponseWriter, req *http.Request) {\n\tmultipartForm := new(MultipartForm)\n\tif errs := binding.Bind(req, multipartForm); errs != nil {\n\t\thttp.Error(resp, errs.Error(), http.StatusBadRequest)\n\t\treturn\n\t}\n\n\t// To access the file data you need to Open the file\n\t// handler and read the bytes out.\n\tvar fh io.ReadCloser\n\tvar err error\n\tif fh, err = multipartForm.Data.Open(); err != nil {\n\t\thttp.Error(resp,\n\t\t\tfmt.Sprint(\"Error opening Mime::Data %v\", err),\n\t\t\thttp.StatusInternalServerError)\n\t\treturn\n\t}\n\tdefer fh.Close()\n\tdataBytes := bytes.Buffer{}\n\tvar size int64\n\tif size, err = dataBytes.ReadFrom(fh); err != nil {\n\t\thttp.Error(resp,\n\t\t\tfmt.Sprint(\"Error reading Mime::Data %v\", err),\n\t\t\thttp.StatusInternalServerError)\n\t\treturn\n\t}\n\t// Now you have the attachment in databytes.\n\t// Maximum size is default is 10MB.\n\tlog.Printf(\"Read %v bytes with filename %s\",\n\t\tsize, multipartForm.Data.Filename)\n}\n\nfunc main() {\n\thttp.HandleFunc(\"/upload\", handler)\n\thttp.ListenAndServe(\":3000\", nil)\n}\n\n```\n\nYou can test from CLI using the excellent httpie client\n\n   `http -f POST localhost:3000/upload data@myupload`\n\nCustom data validation\n-----------------------\n\nYou may optionally have your type implement the `binding.Validator` interface to perform your own data validation. The `.Validate()` method is called after the struct is populated.\n\n```go\nfunc (cf ContactForm) Validate(req *http.Request) error {\n\tif cf.Message == \"Go needs generics\" {\n\t\treturn binding.Errors{\n\t\t\tbinding.NewError([]string{\"message\"}, \"ComplaintError\", \"Go has generics. They're called interfaces.\")\n\t\t}\n\t}\n\treturn nil\n}\n```\n\n\n\nBinding custom types\n---------------------\n\nFor types you've defined, you can bind form data to it by implementing the `Binder` interface. Here's a contrived example:\n\n\n```go\ntype MyBinder map[string]string\n\nfunc (t MyBinder) Bind(fieldName string, strVals []string) error {\n\tt[\"formData\"] = strVals[0]\n\treturn nil\n}\n```\n\nIf you can't add a method to the type, you can still specify a `Binder` func in the field spec. Here's a contrived example that binds an integer (not necessary, but you get the idea):\n\n```go\nfunc (t *MyType) FieldMap(req *http.Request) binding.FieldMap {\n\treturn binding.FieldMap{\n\t\t\"a-key\": binding.Field{\n\t\t\tForm: \"number\",\n\t\t\tBinder: func(fieldName string, formVals []string) error {\n\t\t\t\tval, err := strconv.Atoi(formVals[0])\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn binding.Errors{binding.NewError([]string{fieldName}, binding.DeserializationError, err.Error())}\n\t\t\t\t}\n\t\t\t\tt.SomeNumber = val\n\t\t\t\treturn nil\n\t\t\t},\n\t\t},\n\t}\n}\n```\n\nThe `Errors` type has a convenience method, `Add`, which you can use to append to the slice if you prefer.\n\nSupported types (forms)\n------------------------\n\nThe following types are supported in form deserialization by default. (JSON requests are delegated to `encoding/json`.)\n\n- uint, \\*uint, []uint, uint8, \\*uint8, []uint8, uint16, \\*uint16, []uint16, uint32, \\*uint32, []uint32, uint64, \\*uint64, []uint64\n- int, \\*int, []int, int8, \\*int8, []int8, int16, \\*int16, []int16, int32, \\*int32, []int32, int64, \\*int64, []int64\n- float32, \\*float32, []float32, float64, \\*float64, []float64\n- bool, \\*bool, []bool\n- string, \\*string, []string\n- time.Time, \\*time.Time, []time.Time\n- \\*multipart.FileHeader, []\\*multipart.FileHeader\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmholt%2Fbinding","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmholt%2Fbinding","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmholt%2Fbinding/lists"}