Skip to content

Client form bodies ignore encoding style/explode, so arrays are always sent as name[0]=… #2604

Open

Description

@kahlstrm

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: Subscribed

cfg.yaml:

package: api
generate:
  models: true
  client: true
output: api/api.gen.go

main.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's encoding never reaches the runtime.
  • runtime.MarshalForm only reads RequestBodyEncoding.ContentType. Style and Explode are unused, and marshalFormImpl always writes slice elements as name[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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions