Strict server: gate no-content response headers on nullable/optional#2351
Conversation
The no-content branch of the three strict-server interface templates (`strict-interface.tmpl`, `strict-fiber-interface.tmpl`, `strict-iris-interface.tmpl`) was rendering response headers unconditionally, missing the three-way `.IsNullable` / `.IsOptional` / default switch that PR oapi-codegen#2301 added to the typed-body branch. Because the `{{$opid}}{{$statusCode}}ResponseHeaders` struct is shared between the typed-body and no-content branches and uses `{{.GoTypeDef}}` (since oapi-codegen#2301 / oapi-codegen#2331), an unset optional header field is `*string(nil)` and an unspecified nullable header is a zero `runtime.Nullable[T]`. The bare `fmt.Sprint(...)` in the no-content branch was therefore stringifying these to `<nil>` / a typed zero-value and emitting a junk header rather than omitting it. A 204-style response declaring an optional or nullable header — with the caller leaving it unset — produced a wrong header value instead of no header at all. Apply the same three-way switch to the no-content branch in all three templates so unset values are skipped, matching the typed-body branch behavior and matching what callers expect for OpenAPI `required: false` / `nullable: true` response headers. Add regression coverage: - `strict-schema.yaml` gains a `/no-content-headers` POST operation whose only response is 204 with `optional-header` (`required: false`) and `nullable-header` (`nullable: true`). - `NoContentHeaders` handler stub returning an empty `NoContentHeaders204Response{}` is wired into all seven framework server implementations (chi, echo, fiber, gin, gorilla, iris, stdhttp). - `NoContentHeadersOmitUnset` subtest added to the three `testImpl` copies (shared, stdhttp, fiber). Asserts both headers are absent from `rr.Header().Values(...)` when the response struct's header fields are left at their zero values. The shared `testImpl` covers chi, echo, gin, and iris in one place; stdhttp and fiber maintain their own copies because they live in separate test files (stdhttp because of its own go.mod, fiber because of its package boundary). Gorilla generates code but has no test driver — the handler stub is still required so the generated `StrictServerInterface` is satisfied. Fixes: oapi-codegen#2349 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Greptile SummaryThis PR fixes a bug in the strict-server code generator where the no-content (zero-body) response branch of all three strict templates ( Confidence Score: 5/5Safe to merge — targeted template fix with correct generated output and comprehensive regression coverage across all backends. The template change is minimal, surgical, and mirrors an identical fix already applied to the typed-body branch. All three strict templates are updated consistently. Generated code correctly nil-checks both optional and nullable header fields before emitting them. Regression tests are added in all three relevant test files (shared, fiber, stdhttp), handler stubs are provided for all seven framework backends, and the schema change is well-scoped. No unrelated drift found in the generated files. No files require special attention.
|
| Filename | Overview |
|---|---|
| pkg/codegen/templates/strict/strict-interface.tmpl | No-content branch now has the correct three-way IsNullable/IsOptional/default header switch, matching the typed-body branch. |
| pkg/codegen/templates/strict/strict-fiber-interface.tmpl | Fiber no-content branch receives the same three-way header gating fix, consistent with the standard interface template. |
| pkg/codegen/templates/strict/strict-iris-interface.tmpl | Iris no-content branch receives the same three-way header gating fix, consistent with the other templates. |
| internal/test/strict-server/strict-schema.yaml | Adds /no-content-headers POST operation with 204 response declaring optional-header and nullable-header to drive regression coverage. |
| internal/test/strict-server/strict_test.go | Adds NoContentHeadersOmitUnset subtest to the shared testImpl covering chi, echo, gin, and iris; asserts both headers absent when response struct fields are zero-valued. |
| internal/test/strict-server/fiber/fiber_strict_test.go | Fiber-specific testImpl copy gains the same NoContentHeadersOmitUnset subtest. |
| internal/test/strict-server/stdhttp/std_strict_test.go | stdhttp-specific testImpl copy gains the same NoContentHeadersOmitUnset subtest. |
| internal/test/strict-server/chi/server.gen.go | Regenerated with NoContentHeaders operation; VisitNoContentHeadersResponse correctly gates both headers behind nil checks. |
| internal/test/strict-server/gorilla/server.gen.go | Regenerated with NoContentHeaders stub; generated Visit function correctly nil-checks both headers before emitting. |
| internal/test/strict-server/gorilla/server.go | Adds NoContentHeaders handler stub returning empty 204 response to satisfy the updated StrictServerInterface. |
Reviews (1): Last reviewed commit: "Strict server: gate no-content response ..." | Re-trigger Greptile
The no-content branch of the three strict-server interface templates (
strict-interface.tmpl,strict-fiber-interface.tmpl,strict-iris-interface.tmpl) was rendering response headers unconditionally, missing the three-way.IsNullable/.IsOptional/ default switch that PR #2301 added to the typed-body branch.Because the
{{$opid}}{{$statusCode}}ResponseHeadersstruct is shared between the typed-body and no-content branches and uses{{.GoTypeDef}}(since #2301 / #2331), an unset optional header field is*string(nil)and an unspecified nullable header is a zeroruntime.Nullable[T]. The barefmt.Sprint(...)in the no-content branch was therefore stringifying these to<nil>/ a typed zero-value and emitting a junk header rather than omitting it. A 204-style response declaring an optional or nullable header — with the caller leaving it unset — produced a wrong header value instead of no header at all.Apply the same three-way switch to the no-content branch in all three templates so unset values are skipped, matching the typed-body branch behavior and matching what callers expect for OpenAPI
required: false/nullable: trueresponse headers.Add regression coverage:
strict-schema.yamlgains a/no-content-headersPOST operation whose only response is 204 withoptional-header(required: false) andnullable-header(nullable: true).NoContentHeadershandler stub returning an emptyNoContentHeaders204Response{}is wired into all seven framework server implementations (chi, echo, fiber, gin, gorilla, iris, stdhttp).NoContentHeadersOmitUnsetsubtest added to the threetestImplcopies (shared, stdhttp, fiber). Asserts both headers are absent fromrr.Header().Values(...)when the response struct's header fields are left at their zero values.The shared
testImplcovers chi, echo, gin, and iris in one place; stdhttp and fiber maintain their own copies because they live in separate test files (stdhttp because of its own go.mod, fiber because of its package boundary). Gorilla generates code but has no test driver — the handler stub is still required so the generatedStrictServerInterfaceis satisfied.Fixes: #2349