Repository navigation
Description
Summary
For application/x-www-form-urlencoded request bodies, the generated client encodes array properties as indexed keys (topic[0]=news&topic[1]=releases), even when the spec declares style: form, explode: true for the property. Per the OpenAPI 3.0 Encoding Object, that serializes as repeated keys: topic=news&topic=releases. A server that reads repeated keys sees no topic at all.
This looks like the same problem as #1575, which was closed as a JSON body question; the body there was form-encoded too.
Reproduction
openapi.yaml:
openapi: 3.0.3
info:
title: repro
version: 1.0.0
paths:
/subscriptions:
post:
operationId: subscribe
requestBody:
required: true
content:
application/x-www-form-urlencoded:
schema:
type: object
required: [email, topic]
properties:
email:
type: string
topic:
type: array
items:
type: string
encoding:
topic:
style: form
explode: true
responses:
'204':
description: Subscribedcfg.yaml:
package: api
generate:
models: true
client: true
output: api/api.gen.gomain.go:
package main
import (
"context"
"fmt"
"io"
"net/http"
"net/http/httptest"
"repro/api"
)
func main() {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
body, _ := io.ReadAll(r.Body)
fmt.Printf("body: %s\n", body)
w.WriteHeader(http.StatusNoContent)
}))
defer server.Close()
client, _ := api.NewClient(server.URL)
_, _ = client.SubscribeWithFormdataBody(context.Background(), api.SubscribeFormdataRequestBody{
Email: "jane@example.com",
Topic: []string{"news", "releases"},
})
}$ go mod init repro
$ go run github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@v2.8.0 -config cfg.yaml openapi.yaml
$ go mod tidy
$ go run .
body: email=jane%40example.com&topic%5B0%5D=news&topic%5B1%5D=releases
Expected body: email=jane%40example.com&topic=news&topic=releases.
Cause
- The client template always calls
runtime.MarshalForm(body, nil)(pkg/codegen/templates/client.tmpl), so the spec'sencodingnever reaches the runtime. runtime.MarshalFormonly readsRequestBodyEncoding.ContentType.StyleandExplodeare unused, andmarshalFormImplalways writes slice elements asname[i].
runtime.BindForm accepts repeated keys, name[] and name[i], so a generated client works against a generated server either way. The mismatch only shows up against other servers.
Proposal
Honour style/explode when the spec declares them explicitly: the generator passes the operation's encoding to MarshalForm instead of nil, and MarshalForm writes repeated keys for arrays with style: form, explode: true. Bodies without an explicit encoding keep today's output, so the change isn't breaking.
I can open PRs for both oapi-codegen and runtime if this direction sounds right.
oapi-codegen v2.8.0, runtime v1.7.0, Go 1.26.8
Activity