Comments (6)
While there's a tricky apprach, let's create a custom type and implement the Stringable
interface:
Online demo: https://go.dev/play/p/4IiRYr8n7zg
type CommaSeparatedIntegerArray struct {
Values []int
}
func (a CommaSeparatedIntegerArray) ToString() (string, error) {
var res = make([]string, len(a.Values))
for i := range a.Values {
res[i] = strconv.Itoa(a.Values[i])
}
return strings.Join(res, ","), nil
}
func (pa *CommaSeparatedIntegerArray) FromString(value string) error {
a := CommaSeparatedIntegerArray{}
values := strings.Split(value, ",")
a.Values = make([]int, len(values))
for i := range values {
if value, err := strconv.Atoi(values[i]); err != nil {
return err
} else {
a.Values[i] = value
}
}
*pa = a
return nil
}
type ListUsersInput struct {
IdList CommaSeparatedIntegerArray `in:"query=id"`
}
from httpin.
@krispetkov your idea looks good to me. I think rewriting the query from ids=1,2,3
to ids=1&ids=2&ids=3
is a better way for this case. As httpin does support parsing the latter format of query. And you can use type []int
as well.
from httpin.
Actually I tried to modify the provided example in custom directive, but it seems to not be working at all. The functions are not being called at all 🤔 I don't know if it has something to do that I'm using:
httpIn "github.com/ggicci/httpin"
if err = httpIn.Decode(r, &filters); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
And here what implementation I tried
type DirectiveCaseFormatter struct {
Transform func(string) []int
}
func (f *DirectiveCaseFormatter) Decode(rtm *httpInCore.DirectiveRuntime) error {
if rtm.Value.Type().Elem().Kind() != reflect.String {
return errors.New("not a string")
}
currentValue := rtm.Value.Elem().String()
newValue := f.Transform(currentValue)
slice := reflect.MakeSlice(reflect.TypeOf([]int{}), len(newValue), len(newValue))
for i, num := range newValue {
slice.Index(i).SetInt(int64(num))
}
rtm.Value.Elem().Set(slice)
return nil
}
func (f *DirectiveCaseFormatter) Encode(rtm *httpInCore.DirectiveRuntime) error {
if rtm.Value.Type().Kind() != reflect.String {
return errors.New("not a string")
}
currentValue := rtm.Value.String()
newValue := f.Transform(currentValue)
slice := reflect.MakeSlice(reflect.TypeOf([]int{}), len(newValue), len(newValue))
for i, num := range newValue {
slice.Index(i).SetInt(int64(num))
}
rtm.Value.Elem().Set(slice)
return nil
}
httpInCore.RegisterDirective("to_array", &DirectiveCaseFormatter{
Transform: func(s string) []int {
pieces := strings.Split(s, ",")
ints := make([]int, len(pieces))
for i, piece := range pieces {
num, err := strconv.Atoi(piece)
if err != nil {
log.Printf("Could not convert piece to integer: %v", err)
continue
}
ints[i] = num
}
return ints
},
})
from httpin.
Hi @krispetkov, currently httpin doesn't support your use case. In httpin, a value of a single key in the request (id=1,2,3
) is treated as a scalar value and won't be converted/mapped to an array. i.e.
?id=1,2,3 ---> only can be mapped to type T(string, int, time.Time, MyDate, etc),
but not []T ([]int, []string, etc)
from httpin.
Hey @ggicci, thanks for the proposed solution. I also came up with something, but by using the custom directive. It's just a sample and can be further improved (the logic, the usage of hardcoded ids name, etc.), but I think something like this may also do the work:
import (
httpInCore "github.com/ggicci/httpin/core"
httpInIntegration "github.com/ggicci/httpin/integration"
)
func init() {
httpInCore.RegisterDirective("toArray", &ToArrayFormatter{})
}
type ToArrayFormatter struct {
}
func (f *ToArrayFormatter) Decode(rtm *httpInCore.DirectiveRuntime) error {
req := rtm.GetRequest()
params, err := url.ParseQuery(req.URL.RawQuery)
if err != nil {
return fmt.Errorf("could not parse query: %v", err)
}
if len(params["ids"]) == 1 { // it was passed as a list of ids (eg. ?ids=1,2,3)
ids, ok := params["ids"]
if ok {
// Try to split the string by comma
idStrings := strings.Split(ids[0], ",")
if len(idStrings) == 0 {
// Try to split the string by semicolon
idStrings = strings.Split(ids[0], ";")
}
params.Del("ids")
for _, id := range idStrings {
params.Add("ids", id)
}
}
req.URL.RawQuery = params.Encode()
} else {
// Check if any of the passed ids is a valid number
for _, id := range params["ids"] {
_, err := strconv.ParseFloat(id, 64)
if err != nil {
return fmt.Errorf("arg %s is not a valid number", id)
}
}
}
return nil
}
func (f *ToArrayFormatter) Encode(rtm *httpInCore.DirectiveRuntime) error {
// Your code here ...
return nil
}
type Filters struct {
Ids []int `in:"toArray;query=ids"` // query param (eg. ?ids=1,2,3)
}
from httpin.
Thanks for the help and the info! I will close the issue, and I hope it will help someone in future 🙌
from httpin.
Related Issues (20)
- Is it possible to use message pack instead of json/xml for request bodies? HOT 2
- Any way to pre-initialize the struct? HOT 4
- Go 1.22+ net/http new features support
- Feature Request: non-global directives
- Feature Request: ignore query parameters during encoding HOT 1
- Invoke decode manually HOT 2
- decoder registered with ReplaceTypeDecoder not invoked HOT 2
- implementation regexp for tag HOT 5
- Feature Request: support encoding HOT 4
- Feature: when context(httpin.Input) is nil, then create a new struct
- How do I use nested structs from query? HOT 5
- How do you parse form data into nested struct? HOT 1
- Can I use pointer types as query params? HOT 4
- How do I use httpin.RegisterTypeDecoder in the latest version? HOT 3
- Decode parameter struct with default values only works the first time HOT 2
- Dealing with nested structs (again) and OpenAPI v3 - deepObject
- Impossible to make streaming
- Determining type of error - i.e. client or implementation. HOT 2
- Optional body for POST requests HOT 5
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from httpin.