ajg / form Goto Github PK
View Code? Open in Web Editor NEWA Form Encoding & Decoding Package for Go
License: BSD 3-Clause "New" or "Revised" License
A Form Encoding & Decoding Package for Go
License: BSD 3-Clause "New" or "Revised" License
I always like to check my form fields to see if their values are convertible to needed types and if not then inform the user about invalid fields. form produces *errors.errorString and this is not useful when generating error responses for this purpose.
What about defining a new error type such as below to give more control over them
e.g.
type Error []Field
func (e Error) Error() string {
return "todo"
}
type Field struct {
Name string // tag name e.g. time from form:"time"
Message string // original error message
Type reflect.Type // point out the expected type so we can generate even more graceful error messages by checking this e.g. "time must be a UTC date"
SliceIndex int // point out the index if an invalid Type placed in a slice except []byte
}
A dummy usage:
type Post struct {
Time time.Time `form:"time"`
}
var p Post
err := form.DecodeValues(&p, url.Values{"time", []string{"AnInvalidDate"}})
if (err.(form.Error))[0].Type == form.TimeType {
// so I can response a nice error message to user like:
// HTTP 400
// {"message": "Invalid Form Data", fields: {"time": "must be UTC formatted"}}
}
I'd like to implement this if you'll consider accepting the PR
Is possible parse anonimous fields without prefix if it's tag ommited or empty string (like json)?
Example:
package main
import (
"encoding/json"
"fmt"
"github.com/ajg/form"
)
type T1 struct {
X int `json:"x,omitempty" form:"x,omitempty"`
Y int `json:"y,omitempty" form:"y,omitempty"`
}
type T2 struct {
*T1 `json:"" form:""`
A int `json:"a,omitempty" form:"a,omitempty"`
B int `json:"b,omitempty" form:"b,omitempty"`
}
type T3 struct {
*T1 `json:"t1" form:"t1"`
A int `json:"a,omitempty" form:"a,omitempty"`
B int `json:"b,omitempty" form:"b,omitempty"`
}
func main() {
x := T2{&T1{1, 2}, 3, 4}
s, _ := json.Marshal(x)
s2, _ := form.EncodeToString(x)
fmt.Println(string(s), s2)
x2 := T3{&T1{1, 2}, 3, 4}
s, _ = json.Marshal(x2)
s2, _ = form.EncodeToString(x2)
fmt.Println(string(s), s2)
}
Output:
{"x":1,"y":2,"a":3,"b":4} T1.x=1&T1.y=2&a=3&b=4
{"t1":{"x":1,"y":2},"a":3,"b":4} a=3&b=4&t1.x=1&t1.y=2
Second result is ok, but in first best result is "x=1&y=2&a=3&b=4"
Hi,
I have this:
type Film struct {
Genres []string
Topics []string
}
I am obliged to indicate the index of slice in the input form:
Is possible NOT indicate the index and only leave "Topics"? With gorilla/schema it is possible, but gorilla/schema, in general, is much more limited...
Thanks!
Best,
Emilio
It would be nice to have the ability to encode files in the form data.
For example, we could have a special struct tag that indicates this field represents a file path.
Hi!
In the app that I created found a problem. I use a lot of this library, it allow so much fields but not anonymous fields. For example, i have in my project a two structs:
type Length struct {
Title map[string]string `json:"title"`
Release map[string]string `json:"release,omitempty"`
Genres Genre `json:"genres"`
Topics []string `json:"genres"`
}
type FilmBasedEpisodes struct {
Length
Channel Channel
Seasons []Season
}
But when I want do this:
....
r.ParseMultipartForm(maxMemory)
dataFormHTML := r.Form
film := FilmBasedEpisodes{}
err := form.DecodeValues(&film, dataFormHTML)
....
The content of dataFormHTML
is, for example, like this:
"Title.US":"Six Feet Under", "Title.ES": "A dos metros bajo tierra", "Channel.Original":"HBO"
The error from ajg.form
in go compiler is : Title doesn't exist in struct main.FilmBasedEpisodes
I understand that the library not is compatible with anonymous fields, right? If I wrong, then, how I do it?
Best,
Emilio
A particular third-party API I wish to contact used periods in the key names. I have the keys in my structs labelled as such:
type whatever struct {
CleanupReturnCleaned bool form:"cleanup.returnCleaned"
}
but when I run form.EncodeToString() I get:
cleanup%5C.returnCleaned=true
I've read the documentation in the Readme and have tried escaping the period with a back slash but that doesnt seem to have the desired effect. Am I misreading the readme, or is this not possible using ajg/form?
Cheers!
Due to the intentional special casing of periods within form, any API which uses periods in a form field cannot be interacted with without some hacks. For example, the fastly API utilizes periods in their form fields: https://docs.fastly.com/api/config#settings_9740ff4ac0c1777f455274c4850ece23
The go-fastly
library attempted to work around this by manually ripping out the generated escapes, but that leads to other things being unintentionally ripped out, as noted in this case: fastly/go-fastly#11
Is there any workaround for interacting with forms which have periods in their form keys?
The implementation of isEmptyValue makes encoding certain structs a bit non-intuitive. Similar to the discussion here, I am trying to encode a 0 in a request.
I would expect the following code to result in "num=0&name=test" but instead I end up with "num=&name=test" even though my value does "exist".
package main
import (
"bytes"
"github.com/ajg/form"
"log"
)
type Thing struct {
String string `form:"name,omitempty"`
Integer *uint `form:"num,omitempty"`
}
func main() {
a_num := uint(0)
u := Thing{"test", &a_num}
buf := new(bytes.Buffer)
if err := form.NewEncoder(buf).DelimitWith('|').Encode(u); err != nil {
log.Printf("[ERR] %s", err)
}
body := buf.String()
log.Printf("[DEBUG] %s", body)
}
The library fails to decode a struct if field is missing.
E.g. I do have a struct
type TokenExchange struct {
Type string `form:"grant_type"`
Code string `form:"code"`
}
The input string to parse
grant_type=authorization_code&code=xxx&client_id=xxx
The library fails to decode string with an error
client_id doesn't exist in main.TokenExchange
I would expect that library skips client_id
.
It looks like the tests are failing on Go 1.17 and up around parsing semicolons.
There are a couple ways to resolve - one of them is using the Go handler: https://pkg.go.dev/net/http#AllowQuerySemicolons
Other suggestions: https://golangshowcase.com/question/invalid-semicolon-separator-in-query-after-go-1-17
➜ form git:(master) ✗ go test ./...
--- FAIL: TestDecodeString (0.00s)
decode_test.go:17: DecodeString(";C=42%2B6.6i;A.0=x;M.Bar=8;F=6.6;A.1=y;R=8734;A.2=z;Zs.0.Qp=33_44;B=true;M.Foo=7;T=2013-10-01T07:05:34.000000088Z;E.Bytes1=%00%01%02;Bytes2=%03%04%05;Zs.0.Q=11_22;Zs.0.Z=2006-12-01;M.Qux=9;life=42;S=Hello,+there.;P\\.D\\\\Q\\.B.A=P/D;P\\.D\\\\Q\\.B.B=Q-B;U=http%3A%2F%2Fexample.org%2Ffoo%23bar;"): invalid semicolon separator in query
decode_test.go:17: DecodeString(";C=42%2B6.6i;A.0=x;M.Bar=8;F=6.6;A.1=y;R=8734;A.2=z;Zs.0.Qp=33_44;B=true;M.Foo=7;T=2013-10-01T07:05:34.000000088Z;E.Bytes1=%00%01%02;Bytes2=%03%04%05;Zs.0.Q=11_22;Zs.0.Z=2006-12-01;M.Qux=9;life=42;S=Hello,+there.;P\\.D\\\\Q\\.B.A=P/D;P\\.D\\\\Q\\.B.B=Q-B;U=http%3A%2F%2Fexample.org%2Ffoo%23bar;"): invalid semicolon separator in query
decode_test.go:17: DecodeString(";C=42%2B6.6i;A.0=x;M.Bar=8;F=6.6;A.1=y;R=8734;A.2=z;Zs.0.Qp=33_44;B=true;M.Foo=7;T=2013-10-01T07:05:34.000000088Z;E.Bytes1=%00%01%02;Bytes2=%03%04%05;Zs.0.Q=11_22;Zs.0.Z=2006-12-01;M.Qux=9;life=42;S=Hello,+there.;P\\.D\\\\Q\\.B.A=P/D;P\\.D\\\\Q\\.B.B=Q-B;U=http%3A%2F%2Fexample.org%2Ffoo%23bar;"): invalid semicolon separator in query
--- FAIL: TestDecodeValues (0.00s)
panic: invalid semicolon separator in query [recovered]
panic: invalid semicolon separator in query
FAIL
It would be useful for my use case if both the encoder and decoder structs exposed a Reset
method which allowed changing the underlying io.Writer
or io.Reader
without having to create new instances. This makes it possible to use sync.Pool
for example to manage a set of instances. The implementation would be trivial given that they don't have state other than the writer or reader. Would you consider a PR that added these? I'm thinking something like:
in decode.go
:
func (d *decoder) Reset(r io.Reader) {
d.r = r
}
in encode.go
func (e *encoder) Reset(w io.Writer) {
e.w = w
}
👋🏻 I have an API that expects data to be sent like so:
services[]=A&services[]=B
How can I achieve this?
Currently the default behaviour appears to number each element within the slice.
I see there's a DelimitWith
method but that doesn't quite achieve what I need (e.g. DelimitWith('|')
):
services|0=A&services|1=B
Is there a custom unmarshal method I can define that might help encode the data how I need it?
Thanks!
Please tag commit 9aeb3cf
with v1.5.2
. It simplify the lib usage with go mod and unlocks the latest.
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.