Created
November 16, 2025 07:47
-
-
Save phoenisx/6a2e9aefc2e5f0fa3822688b37db4eef to your computer and use it in GitHub Desktop.
Custom oapi-codegen strict handler template to manage error handling. Helps with issue: https://github.com/oapi-codegen/oapi-codegen/issues/2085
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| {{range .}} | |
| {{/** | |
| * Each operation's RequestObject | |
| */}} | |
| {{$opid := .OperationId -}} | |
| type {{$opid | ucFirst}}RequestObject struct { | |
| {{range .PathParams -}} | |
| {{.GoName | ucFirst}} {{.TypeDef}} {{.JsonTag}} | |
| {{end -}} | |
| {{if .RequiresParamObject -}} | |
| Params {{$opid}}Params | |
| {{end -}} | |
| {{if .HasMaskedRequestContentTypes -}} | |
| ContentType string | |
| {{end -}} | |
| {{$multipleBodies := gt (len .Bodies) 1 -}} | |
| {{range .Bodies -}} | |
| {{if $multipleBodies}}{{.NameTag}}{{end}}Body {{if eq .NameTag "Multipart"}}*multipart.Reader{{else if ne .NameTag ""}}*{{$opid}}{{.NameTag}}RequestBody{{else}}io.Reader{{end}} | |
| {{end -}} | |
| } | |
| {{/** | |
| * | |
| * Each operation's ResponseObject | |
| */}} | |
| type {{$opid | ucFirst}}ResponseObject interface { | |
| Visit{{$opid}}Response(w http.ResponseWriter) error | |
| } | |
| {{/** | |
| * Loops through Response to find all it's details like headers and bodies etc. | |
| */}} | |
| {{range .Responses}} | |
| {{$statusCode := .StatusCode -}} | |
| {{$hasHeaders := ne 0 (len .Headers) -}} | |
| {{$fixedStatusCode := .HasFixedStatusCode -}} | |
| {{$isRef := .IsRef -}} | |
| {{$isExternalRef := .IsExternalRef -}} | |
| {{$ref := .Ref | ucFirstWithPkgName -}} | |
| {{$headers := .Headers -}} | |
| {{/** | |
| * | |
| * Each Response's Headers | |
| */}} | |
| {{if (and $hasHeaders (not $isRef)) -}} | |
| type {{$opid}}{{$statusCode}}ResponseHeaders struct { | |
| {{range .Headers -}} | |
| {{.GoName}} {{.Schema.TypeDecl}} | |
| {{end -}} | |
| } | |
| {{end}} | |
| {{/** | |
| * | |
| * Loop through Response Bodies and create necessary structs and impl interfaces. | |
| * I can remove unnecessary structs generated for all error responses, i.e 4xx - 5xx | |
| */}} | |
| {{range .Contents}} | |
| {{$receiverTypeName := printf "%s%s%s%s" $opid $statusCode .NameTagOrContentType "Response"}} | |
| {{/** | |
| * | |
| * Each Response's Bodies struct types | |
| */}} | |
| {{if and $fixedStatusCode $isRef -}} | |
| {{ if and (not $hasHeaders) ($fixedStatusCode) (.IsSupported) (eq .NameTag "Multipart") -}} | |
| type {{$receiverTypeName}} {{$ref}}{{.NameTagOrContentType}}Response | |
| {{else if $isExternalRef -}} | |
| type {{$receiverTypeName}} struct { {{$ref}} } | |
| {{else -}} | |
| {{if lt ($statusCode | atoi) 400 -}} | |
| type {{$receiverTypeName}} struct{ {{$ref}}{{.NameTagOrContentType}}Response } | |
| {{end}} | |
| {{end}} | |
| {{else if and (not $hasHeaders) ($fixedStatusCode) (.IsSupported) -}} | |
| type {{$receiverTypeName}} {{if eq .NameTag "Multipart"}}func(writer *multipart.Writer)error{{else if .IsSupported}}{{if and .Schema.IsRef (not .Schema.IsExternalRef)}}={{end}} {{.Schema.TypeDecl}}{{else}}io.Reader{{end}} | |
| {{else -}} | |
| type {{$receiverTypeName}} struct { | |
| Body {{if eq .NameTag "Multipart"}}func(writer *multipart.Writer)error{{else if .IsSupported}}{{.Schema.TypeDecl}}{{else}}io.Reader{{end}} | |
| {{if $hasHeaders -}} | |
| Headers {{if $isRef}}{{$ref}}{{else}}{{$opid}}{{$statusCode}}{{end}}ResponseHeaders | |
| {{end -}} | |
| {{if not $fixedStatusCode -}} | |
| StatusCode int | |
| {{end -}} | |
| {{if not .HasFixedContentType -}} | |
| ContentType string | |
| {{end -}} | |
| {{if not .IsSupported -}} | |
| ContentLength int64 | |
| {{end -}} | |
| } | |
| {{end}} | |
| {{/** | |
| * | |
| * Each Response's Bodies interface implementation. | |
| */}} | |
| {{if lt ($statusCode | atoi) 400 -}} | |
| func (response {{$receiverTypeName}}) Visit{{$opid}}Response(w http.ResponseWriter) error { | |
| {{/** | |
| * | |
| * I don't know what this is exactly but feels like form response for with multipart response. | |
| */}} | |
| {{if eq .NameTag "Multipart" -}} | |
| writer := multipart.NewWriter(w) | |
| {{end -}} | |
| {{/** | |
| * | |
| * Set's Headers content type if exists | |
| */}} | |
| w.Header().Set("Content-Type", {{if eq .NameTag "Multipart"}}{{if eq .ContentType "multipart/form-data"}}writer.FormDataContentType(){{else}}mime.FormatMediaType("{{.ContentType}}", map[string]string{"boundary": writer.Boundary()}){{end}}{{else if .HasFixedContentType }}"{{.ContentType}}"{{else}}response.ContentType{{end}}) | |
| {{if not .IsSupported -}} | |
| if response.ContentLength != 0 { | |
| w.Header().Set("Content-Length", fmt.Sprint(response.ContentLength)) | |
| } | |
| {{end -}} | |
| {{/** | |
| * | |
| * Set all kinds of headers defined in schema. We only want to modify for Set-Cookie, so we'll add codition accordingly | |
| */}} | |
| {{range $headers -}} | |
| {{ printf "%s" .Schema.TypeDecl }} | |
| {{if and (eq .Name "Set-Cookie") -}} | |
| for _, cookie := range response.Headers.SetCookie { | |
| w.Header().Add("{{.Name}}", fmt.Sprint(response.Headers.{{.GoName}})) | |
| } | |
| {{else -}} | |
| w.Header().Set("{{.Name}}", fmt.Sprint(response.Headers.{{.GoName}})) | |
| {{end -}} | |
| {{end -}} | |
| w.WriteHeader({{if $fixedStatusCode}}{{$statusCode}}{{else}}response.StatusCode{{end}}) | |
| {{$hasBodyVar := or ($hasHeaders) (not $fixedStatusCode) (not .IsSupported)}} | |
| {{if .IsJSON -}} | |
| {{$hasUnionElements := ne 0 (len .Schema.UnionElements)}} | |
| return json.NewEncoder(w).Encode(response{{if $hasBodyVar}}.Body{{end}}{{if $hasUnionElements}}.union{{end}}) | |
| {{else if eq .NameTag "Text" -}} | |
| _, err := w.Write([]byte({{if $hasBodyVar}}response.Body{{else}}response{{end}})) | |
| return err | |
| {{else if eq .NameTag "Formdata" -}} | |
| if form, err := runtime.MarshalForm({{if $hasBodyVar}}response.Body{{else}}response{{end}}, nil); err != nil { | |
| return err | |
| } else { | |
| _, err := w.Write([]byte(form.Encode())) | |
| return err | |
| } | |
| {{else if eq .NameTag "Multipart" -}} | |
| defer writer.Close() | |
| return {{if $hasBodyVar}}response.Body{{else}}response{{end}}(writer); | |
| {{else -}} | |
| if closer, ok := response.Body.(io.ReadCloser); ok { | |
| defer closer.Close() | |
| } | |
| _, err := io.Copy(w, response.Body) | |
| return err | |
| {{end}}{{/* if eq .NameTag "JSON" */ -}} | |
| } | |
| {{end}} | |
| {{end}} | |
| {{if eq 0 (len .Contents) -}} | |
| {{if and $fixedStatusCode $isRef -}} | |
| type {{$opid}}{{$statusCode}}Response {{if not $isExternalRef}}={{end}} {{$ref}}Response | |
| {{else -}} | |
| type {{$opid}}{{$statusCode}}Response struct { | |
| {{if $hasHeaders -}} | |
| Headers {{if $isRef}}{{$ref}}{{else}}{{$opid}}{{$statusCode}}{{end}}ResponseHeaders | |
| {{end}} | |
| {{if not $fixedStatusCode -}} | |
| StatusCode int | |
| {{end -}} | |
| } | |
| {{end -}} | |
| func (response {{$opid}}{{$statusCode}}Response) Visit{{$opid}}Response(w http.ResponseWriter) error { | |
| {{range $headers -}} | |
| {{if and (eq .Name "Set-Cookie") (eq .Schema.TypeDecl "[]string") -}} | |
| for _, cookie := range response.Headers.SetCookie { | |
| w.Header().Add("{{.Name}}", fmt.Sprint(cookie)) | |
| } | |
| {{else -}} | |
| w.Header().Set("{{.Name}}", fmt.Sprint(response.Headers.{{.GoName}})) | |
| {{end -}} | |
| {{end -}} | |
| w.WriteHeader({{if $fixedStatusCode}}{{$statusCode}}{{else}}response.StatusCode{{end}}) | |
| return nil | |
| } | |
| {{end}} | |
| {{end}} | |
| {{end}} | |
| // StrictServerInterface represents all server handlers. | |
| type StrictServerInterface interface { | |
| {{range .}} | |
| {{.SummaryAsComment }} | |
| // ({{.Method}} {{.Path}}) | |
| {{$opid := .OperationId -}} | |
| {{$opid}}(ctx context.Context, request {{$opid | ucFirst}}RequestObject) ({{$opid | ucFirst}}ResponseObject, error) | |
| {{end}}{{/* range . */ -}} | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment