Repository: deepmap/oapi-codegen
Pull request #471 (2021-10-26 18:55:05)

oneOf/anyOf support (including discriminator)
Implemented using json.RawMessage to delay parsing to concrete type until one of the getter (As...) methods is called. If the schema has a discriminator and its mapping, then ValueByDiscriminator method can be used to get an empty interface that holds one of the union types. Also, when using setters (From... methods), discriminator value will be filled automatically, according to mapping.
This implementation only supports JSON, so will not work in parameters (body only). ~~Also reverts to generating empty interface if "type" or other special properties are specified (for example, if only required properties differ in each variant)~~ fixed that behavior, will now work properly with fixed properties
Tests for this feature are added to internal/test/components.

jonasrmichel: This is great work @Warboss-rus, thank you!

Any plans to support arrays whose contents are oneOf / anyOf?

For example...

```json
{
   "type":"array",
   "items":{
      "oneOf":[
         {
            "$ref":"#/components/schemas/TypeA"
         },
         {
            "$ref":"#/components/schemas/TypeB"
         },
         {
            "$ref":"#/components/schemas/TypeC"
         }
      ]
   }
}
```
Warboss-rus: Thanks, added a test for this case, will fix it this week
Warboss-rus: I've reworked the code to support oneOf\anyOf along fixed properties, fixed type generation for array items that have additional properties or union parts (thanks @jonasrmichel), added some tests and updated information in README
jonasrmichel: Brilliant work @Warboss-rus, thank you!

@deepmap-marcinr @carmo-evan @Karitham -- It'd be wonderful if these contributions could be merged for the next release. :pray: 
tamj0rd2: Nice! Is there a release schedule? Really looking forward to being able to use this
jonasrmichel: Nudge.

@deepmap-marcinr these changes look good to me. They include new functionality, tests, and necessary documentation updates.

What's required for merge approval? It'd be great to see these features in the next minor release.

Patch:
diff --git a/README.md b/README.md
index 0bf46d9..5cc41cc 100644
--- a/README.md
+++ b/README.md
@@ -35,8 +35,8 @@ write a lot of boilerplate code to perform all the marshalling and unmarshalling
 into objects which match the OpenAPI 3.0 definition. The code generator in this
 directory does a lot of that for you. You would run it like so:
 
-    go get github.com/deepmap/oapi-codegen/cmd/oapi-codegen
-    oapi-codegen petstore-expanded.yaml  > petstore.gen.go
+    go install github.com/deepmap/oapi-codegen/cmd/oapi-codegen@latest
+    oapi-codegen petstore-expanded.yaml > petstore.gen.go
 
 Let's go through that `petstore.gen.go` file to show you everything which was
 generated.
@@ -255,6 +255,9 @@ func SetupHandler() {
     http.Handle("/", Handler(&myApi))
 }
 ```
+
+Alternatively, [Gorilla](https://github.com/gorilla/mux) is also 100% compatible with `net/http` and can be generated with `-generate gorilla`.
+
 </summary></details>
 
 #### Additional Properties in type definitions
@@ -315,6 +318,21 @@ define types for inner fields which themselves support additionalProperties, and
 all of them are tested via the `internal/test/components` schemas and tests. Please
 look through those tests for more usage examples.
 
+#### oneOf/anyOf/allOf support
+
+- `oneOf` and `anyOf` are implemented using delayed parsing with the help of `json.RawMessage`. 
+The following schema will result in a type that has methods such as `AsCat`, `AsDog`, `FromCat`, `FromDog`. If the schema also includes a discriminator the generated code will also have methods such as `Discriminator`, `ValueByDiscriminator` and will force discriminator value in `From` methods.
+```yaml
+schema:
+  oneOf:
+    - $ref: '#/components/schemas/Cat'
+    - $ref: '#/components/schemas/Dog'
+```
+- `allOf` is supported, by taking the union of all the fields in all the
+    component schemas. This is the most useful of these operations, and is
+    commonly used to merge objects with an identifier, as in the
+    `petstore-expanded` example.
+
 ## Generated Client Boilerplate
 
 Once your server is up and running, you probably want to make requests to it. If
@@ -608,23 +626,6 @@ by comma separating them in the form `key1:value1,key2:value2`.
 This code is still young, and not complete, since we're filling it in as we
 need it. We've not yet implemented several things:
 
-- `oneOf`, `anyOf` are not supported with strong Go typing. This schema:
-
-        schema:
-          oneOf:
-            - $ref: '#/components/schemas/Cat'
-            - $ref: '#/components/schemas/Dog'
-
-    will result in a Go type of `interface{}`. It will be up to you
-    to validate whether it conforms to `Cat` and/or `Dog`, depending on the
-    keyword. It's not clear if we can do anything much better here given the
-    limits of Go typing.
-
-    `allOf` is supported, by taking the union of all the fields in all the
-    component schemas. This is the most useful of these operations, and is
-    commonly used to merge objects with an identifier, as in the
-    `petstore-expanded` example.
-
 - `patternProperties` isn't yet supported and will exit with an error. Pattern
  properties were defined in JSONSchema, and the `kin-openapi` Swagger object
  knows how to parse them, but they're not part of OpenAPI 3.0, so we've left
diff --git a/cmd/oapi-codegen/oapi-codegen.go b/cmd/oapi-codegen/oapi-codegen.go
index fe43be6..fc1b6cf 100644
--- a/cmd/oapi-codegen/oapi-codegen.go
+++ b/cmd/oapi-codegen/oapi-codegen.go
@@ -86,7 +86,7 @@ func main() {
 	// All flags below are deprecated, and will be removed in a future release. Please do not
 	// update their behavior.
 	flag.StringVar(&flagGenerate, "generate", "types,client,server,spec",
-		`Comma-separated list of code to generate; valid options: "types", "client", "chi-server", "server", "gin", "spec", "skip-fmt", "skip-prune"`)
+		`Comma-separated list of code to generate; valid options: "types", "client", "chi-server", "server", "gin", "gorilla", "spec", "skip-fmt", "skip-prune"`)
 	flag.StringVar(&flagIncludeTags, "include-tags", "", "Only include operations with the given tags. Comma-separated list of tags.")
 	flag.StringVar(&flagExcludeTags, "exclude-tags", "", "Exclude operations that are tagged with the given tags. Comma-separated list of tags.")
 	flag.StringVar(&flagTemplatesDir, "templates", "", "Path to directory containing user templates")
@@ -258,7 +258,7 @@ func updateConfigFromFlags(cfg configuration) (configuration, error) {
 
 	if len(unsupportedFlags) > 0 {
 		return configuration{}, fmt.Errorf("flags %s aren't supported in "+
-			"new config style, please use --old-style-config or update your configuration",
+			"new config style, please use -old-config-style or update your configuration",
 			strings.Join(unsupportedFlags, ", "))
 	}
 
@@ -321,6 +321,8 @@ func newConfigFromOldConfig(c oldConfiguration) configuration {
 			opts.Generate.EchoServer = true
 		case "gin":
 			opts.Generate.GinServer = true
+		case "gorilla":
+			opts.Generate.GorillaServer = true
 		case "types":
 			opts.Generate.Models = true
 		case "spec":
diff --git a/examples/petstore-expanded/chi/api/petstore.gen.go b/examples/petstore-expanded/chi/api/petstore.gen.go
index bf52a20..64aec35 100644
--- a/examples/petstore-expanded/chi/api/petstore.gen.go
+++ b/examples/petstore-expanded/chi/api/petstore.gen.go
@@ -86,7 +86,7 @@ type ServerInterfaceWrapper struct {
 	ErrorHandlerFunc   func(w http.ResponseWriter, r *http.Request, err error)
 }
 
-type MiddlewareFunc func(http.HandlerFunc) http.HandlerFunc
+type MiddlewareFunc func(http.Handler) http.Handler
 
 // FindPets operation middleware
 func (siw *ServerInterfaceWrapper) FindPets(w http.ResponseWriter, r *http.Request) {
@@ -119,30 +119,30 @@ func (siw *ServerInterfaceWrapper) FindPets(w http.ResponseWriter, r *http.Reque
 		return
 	}
 
-	var handler = func(w http.ResponseWriter, r *http.Request) {
+	var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		siw.Handler.FindPets(w, r, params)
-	}
+	})
 
 	for _, middleware := range siw.HandlerMiddlewares {
 		handler = middleware(handler)
 	}
 
-	handler(w, r.WithContext(ctx))
+	handler.ServeHTTP(w, r.WithContext(ctx))
 }
 
 // AddPet operation middleware
 func (siw *ServerInterfaceWrapper) AddPet(w http.ResponseWriter, r *http.Request) {
 	ctx := r.Context()
 
-	var handler = func(w http.ResponseWriter, r *http.Request) {
+	var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		siw.Handler.AddPet(w, r)
-	}
+	})
 
 	for _, middleware := range siw.HandlerMiddlewares {
 		handler = middleware(handler)
 	}
 
-	handler(w, r.WithContext(ctx))
+	handler.ServeHTTP(w, r.WithContext(ctx))
 }
 
 // DeletePet operation middleware
@@ -160,15 +160,15 @@ func (siw *ServerInterfaceWrapper) DeletePet(w http.ResponseWriter, r *http.Requ
 		return
 	}
 
-	var handler = func(w http.ResponseWriter, r *http.Request) {
+	var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		siw.Handler.DeletePet(w, r, id)
-	}
+	})
 
 	for _, middleware := range siw.HandlerMiddlewares {
 		handler = middleware(handler)
 	}
 
-	handler(w, r.WithContext(ctx))
+	handler.ServeHTTP(w, r.WithContext(ctx))
 }
 
 // FindPetByID operation middleware
@@ -186,15 +186,15 @@ func (siw *ServerInterfaceWrapper) FindPetByID(w http.ResponseWriter, r *http.Re
 		return
 	}
 
-	var handler = func(w http.ResponseWriter, r *http.Request) {
+	var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		siw.Handler.FindPetByID(w, r, id)
-	}
+	})
 
 	for _, middleware := range siw.HandlerMiddlewares {
 		handler = middleware(handler)
 	}
 
-	handler(w, r.WithContext(ctx))
+	handler.ServeHTTP(w, r.WithContext(ctx))
 }
 
 type UnescapedCookieParamError struct {
diff --git a/examples/petstore-expanded/gorilla/api/cfg.yaml b/examples/petstore-expanded/gorilla/api/cfg.yaml
new file mode 100644
index 0000000..7f1a2d7
--- /dev/null
+++ b/examples/petstore-expanded/gorilla/api/cfg.yaml
@@ -0,0 +1,6 @@
+package: api
+generate:
+  gorilla-server: true
+  models: true
+  embedded-spec: true
+output: petstore.gen.go
diff --git a/examples/petstore-expanded/gorilla/api/petstore.gen.go b/examples/petstore-expanded/gorilla/api/petstore.gen.go
new file mode 100644
index 0000000..5ddb9e1
--- /dev/null
+++ b/examples/petstore-expanded/gorilla/api/petstore.gen.go
@@ -0,0 +1,427 @@
+// Package api provides primitives to interact with the openapi HTTP API.
+//
+// Code generated by github.com/deepmap/oapi-codegen version (devel) DO NOT EDIT.
+package api
+
+import (
+	"bytes"
+	"compress/gzip"
+	"encoding/base64"
+	"fmt"
+	"net/http"
+	"net/url"
+	"path"
+	"strings"
+
+	"github.com/deepmap/oapi-codegen/pkg/runtime"
+	"github.com/getkin/kin-openapi/openapi3"
+	"github.com/gorilla/mux"
+)
+
+// Error defines model for Error.
+type Error struct {
+	// Error code
+	Code int32 `json:"code"`
+
+	// Error message
+	Message string `json:"message"`
+}
+
+// NewPet defines model for NewPet.
+type NewPet struct {
+	// Name of the pet
+	Name string `json:"name"`
+
+	// Type of the pet
+	Tag *string `json:"tag,omitempty"`
+}
+
+// Pet defines model for Pet.
+type Pet struct {
+	// Unique id of the pet
+	Id int64 `json:"id"`
+
+	// Name of the pet
+	Name string `json:"name"`
+
+	// Type of the pet
+	Tag *string `json:"tag,omitempty"`
+}
+
+// FindPetsParams defines parameters for FindPets.
+type FindPetsParams struct {
+	// tags to filter by
+	Tags *[]string `form:"tags,omitempty" json:"tags,omitempty"`
+
+	// maximum number of results to return
+	Limit *int32 `form:"limit,omitempty" json:"limit,omitempty"`
+}
+
+// AddPetJSONBody defines parameters for AddPet.
+type AddPetJSONBody = NewPet
+
+// AddPetJSONRequestBody defines body for AddPet for application/json ContentType.
+type AddPetJSONRequestBody = AddPetJSONBody
+
+// ServerInterface represents all server handlers.
+type ServerInterface interface {
+	// Returns all pets
+	// (GET /pets)
+	FindPets(w http.ResponseWriter, r *http.Request, params FindPetsParams)
+	// Creates a new pet
+	// (POST /pets)
+	AddPet(w http.ResponseWriter, r *http.Request)
+	// Deletes a pet by ID
+	// (DELETE /pets/{id})
+	DeletePet(w http.ResponseWriter, r *http.Request, id int64)
+	// Returns a pet by ID
+	// (GET /pets/{id})
+	FindPetByID(w http.ResponseWriter, r *http.Request, id int64)
+}
+
+// ServerInterfaceWrapper converts contexts to parameters.
+type ServerInterfaceWrapper struct {
+	Handler            ServerInterface
+	HandlerMiddlewares []MiddlewareFunc
+	ErrorHandlerFunc   func(w http.ResponseWriter, r *http.Request, err error)
+}
+
+type MiddlewareFunc func(http.HandlerFunc) http.HandlerFunc
+
+// FindPets operation middleware
+func (siw *ServerInterfaceWrapper) FindPets(w http.ResponseWriter, r *http.Request) {
+	ctx := r.Context()
+
+	var err error
+
+	// Parameter object where we will unmarshal all parameters from the context
+	var params FindPetsParams
+
+	// ------------- Optional query parameter "tags" -------------
+	if paramValue := r.URL.Query().Get("tags"); paramValue != "" {
+
+	}
+
+	err = runtime.BindQueryParameter("form", true, false, "tags", r.URL.Query(), &params.Tags)
+	if err != nil {
+		siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "tags", Err: err})
+		return
+	}
+
+	// ------------- Optional query parameter "limit" -------------
+	if paramValue := r.URL.Query().Get("limit"); paramValue != "" {
+
+	}
+
+	err = runtime.BindQueryParameter("form", true, false, "limit", r.URL.Query(), &params.Limit)
+	if err != nil {
+		siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "limit", Err: err})
+		return
+	}
+
+	var handler = func(w http.ResponseWriter, r *http.Request) {
+		siw.Handler.FindPets(w, r, params)
+	}
+
+	for _, middleware := range siw.HandlerMiddlewares {
+		handler = middleware(handler)
+	}
+
+	handler(w, r.WithContext(ctx))
+}
+
+// AddPet operation middleware
+func (siw *ServerInterfaceWrapper) AddPet(w http.ResponseWriter, r *http.Request) {
+	ctx := r.Context()
+
+	var handler = func(w http.ResponseWriter, r *http.Request) {
+		siw.Handler.AddPet(w, r)
+	}
+
+	for _, middleware := range siw.HandlerMiddlewares {
+		handler = middleware(handler)
+	}
+
+	handler(w, r.WithContext(ctx))
+}
+
+// DeletePet operation middleware
+func (siw *ServerInterfaceWrapper) DeletePet(w http.ResponseWriter, r *http.Request) {
+	ctx := r.Context()
+
+	var err error
+
+	// ------------- Path parameter "id" -------------
+	var id int64
+
+	err = runtime.BindStyledParameter("simple", false, "id", mux.Vars(r)["id"], &id)
+	if err != nil {
+		siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "id", Err: err})
+		return
+	}
+
+	var handler = func(w http.ResponseWriter, r *http.Request) {
+		siw.Handler.DeletePet(w, r, id)
+	}
+
+	for _, middleware := range siw.HandlerMiddlewares {
+		handler = middleware(handler)
+	}
+
+	handler(w, r.WithContext(ctx))
+}
+
+// FindPetByID operation middleware
+func (siw *ServerInterfaceWrapper) FindPetByID(w http.ResponseWriter, r *http.Request) {
+	ctx := r.Context()
+
+	var err error
+
+	// ------------- Path parameter "id" -------------
+	var id int64
+
+	err = runtime.BindStyledParameter("simple", false, "id", mux.Vars(r)["id"], &id)
+	if err != nil {
+		siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "id", Err: err})
+		return
+	}
+
+	var handler = func(w http.ResponseWriter, r *http.Request) {
+		siw.Handler.FindPetByID(w, r, id)
+	}
+
+	for _, middleware := range siw.HandlerMiddlewares {
+		handler = middleware(handler)
+	}
+
+	handler(w, r.WithContext(ctx))
+}
+
+type UnescapedCookieParamError struct {
+	ParamName string
+	Err       error
+}
+
+func (e *UnescapedCookieParamError) Error() string {
+	return fmt.Sprintf("error unescaping cookie parameter '%s'", e.ParamName)
+}
+
+func (e *UnescapedCookieParamError) Unwrap() error {
+	return e.Err
+}
+
+type UnmarshalingParamError struct {
+	ParamName string
+	Err       error
+}
+
+func (e *UnmarshalingParamError) Error() string {
+	return fmt.Sprintf("Error unmarshaling parameter %s as JSON: %s", e.ParamName, e.Err.Error())
+}
+
+func (e *UnmarshalingParamError) Unwrap() error {
+	return e.Err
+}
+
+type RequiredParamError struct {
+	ParamName string
+}
+
+func (e *RequiredParamError) Error() string {
+	return fmt.Sprintf("Query argument %s is required, but not found", e.ParamName)
+}
+
+type RequiredHeaderError struct {
+	ParamName string
+	Err       error
+}
+
+func (e *RequiredHeaderError) Error() string {
+	return fmt.Sprintf("Header parameter %s is required, but not found", e.ParamName)
+}
+
+func (e *RequiredHeaderError) Unwrap() error {
+	return e.Err
+}
+
+type InvalidParamFormatError struct {
+	ParamName string
+	Err       error
+}
+
+func (e *InvalidParamFormatError) Error() string {
+	return fmt.Sprintf("Invalid format for parameter %s: %s", e.ParamName, e.Err.Error())
+}
+
+func (e *InvalidParamFormatError) Unwrap() error {
+	return e.Err
+}
+
+type TooManyValuesForParamError struct {
+	ParamName string
+	Count     int
+}
+
+func (e *TooManyValuesForParamError) Error() string {
+	return fmt.Sprintf("Expected one value for %s, got %d", e.ParamName, e.Count)
+}
+
+// Handler creates http.Handler with routing matching OpenAPI spec.
+func Handler(si ServerInterface) http.Handler {
+	return HandlerWithOptions(si, GorillaServerOptions{})
+}
+
+type GorillaServerOptions struct {
+	BaseURL          string
+	BaseRouter       *mux.Router
+	Middlewares      []MiddlewareFunc
+	ErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error)
+}
+
+// HandlerFromMux creates http.Handler with routing matching OpenAPI spec based on the provided mux.
+func HandlerFromMux(si ServerInterface, r *mux.Router) http.Handler {
+	return HandlerWithOptions(si, GorillaServerOptions{
+		BaseRouter: r,
+	})
+}
+
+func HandlerFromMuxWithBaseURL(si ServerInterface, r *mux.Router, baseURL string) http.Handler {
+	return HandlerWithOptions(si, GorillaServerOptions{
+		BaseURL:    baseURL,
+		BaseRouter: r,
+	})
+}
+
+// HandlerWithOptions creates http.Handler with additional options
+func HandlerWithOptions(si ServerInterface, options GorillaServerOptions) http.Handler {
+	r := options.BaseRouter
+
+	if r == nil {
+		r = mux.NewRouter()
+	}
+	if options.ErrorHandlerFunc == nil {
+		options.ErrorHandlerFunc = func(w http.ResponseWriter, r *http.Request, err error) {
+			http.Error(w, err.Error(), http.StatusBadRequest)
+		}
+	}
+	wrapper := ServerInterfaceWrapper{
+		Handler:            si,
+		HandlerMiddlewares: options.Middlewares,
+		ErrorHandlerFunc:   options.ErrorHandlerFunc,
+	}
+
+	r.HandleFunc(options.BaseURL+"/pets", wrapper.FindPets).Methods("GET")
+
+	r.HandleFunc(options.BaseURL+"/pets", wrapper.AddPet).Methods("POST")
+
+	r.HandleFunc(options.BaseURL+"/pets/{id}", wrapper.DeletePet).Methods("DELETE")
+
+	r.HandleFunc(options.BaseURL+"/pets/{id}", wrapper.FindPetByID).Methods("GET")
+
+	return r
+}
+
+// Base64 encoded, gzipped, json marshaled Swagger object
+var swaggerSpec = []string{
+
+	"H4sIAAAAAAAC/+RXW48budH9KwV+32OnNbEXedBTvB4vICBrT+LdvKznoYZdkmrBSw9Z1FgY6L8HRbZu",
+	"I3k2QYIgQV506WY1T51zqlj9bGz0YwwUJJv5s8l2TR7rzw8pxaQ/xhRHSsJUL9s4kH4PlG3iUTgGM2+L",
+	"od7rzDImj2LmhoO8fWM6I9uR2l9aUTK7znjKGVfffND+9iE0S+KwMrtdZxI9Fk40mPkvZtpwv/x+15mP",
+	"9HRHcok7oL+y3Uf0BHEJsiYYSS437Izg6jLup+34etwLoHV3hTdhQ+c+Lc38l2fz/4mWZm7+b3YUYjap",
+	"MJty2XUvk+HhEtLPgR8LAQ/nuE7F+MN3V8R4gZQHc7+73+llDsvYJA+CtuImj+zM3ODIQuj/mJ9wtaLU",
+	"czTdRLH53K7Bu7sF/EToTWdK0qC1yDifzU5idt2LJN5BRj86qsGyRoGSKQNqMlliIsAMGIC+tmUSYSAf",
+	"Q5aEQrAklJIoA4dKwaeRgj7pbX8DeSTLS7ZYt+qMY0sh09Eb5t2Idk3wpr85g5zns9nT01OP9XYf02o2",
+	"xebZnxbvP3z8/OF3b/qbfi3eVcNQ8vnT8jOlDVu6lvesLpmpGCzulLO7KU3TmQ2l3Ej5fX/T3+iT40gB",
+	"RzZz87Ze6syIsq6OmClB+mPVDHZO619ISgoZ0LnKJCxT9JWhvM1CvlGt/0umBGsl2VrKGSR+CR/RQ6YB",
+	"bAwDewpSPFCWHn5EshQwg5AfY4KMKxbhDBlHptBBIAtpHYMtGTL5kwUsgJ6kh3cUCAOgwCrhhgcELKtC",
+	"HaAFRlsc19Ae3peEDywlQRw4gouJfAcxBUwEtCIBcjShC2Q7sCXlkrUgHFkpuYfbwhk8g5Q0cu5gLG7D",
+	"AZPuRSlq0h0IB8tDCQIbTFwy/FqyxB4WAdZoYa0gMGeC0aEQwsBWilc6Fq2kNBcceORsOawAg2g2x9wd",
+	"r4rDQ+bjGhNJwj2Juh58dJSFCdiPlAZWpv7KG/QtIXT8WNDDwKjMJMzwqLltyLFAiAEkJolJKeElheGw",
+	"ew93CSlTEIVJgf0RQEkBYRNdkREFNhQooAJu5OqHx5L0GYtwfPKS0sT6Ei07zmeb1B30ozvqayHHAR2p",
+	"sEOnPFpKKJqYfvfwueSRwsDKskM1zxBdTJ06MJMVdXPNslpFs+5gQ2u2xSFoY0tD8eD4gVLs4ceYHhio",
+	"cPZxOJVBb1djO7QcGPsv4Uv4TENVomRYkprPxYeYagDFo2NSkVR8D1obHusDJ/I5uw6onFVLkxxcUR+q",
+	"O3u4W2Mm51phjJSm8EpzlZcEllgsP5RGOO730XWn8Rtyk3S8oZSwO99a6wR46A6FGPhh3cPPAiM5R0Eo",
+	"67kxxlxIK2lfRD0oFbivAi26PZf7J+3Tqkx2FcjBFqEEC5I4Sz2WNixIPfxQsiUgqd1gKHyoAu0U2ZKj",
+	"xBVO8+8+wKtbClbz2OIzBvC40pTJTWr18OfSQn10qltTj0rzzhFKd2g+gMVqkbSVkz1b2pM5piZzqEY1",
+	"iwoMHLojlKlwA2feA86KwbKUgRVqzghF9j6bhGw7nZFW9+vh7lSYytyEcUwkXPxJ52qmKd2Jv7X19l/0",
+	"iNORoR53i8HMzQ8cBj1f6rGRlABKuc4g54eF4Er7PizZCSV42BodBczcPBZK2+M5r+tMN42MdSoR8vUM",
+	"upyh2gVMCbf6P8u2Hns6nNTx5hyBx6/stY0X/0BJ55lEuTipsFI9y76BybFnOQP1m8Po7l4HoDxqa6no",
+	"39zc7KceCm1aG0c3DQ6zX7NCfL6W9mujXJvjXhCxu5h/RhLYg2nT0RKLk38Iz2sw2lB/ZeMS6OuorVV7",
+	"cFvTmVy8x7S9MkAotjHmK6PG+0QodWQL9KRr97NYnWv0DG7YdYmOc87FJxouzPpuUK+aNptSlu/jsP2X",
+	"sbCfqy9puCNRj+Ew6NcBtjmdkSUV2v2TnvlNq/z3WONC8Hq/zqOzZx52zSKO5MrrV7uusZnDytV3FnhA",
+	"bbOxuWZxC7loTlc8clujm01e7WiLW+0hY9N2wjL1Dx2gj+2Dhwulv9VLrr9LXfaS7y6zViANxfCfJOTt",
+	"QYyqwhYWtwrv9ReKc8UOOi5uv3X8fL+t9/5+vZYkdv1vk+t/toxfKNrUr0sobfYynb3H71/J+5MXW307",
+	"3d3v/hYAAP//wO3O5VcSAAA=",
+}
+
+// GetSwagger returns the content of the embedded swagger specification file
+// or error if failed to decode
+func decodeSpec() ([]byte, error) {
+	zipped, err := base64.StdEncoding.DecodeString(strings.Join(swaggerSpec, ""))
+	if err != nil {
+		return nil, fmt.Errorf("error base64 decoding spec: %s", err)
+	}
+	zr, err := gzip.NewReader(bytes.NewReader(zipped))
+	if err != nil {
+		return nil, fmt.Errorf("error decompressing spec: %s", err)
+	}
+	var buf bytes.Buffer
+	_, err = buf.ReadFrom(zr)
+	if err != nil {
+		return nil, fmt.Errorf("error decompressing spec: %s", err)
+	}
+
+	return buf.Bytes(), nil
+}
+
+var rawSpec = decodeSpecCached()
+
+// a naive cached of a decoded swagger spec
+func decodeSpecCached() func() ([]byte, error) {
+	data, err := decodeSpec()
+	return func() ([]byte, error) {
+		return data, err
+	}
+}
+
+// Constructs a synthetic filesystem for resolving external references when loading openapi specifications.
+func PathToRawSpec(pathToFile string) map[string]func() ([]byte, error) {
+	var res = make(map[string]func() ([]byte, error))
+	if len(pathToFile) > 0 {
+		res[pathToFile] = rawSpec
+	}
+
+	return res
+}
+
+// GetSwagger returns the Swagger specification corresponding to the generated code
+// in this file. The external references of Swagger specification are resolved.
+// The logic of resolving external references is tightly connected to "import-mapping" feature.
+// Externally referenced files must be embedded in the corresponding golang packages.
+// Urls can be supported but this task was out of the scope.
+func GetSwagger() (swagger *openapi3.T, err error) {
+	var resolvePath = PathToRawSpec("")
+
+	loader := openapi3.NewLoader()
+	loader.IsExternalRefsAllowed = true
+	loader.ReadFromURIFunc = func(loader *openapi3.Loader, url *url.URL) ([]byte, error) {
+		var pathToFile = url.String()
+		pathToFile = path.Clean(pathToFile)
+		getSpec, ok := resolvePath[pathToFile]
+		if !ok {
+			err1 := fmt.Errorf("path not found: %s", pathToFile)
+			return nil, err1
+		}
+		return getSpec()
+	}
+	var specData []byte
+	specData, err = rawSpec()
+	if err != nil {
+		return
+	}
+	swagger, err = loader.LoadFromData(specData)
+	if err != nil {
+		return
+	}
+	return
+}
diff --git a/examples/petstore-expanded/gorilla/api/petstore.go b/examples/petstore-expanded/gorilla/api/petstore.go
new file mode 100644
index 0000000..ea8c34a
--- /dev/null
+++ b/examples/petstore-expanded/gorilla/api/petstore.go
@@ -0,0 +1,128 @@
+//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen --config=cfg.yaml ../../petstore-expanded.yaml
+
+package api
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"sync"
+)
+
+type PetStore struct {
+	Pets   map[int64]Pet
+	NextId int64
+	Lock   sync.Mutex
+}
+
+// Make sure we conform to ServerInterface
+
+var _ ServerInterface = (*PetStore)(nil)
+
+func NewPetStore() *PetStore {
+	return &PetStore{
+		Pets:   make(map[int64]Pet),
+		NextId: 1000,
+	}
+}
+
+// This function wraps sending of an error in the Error format, and
+// handling the failure to marshal that.
+func sendPetstoreError(w http.ResponseWriter, code int, message string) {
+	petErr := Error{
+		Code:    int32(code),
+		Message: message,
+	}
+	w.WriteHeader(code)
+	json.NewEncoder(w).Encode(petErr)
+}
+
+// Here, we implement all of the handlers in the ServerInterface
+func (p *PetStore) FindPets(w http.ResponseWriter, r *http.Request, params FindPetsParams) {
+	p.Lock.Lock()
+	defer p.Lock.Unlock()
+
+	var result []Pet
+
+	for _, pet := range p.Pets {
+		if params.Tags != nil {
+			// If we have tags,  filter pets by tag
+			for _, t := range *params.Tags {
+				if pet.Tag != nil && (*pet.Tag == t) {
+					result = append(result, pet)
+				}
+			}
+		} else {
+			// Add all pets if we're not filtering
+			result = append(result, pet)
+		}
+
+		if params.Limit != nil {
+			l := int(*params.Limit)
+			if len(result) >= l {
+				// We're at the limit
+				break
+			}
+		}
+	}
+
+	w.WriteHeader(http.StatusOK)
+	json.NewEncoder(w).Encode(result)
+}
+
+func (p *PetStore) AddPet(w http.ResponseWriter, r *http.Request) {
+	// We expect a NewPet object in the request body.
+	var newPet NewPet
+	if err := json.NewDecoder(r.Body).Decode(&newPet); err != nil {
+		sendPetstoreError(w, http.StatusBadRequest, "Invalid format for NewPet")
+		return
+	}
+
+	// We now have a pet, let's add it to our "database".
+
+	// We're always asynchronous, so lock unsafe operations below
+	p.Lock.Lock()
+	defer p.Lock.Unlock()
+
+	// We handle pets, not NewPets, which have an additional ID field
+	var pet Pet
+	pet.Name = newPet.Name
+	pet.Tag = newPet.Tag
+	pet.Id = p.NextId
+	p.NextId = p.NextId + 1
+
+	// Insert into map
+	p.Pets[pet.Id] = pet
+
+	// Now, we have to return the NewPet
+	w.WriteHeader(http.StatusCreated)
+	json.NewEncoder(w).Encode(pet)
+}
+
+func (p *PetStore) FindPetByID(w http.ResponseWriter, r *http.Request, id int64) {
+	p.Lock.Lock()
+	defer p.Lock.Unlock()
+
+	pet, found := p.Pets[id]
+	if !found {
+		sendPetstoreError(w, http.StatusNotFound, fmt.Sprintf("Could not find pet with ID %d", id))
+		return
+	}
+
+	w.WriteHeader(http.StatusOK)
+	json.NewEncoder(w).Encode(pet)
+}
+
+func (p *PetStore) DeletePet(w http.ResponseWriter, r *http.Request, id int64) {
+	p.Lock.Lock()
+	defer p.Lock.Unlock()
+
+	_, found := p.Pets[id]
+	if !found {
+		sendPetstoreError(w, http.StatusNotFound, fmt.Sprintf("Could not find pet with ID %d", id))
+		return
+	}
+	delete(p.Pets, id)
+
+	w.WriteHeader(http.StatusNoContent)
+}
diff --git a/examples/petstore-expanded/gorilla/petstore.go b/examples/petstore-expanded/gorilla/petstore.go
new file mode 100644
index 0000000..b31239b
--- /dev/null
+++ b/examples/petstore-expanded/gorilla/petstore.go
@@ -0,0 +1,53 @@
+// This is an example of implementing the Pet Store from the OpenAPI documentation
+// found at:
+// https://github.com/OAI/OpenAPI-Specification/blob/master/examples/v3.0/petstore.yaml
+
+package main
+
+import (
+	"flag"
+	"fmt"
+	"log"
+	"net/http"
+	"os"
+
+	api "github.com/deepmap/oapi-codegen/examples/petstore-expanded/gorilla/api"
+	middleware "github.com/deepmap/oapi-codegen/pkg/chi-middleware"
+	"github.com/gorilla/mux"
+)
+
+func main() {
+	var port = flag.Int("port", 8080, "Port for test HTTP server")
+	flag.Parse()
+
+	swagger, err := api.GetSwagger()
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "Error loading swagger spec\n: %s", err)
+		os.Exit(1)
+	}
+
+	// Clear out the servers array in the swagger spec, that skips validating
+	// that server names match. We don't know how this thing will be run.
+	swagger.Servers = nil
+
+	// Create an instance of our handler which satisfies the generated interface
+	petStore := api.NewPetStore()
+
+	// This is how you set up a basic Gorilla router
+	r := mux.NewRouter()
+
+	// Use our validation middleware to check all requests against the
+	// OpenAPI schema.
+	r.Use(middleware.OapiRequestValidator(swagger))
+
+	// We now register our petStore above as the handler for the interface
+	api.HandlerFromMux(petStore, r)
+
+	s := &http.Server{
+		Handler: r,
+		Addr:    fmt.Sprintf("0.0.0.0:%d", *port),
+	}
+
+	// And we serve HTTP until the world ends.
+	log.Fatal(s.ListenAndServe())
+}
diff --git a/go.mod b/go.mod
index 7d46fee..60272ca 100644
--- a/go.mod
+++ b/go.mod
@@ -11,6 +11,7 @@ require (
 	github.com/golang/protobuf v1.5.2 // indirect
 	github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219
 	github.com/google/uuid v1.3.0
+	github.com/gorilla/mux v1.8.0
 	github.com/json-iterator/go v1.1.12 // indirect
 	github.com/labstack/echo/v4 v4.7.2
 	github.com/lestrrat-go/blackmagic v1.0.1 // indirect
@@ -30,6 +31,7 @@ require (
 	golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f // indirect
 	google.golang.org/protobuf v1.28.0 // indirect
 	gopkg.in/yaml.v2 v2.4.0
+	gopkg.in/yaml.v3 v3.0.1 // indirect
 )
 
 go 1.16
diff --git a/go.sum b/go.sum
index 98ca6d5..5329680 100644
--- a/go.sum
+++ b/go.sum
@@ -206,3 +206,5 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
 gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/pkg/codegen/codegen.go b/pkg/codegen/codegen.go
index 0fc91dc..a997164 100644
--- a/pkg/codegen/codegen.go
+++ b/pkg/codegen/codegen.go
@@ -176,6 +176,14 @@ func Generate(spec *openapi3.T, opts Configuration) (string, error) {
 		}
 	}
 
+	var gorillaServerOut string
+	if opts.Generate.GorillaServer {
+		gorillaServerOut, err = GenerateGorillaServer(t, ops)
+		if err != nil {
+			return "", fmt.Errorf("error generating Go handlers for Paths: %w", err)
+		}
+	}
+
 	var clientOut string
 	if opts.Generate.Client {
 		clientOut, err = GenerateClient(t, ops)
@@ -256,6 +264,13 @@ func Generate(spec *openapi3.T, opts Configuration) (string, error) {
 		}
 	}
 
+	if opts.Generate.GorillaServer {
+		_, err = w.WriteString(gorillaServerOut)
+		if err != nil {
+			return "", fmt.Errorf("error writing server path handlers: %w", err)
+		}
+	}
+
 	if opts.Generate.EmbeddedSpec {
 		_, err = w.WriteString(inlinedSpec)
 		if err != nil {
@@ -329,7 +344,12 @@ func GenerateTypeDefinitions(t *template.Template, swagger *openapi3.T, ops []Op
 		return "", fmt.Errorf("error generating allOf boilerplate: %w", err)
 	}
 
-	typeDefinitions := strings.Join([]string{enumsOut, typesOut, paramTypesOut, allOfBoilerplate}, "")
+	unionBoilerplate, err := GenerateUnionBoilerplate(t, allTypes)
+	if err != nil {
+		return "", fmt.Errorf("error generating union boilerplate: %w", err)
+	}
+
+	typeDefinitions := strings.Join([]string{enumsOut, typesOut, paramTypesOut, allOfBoilerplate, unionBoilerplate}, "")
 	return typeDefinitions, nil
 }
 
@@ -679,6 +699,27 @@ func GenerateAdditionalPropertyBoilerplate(t *template.Template, typeDefs []Type
 	return GenerateTemplates([]string{"additional-properties.tmpl"}, t, context)
 }
 
+func GenerateUnionBoilerplate(t *template.Template, typeDefs []TypeDefinition) (string, error) {
+	var filteredTypes []TypeDefinition
+	for _, t := range typeDefs {
+		if len(t.Schema.UnionElements) != 0 {
+			filteredTypes = append(filteredTypes, t)
+		}
+	}
+
+	if len(filteredTypes) == 0 {
+		return "", nil
+	}
+
+	context := struct {
+		Types []TypeDefinition
+	}{
+		Types: filteredTypes,
+	}
+
+	return GenerateTemplates([]string{"union.tmpl"}, t, context)
+}
+
 // SanitizeCode runs sanitizers across the generated Go code to ensure the
 // generated code will be able to compile.
 func SanitizeCode(goCode string) string {
diff --git a/pkg/codegen/configuration.go b/pkg/codegen/configuration.go
index 084ab5b..5dd98eb 100644
--- a/pkg/codegen/configuration.go
+++ b/pkg/codegen/configuration.go
@@ -16,12 +16,13 @@ type Configuration struct {
 
 // GenerateOptions specifies which supported output formats to generate.
 type GenerateOptions struct {
-	ChiServer    bool `yaml:"chi-server,omitempty"`    // ChiServer specifies whether to generate chi server boilerplate
-	EchoServer   bool `yaml:"echo-server,omitempty"`   // EchoServer specifies whether to generate echo server boilerplate
-	GinServer    bool `yaml:"gin-server,omitempty"`    // GinServer specifies whether to generate echo server boilerplate
-	Client       bool `yaml:"client,omitempty"`        // Client specifies whether to generate client boilerplate
-	Models       bool `yaml:"models,omitempty"`        // Models specifies whether to generate type definitions
-	EmbeddedSpec bool `yaml:"embedded-spec,omitempty"` // Whether to embed the swagger spec in the generated code
+	ChiServer     bool `yaml:"chi-server,omitempty"`     // ChiServer specifies whether to generate chi server boilerplate
+	EchoServer    bool `yaml:"echo-server,omitempty"`    // EchoServer specifies whether to generate echo server boilerplate
+	GinServer     bool `yaml:"gin-server,omitempty"`     // GinServer specifies whether to generate echo server boilerplate
+	GorillaServer bool `yaml:"gorilla-server,omitempty"` // GorillaServer specifies whether to generate Gorilla server boilerplate
+	Client        bool `yaml:"client,omitempty"`         // Client specifies whether to generate client boilerplate
+	Models        bool `yaml:"models,omitempty"`         // Models specifies whether to generate type definitions
+	EmbeddedSpec  bool `yaml:"embedded-spec,omitempty"`  // Whether to embed the swagger spec in the generated code
 }
 
 // CompatibilityOptions specifies backward compatibility settings for the
diff --git a/pkg/codegen/operations.go b/pkg/codegen/operations.go
index 3196d5c..e858482 100644
--- a/pkg/codegen/operations.go
+++ b/pkg/codegen/operations.go
@@ -683,6 +683,12 @@ func GenerateGinServer(t *template.Template, operations []OperationDefinition) (
 	return GenerateTemplates([]string{"gin/gin-interface.tmpl", "gin/gin-wrappers.tmpl", "gin/gin-register.tmpl"}, t, operations)
 }
 
+// GenerateGinServer This function generates all the go code for the ServerInterface as well as
+// all the wrapper functions around our handlers.
+func GenerateGorillaServer(t *template.Template, operations []OperationDefinition) (string, error) {
+	return GenerateTemplates([]string{"gorilla/gorilla-interface.tmpl", "gorilla/gorilla-middleware.tmpl", "gorilla/gorilla-register.tmpl"}, t, operations)
+}
+
 // Uses the template engine to generate the function which registers our wrappers
 // as Echo path handlers.
 func GenerateClient(t *template.Template, ops []OperationDefinition) (string, error) {
diff --git a/pkg/codegen/schema.go b/pkg/codegen/schema.go
index 2b44397..09cd2cc 100644
--- a/pkg/codegen/schema.go
+++ b/pkg/codegen/schema.go
@@ -27,6 +27,9 @@ type Schema struct {
 
 	Description string // The description of the element
 
+	UnionElements []string       // Possible elements of oneOf/anyOf union
+	Discriminator *Discriminator // Describes which value is stored in a union
+
 	// If this is set, the schema will declare a type via alias, eg,
 	// `type Foo = bool`. If this is not set, we will define this type via
 	// type definition `type Foo bool`
@@ -172,6 +175,22 @@ func (t *TypeDefinition) IsAlias() bool {
 	return !globalState.options.Compatibility.OldAliasing && t.Schema.DefineViaAlias
 }
 
+type Discriminator struct {
+	// maps discriminator value to go type
+	Mapping map[string]string
+
+	// JSON property name that holds the discriminator
+	Property string
+}
+
+func (d *Discriminator) JSONTag() string {
+	return fmt.Sprintf("`json:\"%s\"`", d.Property)
+}
+
+func (d *Discriminator) PropertyName() string {
+	return SchemaNameToTypeName(d.Property)
+}
+
 func PropertiesEqual(a, b Property) bool {
 	return a.JsonFieldName == b.JsonFieldName && a.Schema.TypeDecl() == b.Schema.TypeDecl() && a.Required == b.Required
 }
@@ -207,17 +226,6 @@ func GenerateGoSchema(sref *openapi3.SchemaRef, path []string) (Schema, error) {
 		OAPISchema:  schema,
 	}
 
-	// We can't support this in any meaningful way
-	if schema.AnyOf != nil {
-		outSchema.GoType = "interface{}"
-		return outSchema, nil
-	}
-	// We can't support this in any meaningful way
-	if schema.OneOf != nil {
-		outSchema.GoType = "interface{}"
-		return outSchema, nil
-	}
-
 	// AllOf is interesting, and useful. It's the union of a number of other
 	// schemas. A common usage is to create a union of an object with an ID,
 	// so that in a RESTful paradigm, the Create operation can return
@@ -247,7 +255,7 @@ func GenerateGoSchema(sref *openapi3.SchemaRef, path []string) (Schema, error) {
 	if t == "" || t == "object" {
 		var outType string
 
-		if len(schema.Properties) == 0 && !SchemaHasAdditionalProperties(schema) {
+		if len(schema.Properties) == 0 && !SchemaHasAdditionalProperties(schema) && schema.AnyOf == nil && schema.OneOf == nil {
 			// If the object has no properties or additional properties, we
 			// have some special cases for its type.
 			if t == "object" {
@@ -276,8 +284,8 @@ func GenerateGoSchema(sref *openapi3.SchemaRef, path []string) (Schema, error) {
 
 				required := StringInArray(pName, schema.Required)
 
-				if pSchema.HasAdditionalProperties && pSchema.RefType == "" {
-					// If we have fields present which have additional properties,
+				if (pSchema.HasAdditionalProperties || len(pSchema.UnionElements) != 0) && pSchema.RefType == "" {
+					// If we have fields present which have additional properties or union values,
 					// but are not a pre-defined type, we need to define a type
 					// for them, which will be based on the field names we followed
 					// to get to the type.
@@ -318,7 +326,34 @@ func GenerateGoSchema(sref *openapi3.SchemaRef, path []string) (Schema, error) {
 				if err != nil {
 					return Schema{}, fmt.Errorf("error generating type for additional properties: %w", err)
 				}
+				if additionalSchema.HasAdditionalProperties || len(additionalSchema.UnionElements) != 0 {
+					// If we have fields present which have additional properties or union values,
+					// but are not a pre-defined type, we need to define a type
+					// for them, which will be based on the field names we followed
+					// to get to the type.
+					typeName := PathToTypeName(append(path, "AdditionalProperties"))
+
+					typeDef := TypeDefinition{
+						TypeName: typeName,
+						JsonName: strings.Join(append(path, "AdditionalProperties"), "."),
+						Schema:   additionalSchema,
+					}
+					additionalSchema.RefType = typeName
+					additionalSchema.AdditionalTypes = append(additionalSchema.AdditionalTypes, typeDef)
+				}
 				outSchema.AdditionalPropertiesType = &additionalSchema
+				outSchema.AdditionalTypes = append(outSchema.AdditionalTypes, additionalSchema.AdditionalTypes...)
+			}
+
+			if schema.AnyOf != nil {
+				if err := generateUnion(&outSchema, schema.AnyOf, schema.Discriminator, path); err != nil {
+					return Schema{}, fmt.Errorf("error generating type for anyOf: %w", err)
+				}
+			}
+			if schema.OneOf != nil {
+				if err := generateUnion(&outSchema, schema.OneOf, schema.Discriminator, path); err != nil {
+					return Schema{}, fmt.Errorf("error generating type for oneOf: %w", err)
+				}
 			}
 
 			outSchema.GoType = GenStructFromSchema(outSchema)
@@ -365,7 +400,7 @@ func GenerateGoSchema(sref *openapi3.SchemaRef, path []string) (Schema, error) {
 			outSchema.AdditionalTypes = append(outSchema.AdditionalTypes, typeDef)
 			outSchema.RefType = typeName
 		}
-		//outSchema.RefType = typeName
+		// outSchema.RefType = typeName
 	} else {
 		err := oapiSchemaToGoType(schema, path, &outSchema)
 		if err != nil {
@@ -389,6 +424,22 @@ func oapiSchemaToGoType(schema *openapi3.Schema, path []string, outSchema *Schem
 		if err != nil {
 			return fmt.Errorf("error generating type for array: %w", err)
 		}
+		if (arrayType.HasAdditionalProperties || len(arrayType.UnionElements) != 0) && arrayType.RefType == "" {
+			// If we have items which have additional properties or union values,
+			// but are not a pre-defined type, we need to define a type
+			// for them, which will be based on the field names we followed
+			// to get to the type.
+			typeName := PathToTypeName(append(path, "Item"))
+
+			typeDef := TypeDefinition{
+				TypeName: typeName,
+				JsonName: strings.Join(append(path, "Item"), "."),
+				Schema:   arrayType,
+			}
+			arrayType.AdditionalTypes = append(arrayType.AdditionalTypes, typeDef)
+
+			arrayType.RefType = typeName
+		}
 		outSchema.ArrayType = &arrayType
 		outSchema.GoType = "[]" + arrayType.TypeDecl()
 		outSchema.AdditionalTypes = arrayType.AdditionalTypes
@@ -562,6 +613,9 @@ func GenStructFromSchema(schema Schema) string {
 		objectParts = append(objectParts,
 			fmt.Sprintf("AdditionalProperties map[string]%s `json:\"-\"`", addPropsType))
 	}
+	if len(schema.UnionElements) != 0 {
+		objectParts = append(objectParts, "union json.RawMessage")
+	}
 	objectParts = append(objectParts, "}")
 	return strings.Join(objectParts, "\n")
 }
@@ -601,3 +655,40 @@ func paramToGoType(param *openapi3.Parameter, path []string) (Schema, error) {
 	// For json, we go through the standard schema mechanism
 	return GenerateGoSchema(mt.Schema, path)
 }
+
+func generateUnion(outSchema *Schema, elements openapi3.SchemaRefs, discriminator *openapi3.Discriminator, path []string) error {
+	if discriminator != nil {
+		outSchema.Discriminator = &Discriminator{
+			Property: discriminator.PropertyName,
+			Mapping:  make(map[string]string),
+		}
+	}
+
+	refToGoTypeMap := make(map[string]string)
+	for i, element := range elements {
+		elementSchema, err := GenerateGoSchema(element, path)
+		if err != nil {
+			return err
+		}
+
+		if element.Ref == "" {
+			td := TypeDefinition{Schema: elementSchema, TypeName: SchemaNameToTypeName(PathToTypeName(append(path, fmt.Sprint(i))))}
+			outSchema.AdditionalTypes = append(outSchema.AdditionalTypes, td)
+			elementSchema.GoType = td.TypeName
+		} else {
+			refToGoTypeMap[element.Ref] = elementSchema.GoType
+		}
+
+		if discriminator != nil {
+			for k, v := range discriminator.Mapping {
+				if v == element.Ref {
+					outSchema.Discriminator.Mapping[k] = elementSchema.GoType
+					break
+				}
+			}
+		}
+		outSchema.UnionElements = append(outSchema.UnionElements, elementSchema.GoType)
+	}
+
+	return nil
+}
diff --git a/pkg/codegen/template_helpers.go b/pkg/codegen/template_helpers.go
index eeb7810..3ff5e1e 100644
--- a/pkg/codegen/template_helpers.go
+++ b/pkg/codegen/template_helpers.go
@@ -274,6 +274,7 @@ var TemplateFunctions = template.FuncMap{
 	"swaggerUriToEchoUri":        SwaggerUriToEchoUri,
 	"swaggerUriToChiUri":         SwaggerUriToChiUri,
 	"swaggerUriToGinUri":         SwaggerUriToGinUri,
+	"swaggerUriToGorillaUri":     SwaggerUriToGorillaUri,
 	"lcFirst":                    LowercaseFirstCharacter,
 	"ucFirst":                    UppercaseFirstCharacter,
 	"camelCase":                  ToCamelCase,
diff --git a/pkg/codegen/templates/chi/chi-middleware.tmpl b/pkg/codegen/templates/chi/chi-middleware.tmpl
index 7c1385b..30270cc 100644
--- a/pkg/codegen/templates/chi/chi-middleware.tmpl
+++ b/pkg/codegen/templates/chi/chi-middleware.tmpl
@@ -5,7 +5,7 @@ type ServerInterfaceWrapper struct {
     ErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error)
 }
 
-type MiddlewareFunc func(http.HandlerFunc) http.HandlerFunc
+type MiddlewareFunc func(http.Handler) http.Handler
 
 {{range .}}{{$opid := .OperationId}}
 
@@ -168,15 +168,15 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(w http.ResponseWriter, r *http.Requ
     {{end}}
   {{end}}
 
-  var handler = func(w http.ResponseWriter, r *http.Request) {
+  var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
     siw.Handler.{{.OperationId}}(w, r{{genParamNames .PathParams}}{{if .RequiresParamObject}}, params{{end}})
-}
+  })
 
   for _, middleware := range siw.HandlerMiddlewares {
     handler = middleware(handler)
   }
 
-  handler(w, r.WithContext(ctx))
+  handler.ServeHTTP(w, r.WithContext(ctx))
 }
 {{end}}
 
diff --git a/pkg/codegen/templates/gorilla/gorilla-interface.tmpl b/pkg/codegen/templates/gorilla/gorilla-interface.tmpl
new file mode 100644
index 0000000..79a51fd
--- /dev/null
+++ b/pkg/codegen/templates/gorilla/gorilla-interface.tmpl
@@ -0,0 +1,7 @@
+// ServerInterface represents all server handlers.
+type ServerInterface interface {
+{{range .}}{{.SummaryAsComment }}
+// ({{.Method}} {{.Path}})
+{{.OperationId}}(w http.ResponseWriter, r *http.Request{{genParamArgs .PathParams}}{{if .RequiresParamObject}}, params {{.OperationId}}Params{{end}})
+{{end}}
+}
diff --git a/pkg/codegen/templates/gorilla/gorilla-middleware.tmpl b/pkg/codegen/templates/gorilla/gorilla-middleware.tmpl
new file mode 100644
index 0000000..a08bc5e
--- /dev/null
+++ b/pkg/codegen/templates/gorilla/gorilla-middleware.tmpl
@@ -0,0 +1,251 @@
+// ServerInterfaceWrapper converts contexts to parameters.
+type ServerInterfaceWrapper struct {
+    Handler ServerInterface
+    HandlerMiddlewares []MiddlewareFunc
+    ErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error)
+}
+
+type MiddlewareFunc func(http.HandlerFunc) http.HandlerFunc
+
+{{range .}}{{$opid := .OperationId}}
+
+// {{$opid}} operation middleware
+func (siw *ServerInterfaceWrapper) {{$opid}}(w http.ResponseWriter, r *http.Request) {
+  ctx := r.Context()
+  {{if or .RequiresParamObject (gt (len .PathParams) 0) }}
+  var err error
+  {{end}}
+
+  {{range .PathParams}}// ------------- Path parameter "{{.ParamName}}" -------------
+  var {{$varName := .GoVariableName}}{{$varName}} {{.TypeDef}}
+
+  {{if .IsPassThrough}}
+  {{$varName}} = mux.Vars(r)["{{.ParamName}}"]
+  {{end}}
+  {{if .IsJson}}
+  err = json.Unmarshal([]byte(mux.Vars(r)["{{.ParamName}}"]), &{{$varName}})
+  if err != nil {
+    siw.ErrorHandlerFunc(w, r, &UnmarshalingParamError{ParamName: "{{.ParamName}}", Err: err})
+    return
+  }
+  {{end}}
+  {{if .IsStyled}}
+  err = runtime.BindStyledParameter("{{.Style}}",{{.Explode}}, "{{.ParamName}}", mux.Vars(r)["{{.ParamName}}"], &{{$varName}})
+  if err != nil {
+    siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "{{.ParamName}}", Err: err})
+    return
+  }
+  {{end}}
+
+  {{end}}
+
+{{range .SecurityDefinitions}}
+  ctx = context.WithValue(ctx, {{.ProviderName | ucFirst}}Scopes, {{toStringArray .Scopes}})
+{{end}}
+
+  {{if .RequiresParamObject}}
+    // Parameter object where we will unmarshal all parameters from the context
+    var params {{.OperationId}}Params
+
+    {{range $paramIdx, $param := .QueryParams}}// ------------- {{if .Required}}Required{{else}}Optional{{end}} query parameter "{{.ParamName}}" -------------
+      if paramValue := r.URL.Query().Get("{{.ParamName}}"); paramValue != "" {
+
+      {{if .IsPassThrough}}
+        params.{{.GoName}} = {{if not .Required}}&{{end}}paramValue
+      {{end}}
+
+      {{if .IsJson}}
+        var value {{.TypeDef}}
+        err = json.Unmarshal([]byte(paramValue), &value)
+        if err != nil {
+          siw.ErrorHandlerFunc(w, r, &UnmarshalingParamError{ParamName: "{{.ParamName}}", Err: err})
+          return
+        }
+
+        params.{{.GoName}} = {{if not .Required}}&{{end}}value
+      {{end}}
+      }{{if .Required}} else {
+          siw.ErrorHandlerFunc(w, r, &RequiredParamError{ParamName: "{{.ParamName}}"})
+          return
+      }{{end}}
+      {{if .IsStyled}}
+      err = runtime.BindQueryParameter("{{.Style}}", {{.Explode}}, {{.Required}}, "{{.ParamName}}", r.URL.Query(), &params.{{.GoName}})
+      if err != nil {
+        siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "{{.ParamName}}", Err: err})
+        return
+      }
+      {{end}}
+  {{end}}
+
+    {{if .HeaderParams}}
+      headers := r.Header
+
+      {{range .HeaderParams}}// ------------- {{if .Required}}Required{{else}}Optional{{end}} header parameter "{{.ParamName}}" -------------
+        if valueList, found := headers[http.CanonicalHeaderKey("{{.ParamName}}")]; found {
+          var {{.GoName}} {{.TypeDef}}
+          n := len(valueList)
+          if n != 1 {
+            siw.ErrorHandlerFunc(w, r, &TooManyValuesForParamError{ParamName: "{{.ParamName}}", Count: n})
+            return
+          }
+
+        {{if .IsPassThrough}}
+          params.{{.GoName}} = {{if not .Required}}&{{end}}valueList[0]
+        {{end}}
+
+        {{if .IsJson}}
+          err = json.Unmarshal([]byte(valueList[0]), &{{.GoName}})
+          if err != nil {
+            siw.ErrorHandlerFunc(w, r, &UnmarshalingParamError{ParamName: "{{.ParamName}}", Err: err})
+            return
+          }
+        {{end}}
+
+        {{if .IsStyled}}
+          err = runtime.BindStyledParameterWithLocation("{{.Style}}",{{.Explode}}, "{{.ParamName}}", runtime.ParamLocationHeader, valueList[0], &{{.GoName}})
+          if err != nil {
+            siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "{{.ParamName}}", Err: err})
+            return
+          }
+        {{end}}
+
+          params.{{.GoName}} = {{if not .Required}}&{{end}}{{.GoName}}
+
+        } {{if .Required}}else {
+            err := fmt.Errorf("Header parameter {{.ParamName}} is required, but not found")
+            siw.ErrorHandlerFunc(w, r, &RequiredHeaderError{ParamName: "{{.ParamName}}", Err: err})
+            return
+        }{{end}}
+
+      {{end}}
+    {{end}}
+
+    {{range .CookieParams}}
+      var cookie *http.Cookie
+
+      if cookie, err = r.Cookie("{{.ParamName}}"); err == nil {
+
+      {{- if .IsPassThrough}}
+        params.{{.GoName}} = {{if not .Required}}&{{end}}cookie.Value
+      {{end}}
+
+      {{- if .IsJson}}
+        var value {{.TypeDef}}
+        var decoded string
+        decoded, err := url.QueryUnescape(cookie.Value)
+        if err != nil {
+          err = fmt.Errorf("Error unescaping cookie parameter '{{.ParamName}}'")
+          siw.ErrorHandlerFunc(w, r, &UnescapedCookieParamError{ParamName: "{{.ParamName}}", Err: err})
+          return
+        }
+
+        err = json.Unmarshal([]byte(decoded), &value)
+        if err != nil {
+          siw.ErrorHandlerFunc(w, r, &UnmarshalingParamError{ParamName: "{{.ParamName}}", Err: err})
+          return
+        }
+
+        params.{{.GoName}} = {{if not .Required}}&{{end}}value
+      {{end}}
+
+      {{- if .IsStyled}}
+        var value {{.TypeDef}}
+        err = runtime.BindStyledParameter("simple",{{.Explode}}, "{{.ParamName}}", cookie.Value, &value)
+        if err != nil {
+          siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "{{.ParamName}}", Err: err})
+          return
+        }
+        params.{{.GoName}} = {{if not .Required}}&{{end}}value
+      {{end}}
+
+      }
+
+      {{- if .Required}} else {
+        siw.ErrorHandlerFunc(w, r, &RequiredParamError{ParamName: "{{.ParamName}}"})
+        return
+      }
+      {{- end}}
+    {{end}}
+  {{end}}
+
+  var handler = func(w http.ResponseWriter, r *http.Request) {
+    siw.Handler.{{.OperationId}}(w, r{{genParamNames .PathParams}}{{if .RequiresParamObject}}, params{{end}})
+}
+
+  for _, middleware := range siw.HandlerMiddlewares {
+    handler = middleware(handler)
+  }
+
+  handler(w, r.WithContext(ctx))
+}
+{{end}}
+
+type UnescapedCookieParamError struct {
+    ParamName string
+    Err error
+}
+
+func (e *UnescapedCookieParamError) Error() string {
+    return fmt.Sprintf("error unescaping cookie parameter '%s'", e.ParamName)
+}
+
+func (e *UnescapedCookieParamError) Unwrap() error {
+    return e.Err
+}
+
+type UnmarshalingParamError struct {
+    ParamName string
+    Err error
+}
+
+func (e *UnmarshalingParamError) Error() string {
+    return fmt.Sprintf("Error unmarshaling parameter %s as JSON: %s", e.ParamName, e.Err.Error())
+}
+
+func (e *UnmarshalingParamError) Unwrap() error {
+    return e.Err
+}
+
+type RequiredParamError struct {
+    ParamName string
+}
+
+func (e *RequiredParamError) Error() string {
+    return fmt.Sprintf("Query argument %s is required, but not found", e.ParamName)
+}
+
+type RequiredHeaderError struct {
+    ParamName string
+    Err error
+}
+
+func (e *RequiredHeaderError) Error() string {
+    return fmt.Sprintf("Header parameter %s is required, but not found", e.ParamName)
+}
+
+func (e *RequiredHeaderError) Unwrap() error {
+    return e.Err
+}
+
+type InvalidParamFormatError struct {
+    ParamName string
+	  Err error
+}
+
+func (e *InvalidParamFormatError) Error() string {
+    return fmt.Sprintf("Invalid format for parameter %s: %s", e.ParamName, e.Err.Error())
+}
+
+func (e *InvalidParamFormatError) Unwrap() error {
+    return e.Err
+}
+
+type TooManyValuesForParamError struct {
+    ParamName string
+    Count int
+}
+
+func (e *TooManyValuesForParamError) Error() string {
+    return fmt.Sprintf("Expected one value for %s, got %d", e.ParamName, e.Count)
+}
+
diff --git a/pkg/codegen/templates/gorilla/gorilla-register.tmpl b/pkg/codegen/templates/gorilla/gorilla-register.tmpl
new file mode 100644
index 0000000..b019e1d
--- /dev/null
+++ b/pkg/codegen/templates/gorilla/gorilla-register.tmpl
@@ -0,0 +1,49 @@
+// Handler creates http.Handler with routing matching OpenAPI spec.
+func Handler(si ServerInterface) http.Handler {
+  return HandlerWithOptions(si, GorillaServerOptions{})
+}
+
+type GorillaServerOptions struct {
+    BaseURL string
+    BaseRouter *mux.Router
+    Middlewares []MiddlewareFunc
+    ErrorHandlerFunc   func(w http.ResponseWriter, r *http.Request, err error)
+}
+
+// HandlerFromMux creates http.Handler with routing matching OpenAPI spec based on the provided mux.
+func HandlerFromMux(si ServerInterface, r *mux.Router) http.Handler {
+    return HandlerWithOptions(si, GorillaServerOptions {
+        BaseRouter: r,
+    })
+}
+
+func HandlerFromMuxWithBaseURL(si ServerInterface, r *mux.Router, baseURL string) http.Handler {
+    return HandlerWithOptions(si, GorillaServerOptions {
+        BaseURL: baseURL,
+        BaseRouter: r,
+    })
+}
+
+// HandlerWithOptions creates http.Handler with additional options
+func HandlerWithOptions(si ServerInterface, options GorillaServerOptions) http.Handler {
+r := options.BaseRouter
+
+if r == nil {
+r = mux.NewRouter()
+}
+if options.ErrorHandlerFunc == nil {
+    options.ErrorHandlerFunc = func(w http.ResponseWriter, r *http.Request, err error) {
+        http.Error(w, err.Error(), http.StatusBadRequest)
+    }
+}
+{{if .}}wrapper := ServerInterfaceWrapper{
+Handler: si,
+HandlerMiddlewares: options.Middlewares,
+ErrorHandlerFunc: options.ErrorHandlerFunc,
+}
+{{end}}
+{{range .}}
+r.HandleFunc(options.BaseURL+"{{.Path | swaggerUriToGorillaUri }}", wrapper.{{.OperationId}}).Methods("{{.Method }}")
+{{end}}
+return r
+}
diff --git a/pkg/codegen/templates/union.tmpl b/pkg/codegen/templates/union.tmpl
new file mode 100644
index 0000000..a29e2d0
--- /dev/null
+++ b/pkg/codegen/templates/union.tmpl
@@ -0,0 +1,105 @@
+{{range .Types}}
+    {{$typeName := .TypeName -}}
+    {{$discriminator := .Schema.Discriminator}}
+    {{$properties := .Schema.Properties -}}
+    {{range .Schema.UnionElements}}
+        {{$element := . -}}
+        func (t {{$typeName}}) As{{.}}() ({{.}}, error) {
+            var body {{.}}
+            err := json.Unmarshal(t.union, &body)
+            return body, err
+        }
+
+        func (t *{{$typeName}}) From{{.}} (v {{.}}) error {
+            {{if $discriminator -}}
+                {{range $value, $type := $discriminator.Mapping -}}
+                    {{if eq $type $element -}}
+                        {{$hasProperty := false -}}
+                        {{range $properties -}}
+                            {{if eq .GoFieldName $discriminator.PropertyName -}}
+                                t.{{$discriminator.PropertyName}} = "{{$value}}"
+                                {{$hasProperty = true -}}
+                            {{end -}}
+                        {{end -}}
+                        {{if not $hasProperty}}v.{{$discriminator.PropertyName}} = "{{$value}}"{{end}}
+                    {{end -}}
+                {{end -}}
+            {{end -}}
+            b, err := json.Marshal(v)
+            t.union = b
+            return err
+        }
+    {{end}}
+
+    {{if $discriminator}}
+        func (t {{.TypeName}}) Discriminator() (string, error) {
+            var discriminator struct {
+                Discriminator string {{$discriminator.JSONTag}}
+            }
+            err := json.Unmarshal(t.union, &discriminator)
+            return discriminator.Discriminator, err
+        }
+
+        {{if ne 0 (len $discriminator.Mapping)}}
+            func (t {{.TypeName}}) ValueByDiscriminator() (interface{}, error) {
+                discriminator, err := t.Discriminator()
+                if err != nil {
+                    return nil, err
+                }
+                switch discriminator{
+                    {{range $value, $type := $discriminator.Mapping -}}
+                        case "{{$value}}":
+                            return t.As{{$type}}()
+                    {{end -}}
+                    default:
+                        return nil, errors.New("unknown discriminator value: "+discriminator)
+                }
+            }
+        {{end}}
+    {{end}}
+
+    func (t {{.TypeName}}) MarshalJSON() ([]byte, error) {
+        b, err := t.union.MarshalJSON()
+        {{if ne 0 (len .Schema.Properties) -}}
+            if err != nil {
+                return nil, err
+            }
+            object := make(map[string]json.RawMessage)
+            err = json.Unmarshal(b, &object)
+            if err != nil {
+                return nil, err
+            }
+            {{range .Schema.Properties}}
+                object["{{.JsonFieldName}}"], err = json.Marshal(t.{{.GoFieldName}})
+                if err != nil {
+                    return nil, fmt.Errorf("error marshaling '{{.JsonFieldName}}': %w", err)
+                }
+            {{end -}}
+            b, err = json.Marshal(object)
+        {{end -}}
+        return b, err
+    }
+
+    func (t *{{.TypeName}}) UnmarshalJSON(b []byte) error {
+        err := t.union.UnmarshalJSON(b)
+        {{if ne 0 (len .Schema.Properties) -}}
+            if err != nil {
+                return err
+            }
+            object := make(map[string]json.RawMessage)
+            err = json.Unmarshal(b, &object)
+            if err != nil {
+                return err
+            }
+            {{range .Schema.Properties}}
+                if raw, found := object["{{.JsonFieldName}}"]; found {
+                    err = json.Unmarshal(raw, &t.{{.GoFieldName}})
+                    if err != nil {
+                        return fmt.Errorf("error reading '{{.JsonFieldName}}': %w", err)
+                    }
+                }
+            {{end}}
+        {{end -}}
+        return err
+    }
+{{end}}
\ No newline at end of file
diff --git a/pkg/codegen/utils.go b/pkg/codegen/utils.go
index 034442e..c713002 100644
--- a/pkg/codegen/utils.go
+++ b/pkg/codegen/utils.go
@@ -325,6 +325,21 @@ func SwaggerUriToGinUri(uri string) string {
 	return pathParamRE.ReplaceAllString(uri, ":$1")
 }
 
+// This function converts a swagger style path URI with parameters to a
+// Gorilla compatible path URI. We need to replace all of Swagger parameters with
+// ":param". Valid input parameters are:
+//   {param}
+//   {param*}
+//   {.param}
+//   {.param*}
+//   {;param}
+//   {;param*}
+//   {?param}
+//   {?param*}
+func SwaggerUriToGorillaUri(uri string) string {
+	return pathParamRE.ReplaceAllString(uri, "{$1}")
+}
+
 // Returns the argument names, in order, in a given URI string, so for
 // /path/{param1}/{.param2*}/{?param3}, it would return param1, param2, param3
 func OrderedParamsFromUri(uri string) []string {


Test patch:
diff --git a/examples/petstore-expanded/gorilla/petstore_test.go b/examples/petstore-expanded/gorilla/petstore_test.go
new file mode 100644
index 0000000..c325326
--- /dev/null
+++ b/examples/petstore-expanded/gorilla/petstore_test.go
@@ -0,0 +1,168 @@
+package main
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"net/http/httptest"
+	"testing"
+
+	"github.com/gorilla/mux"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+
+	"github.com/deepmap/oapi-codegen/examples/petstore-expanded/gorilla/api"
+	middleware "github.com/deepmap/oapi-codegen/pkg/chi-middleware"
+	"github.com/deepmap/oapi-codegen/pkg/testutil"
+)
+
+func doGet(t *testing.T, mux *mux.Router, url string) *httptest.ResponseRecorder {
+	response := testutil.NewRequest().Get(url).WithAcceptJson().GoWithHTTPHandler(t, mux)
+	return response.Recorder
+}
+
+func TestPetStore(t *testing.T) {
+	var err error
+
+	// Get the swagger description of our API
+	swagger, err := api.GetSwagger()
+	require.NoError(t, err)
+
+	// Clear out the servers array in the swagger spec, that skips validating
+	// that server names match. We don't know how this thing will be run.
+	swagger.Servers = nil
+
+	// This is how you set up a basic Gorilla router
+	r := mux.NewRouter()
+
+	// Use our validation middleware to check all requests against the
+	// OpenAPI schema.
+	r.Use(middleware.OapiRequestValidator(swagger))
+
+	store := api.NewPetStore()
+	api.HandlerFromMux(store, r)
+
+	t.Run("Add pet", func(t *testing.T) {
+		tag := "TagOfSpot"
+		newPet := api.NewPet{
+			Name: "Spot",
+			Tag:  &tag,
+		}
+
+		rr := testutil.NewRequest().Post("/pets").WithJsonBody(newPet).GoWithHTTPHandler(t, r).Recorder
+		assert.Equal(t, http.StatusCreated, rr.Code)
+
+		var resultPet api.Pet
+		err = json.NewDecoder(rr.Body).Decode(&resultPet)
+		assert.NoError(t, err, "error unmarshaling response")
+		assert.Equal(t, newPet.Name, resultPet.Name)
+		assert.Equal(t, *newPet.Tag, *resultPet.Tag)
+	})
+
+	t.Run("Find pet by ID", func(t *testing.T) {
+		pet := api.Pet{
+			Id: 100,
+		}
+
+		store.Pets[pet.Id] = pet
+		rr := doGet(t, r, fmt.Sprintf("/pets/%d", pet.Id))
+
+		var resultPet api.Pet
+		err = json.NewDecoder(rr.Body).Decode(&resultPet)
+		assert.NoError(t, err, "error getting pet")
+		assert.Equal(t, pet, resultPet)
+	})
+
+	t.Run("Pet not found", func(t *testing.T) {
+		rr := doGet(t, r, "/pets/27179095781")
+		assert.Equal(t, http.StatusNotFound, rr.Code)
+
+		var petError api.Error
+		err = json.NewDecoder(rr.Body).Decode(&petError)
+		assert.NoError(t, err, "error getting response", err)
+		assert.Equal(t, int32(http.StatusNotFound), petError.Code)
+	})
+
+	t.Run("List all pets", func(t *testing.T) {
+		store.Pets = map[int64]api.Pet{
+			1: api.Pet{},
+			2: api.Pet{},
+		}
+
+		// Now, list all pets, we should have two
+		rr := doGet(t, r, "/pets")
+		assert.Equal(t, http.StatusOK, rr.Code)
+
+		var petList []api.Pet
+		err = json.NewDecoder(rr.Body).Decode(&petList)
+		assert.NoError(t, err, "error getting response", err)
+		assert.Equal(t, 2, len(petList))
+	})
+
+	t.Run("Filter pets by tag", func(t *testing.T) {
+		tag := "TagOfFido"
+
+		store.Pets = map[int64]api.Pet{
+			1: {
+				Tag: &tag,
+			},
+			2: {},
+		}
+
+		// Filter pets by tag, we should have 1
+		rr := doGet(t, r, "/pets?tags=TagOfFido")
+		assert.Equal(t, http.StatusOK, rr.Code)
+
+		var petList []api.Pet
+		err = json.NewDecoder(rr.Body).Decode(&petList)
+		assert.NoError(t, err, "error getting response", err)
+		assert.Equal(t, 1, len(petList))
+	})
+
+	t.Run("Filter pets by tag", func(t *testing.T) {
+		store.Pets = map[int64]api.Pet{
+			1: api.Pet{},
+			2: api.Pet{},
+		}
+
+		// Filter pets by non existent tag, we should have 0
+		rr := doGet(t, r, "/pets?tags=NotExists")
+		assert.Equal(t, http.StatusOK, rr.Code)
+
+		var petList []api.Pet
+		err = json.NewDecoder(rr.Body).Decode(&petList)
+		assert.NoError(t, err, "error getting response", err)
+		assert.Equal(t, 0, len(petList))
+	})
+
+	t.Run("Delete pets", func(t *testing.T) {
+		store.Pets = map[int64]api.Pet{
+			1: api.Pet{},
+			2: api.Pet{},
+		}
+
+		// Let's delete non-existent pet
+		rr := testutil.NewRequest().Delete("/pets/7").GoWithHTTPHandler(t, r).Recorder
+		assert.Equal(t, http.StatusNotFound, rr.Code)
+
+		var petError api.Error
+		err = json.NewDecoder(rr.Body).Decode(&petError)
+		assert.NoError(t, err, "error unmarshaling PetError")
+		assert.Equal(t, int32(http.StatusNotFound), petError.Code)
+
+		// Now, delete both real pets
+		rr = testutil.NewRequest().Delete("/pets/1").GoWithHTTPHandler(t, r).Recorder
+		assert.Equal(t, http.StatusNoContent, rr.Code)
+
+		rr = testutil.NewRequest().Delete("/pets/2").GoWithHTTPHandler(t, r).Recorder
+		assert.Equal(t, http.StatusNoContent, rr.Code)
+
+		// Should have no pets left.
+		var petList []api.Pet
+		rr = doGet(t, r, "/pets")
+		assert.Equal(t, http.StatusOK, rr.Code)
+		err = json.NewDecoder(rr.Body).Decode(&petList)
+		assert.NoError(t, err, "error getting response", err)
+		assert.Equal(t, 0, len(petList))
+	})
+}
diff --git a/internal/test/components/components.gen.go b/internal/test/components/components.gen.go
index aad97ec..09817cc 100644
--- a/internal/test/components/components.gen.go
+++ b/internal/test/components/components.gen.go
@@ -5,6 +5,7 @@ package components
 
 import (
 	"encoding/json"
+	"errors"
 	"fmt"
 )
 
@@ -126,6 +127,19 @@ type AdditionalPropertiesObject5 struct {
 	AdditionalProperties map[string]SchemaObject `json:"-"`
 }
 
+// Array of object with additional properties
+type AdditionalPropertiesObject6 = []AdditionalPropertiesObject6_Item
+
+// AdditionalPropertiesObject6_Item defines model for AdditionalPropertiesObject6.Item.
+type AdditionalPropertiesObject6_Item struct {
+	AdditionalProperties map[string]SchemaObject `json:"-"`
+}
+
+// simple anyOf case
+type AnyOfObject1 struct {
+	union json.RawMessage
+}
+
 // Conflicts with Enum2, enum values need to be prefixed with type
 // name.
 type Enum1 string
@@ -160,6 +174,145 @@ type ObjectWithJsonField struct {
 	Value2 json.RawMessage `json:"value2,omitempty"`
 }
 
+// oneOf with references and no disciminator
+type OneOfObject1 struct {
+	union json.RawMessage
+}
+
+// fixed properties, variable required - will compile, but not much sense
+type OneOfObject10 struct {
+	One   *string `json:"one,omitempty"`
+	Three *bool   `json:"three,omitempty"`
+	Two   *int    `json:"two,omitempty"`
+	union json.RawMessage
+}
+
+// OneOfObject100 defines model for .
+type OneOfObject100 = interface{}
+
+// OneOfObject101 defines model for .
+type OneOfObject101 = interface{}
+
+// additional properties of oneOf
+type OneOfObject11 struct {
+	AdditionalProperties map[string]OneOfObject11_AdditionalProperties `json:"-"`
+}
+
+// OneOfObject110 defines model for .
+type OneOfObject110 = bool
+
+// OneOfObject111 defines model for .
+type OneOfObject111 = float32
+
+// OneOfObject112 defines model for .
+type OneOfObject112 = string
+
+// OneOfObject11_AdditionalProperties defines model for OneOfObject11.AdditionalProperties.
+type OneOfObject11_AdditionalProperties struct {
+	union json.RawMessage
+}
+
+// OneOfObject12 defines model for OneOfObject12.
+type OneOfObject12 struct {
+	union json.RawMessage
+}
+
+// OneOfObject120 defines model for .
+type OneOfObject120 = string
+
+// OneOfObject121 defines model for .
+type OneOfObject121 = float32
+
+// oneOf with inline elements
+type OneOfObject2 struct {
+	union json.RawMessage
+}
+
+// OneOfObject20 defines model for .
+type OneOfObject20 struct {
+	Name *string `json:"name,omitempty"`
+}
+
+// OneOfObject21 defines model for .
+type OneOfObject21 = []float32
+
+// OneOfObject22 defines model for .
+type OneOfObject22 = bool
+
+// inline OneOf
+type OneOfObject3 struct {
+	Union *OneOfObject3_Union `json:"union,omitempty"`
+}
+
+// OneOfObject3_Union defines model for OneOfObject3.Union.
+type OneOfObject3_Union struct {
+	union json.RawMessage
+}
+
+// oneOf plus fixed type - custom marshaling/unmarshaling
+type OneOfObject4 struct {
+	FixedProperty *string `json:"fixedProperty,omitempty"`
+	union         json.RawMessage
+}
+
+// oneOf with disciminator but no mapping
+type OneOfObject5 struct {
+	union json.RawMessage
+}
+
+// oneOf with discriminator and mapping
+type OneOfObject6 struct {
+	union json.RawMessage
+}
+
+// array of oneOf
+type OneOfObject7 = []OneOfObject7_Item
+
+// OneOfObject7_Item defines model for OneOfObject7.Item.
+type OneOfObject7_Item struct {
+	union json.RawMessage
+}
+
+// oneOf with fixed properties
+type OneOfObject8 struct {
+	Fixed *string `json:"fixed,omitempty"`
+	union json.RawMessage
+}
+
+// oneOf with fixed descriminator
+type OneOfObject9 struct {
+	Type  string `json:"type"`
+	union json.RawMessage
+}
+
+// OneOfVariant1 defines model for OneOfVariant1.
+type OneOfVariant1 struct {
+	Name string `json:"name"`
+}
+
+// OneOfVariant2 defines model for OneOfVariant2.
+type OneOfVariant2 = []int
+
+// OneOfVariant3 defines model for OneOfVariant3.
+type OneOfVariant3 = bool
+
+// OneOfVariant4 defines model for OneOfVariant4.
+type OneOfVariant4 struct {
+	Discriminator string `json:"discriminator"`
+	Name          string `json:"name"`
+}
+
+// OneOfVariant5 defines model for OneOfVariant5.
+type OneOfVariant5 struct {
+	Discriminator string `json:"discriminator"`
+	Id            int    `json:"id"`
+}
+
+// OneOfVariant6 defines model for OneOfVariant6.
+type OneOfVariant6 struct {
+	Values OneOfVariant2 `json:"values"`
+}
+
 // When a Schema is renamed, $ref should refer to the new name
 type ReferenceToRenameMe struct {
 	// This schema should be renamed via x-go-name when generating
@@ -840,3 +993,839 @@ func (a AdditionalPropertiesObject5) MarshalJSON() ([]byte, error) {
 	}
 	return json.Marshal(object)
 }
+
+// Getter for additional properties for AdditionalPropertiesObject6_Item. Returns the specified
+// element and whether it was found
+func (a AdditionalPropertiesObject6_Item) Get(fieldName string) (value SchemaObject, found bool) {
+	if a.AdditionalProperties != nil {
+		value, found = a.AdditionalProperties[fieldName]
+	}
+	return
+}
+
+// Setter for additional properties for AdditionalPropertiesObject6_Item
+func (a *AdditionalPropertiesObject6_Item) Set(fieldName string, value SchemaObject) {
+	if a.AdditionalProperties == nil {
+		a.AdditionalProperties = make(map[string]SchemaObject)
+	}
+	a.AdditionalProperties[fieldName] = value
+}
+
+// Override default JSON handling for AdditionalPropertiesObject6_Item to handle AdditionalProperties
+func (a *AdditionalPropertiesObject6_Item) UnmarshalJSON(b []byte) error {
+	object := make(map[string]json.RawMessage)
+	err := json.Unmarshal(b, &object)
+	if err != nil {
+		return err
+	}
+
+	if len(object) != 0 {
+		a.AdditionalProperties = make(map[string]SchemaObject)
+		for fieldName, fieldBuf := range object {
+			var fieldVal SchemaObject
+			err := json.Unmarshal(fieldBuf, &fieldVal)
+			if err != nil {
+				return fmt.Errorf("error unmarshaling field %s: %w", fieldName, err)
+			}
+			a.AdditionalProperties[fieldName] = fieldVal
+		}
+	}
+	return nil
+}
+
+// Override default JSON handling for AdditionalPropertiesObject6_Item to handle AdditionalProperties
+func (a AdditionalPropertiesObject6_Item) MarshalJSON() ([]byte, error) {
+	var err error
+	object := make(map[string]json.RawMessage)
+
+	for fieldName, field := range a.AdditionalProperties {
+		object[fieldName], err = json.Marshal(field)
+		if err != nil {
+			return nil, fmt.Errorf("error marshaling '%s': %w", fieldName, err)
+		}
+	}
+	return json.Marshal(object)
+}
+
+// Getter for additional properties for OneOfObject11. Returns the specified
+// element and whether it was found
+func (a OneOfObject11) Get(fieldName string) (value OneOfObject11_AdditionalProperties, found bool) {
+	if a.AdditionalProperties != nil {
+		value, found = a.AdditionalProperties[fieldName]
+	}
+	return
+}
+
+// Setter for additional properties for OneOfObject11
+func (a *OneOfObject11) Set(fieldName string, value OneOfObject11_AdditionalProperties) {
+	if a.AdditionalProperties == nil {
+		a.AdditionalProperties = make(map[string]OneOfObject11_AdditionalProperties)
+	}
+	a.AdditionalProperties[fieldName] = value
+}
+
+// Override default JSON handling for OneOfObject11 to handle AdditionalProperties
+func (a *OneOfObject11) UnmarshalJSON(b []byte) error {
+	object := make(map[string]json.RawMessage)
+	err := json.Unmarshal(b, &object)
+	if err != nil {
+		return err
+	}
+
+	if len(object) != 0 {
+		a.AdditionalProperties = make(map[string]OneOfObject11_AdditionalProperties)
+		for fieldName, fieldBuf := range object {
+			var fieldVal OneOfObject11_AdditionalProperties
+			err := json.Unmarshal(fieldBuf, &fieldVal)
+			if err != nil {
+				return fmt.Errorf("error unmarshaling field %s: %w", fieldName, err)
+			}
+			a.AdditionalProperties[fieldName] = fieldVal
+		}
+	}
+	return nil
+}
+
+// Override default JSON handling for OneOfObject11 to handle AdditionalProperties
+func (a OneOfObject11) MarshalJSON() ([]byte, error) {
+	var err error
+	object := make(map[string]json.RawMessage)
+
+	for fieldName, field := range a.AdditionalProperties {
+		object[fieldName], err = json.Marshal(field)
+		if err != nil {
+			return nil, fmt.Errorf("error marshaling '%s': %w", fieldName, err)
+		}
+	}
+	return json.Marshal(object)
+}
+
+func (t AnyOfObject1) AsOneOfVariant4() (OneOfVariant4, error) {
+	var body OneOfVariant4
+	err := json.Unmarshal(t.union, &body)
+	return body, err
+}
+
+func (t *AnyOfObject1) FromOneOfVariant4(v OneOfVariant4) error {
+	b, err := json.Marshal(v)
+	t.union = b
+	return err
+}
+
+func (t AnyOfObject1) AsOneOfVariant5() (OneOfVariant5, error) {
+	var body OneOfVariant5
+	err := json.Unmarshal(t.union, &body)
+	return body, err
+}
+
+func (t *AnyOfObject1) FromOneOfVariant5(v OneOfVariant5) error {
+	b, err := json.Marshal(v)
+	t.union = b
+	return err
+}
+
+func (t AnyOfObject1) MarshalJSON() ([]byte, error) {
+	b, err := t.union.MarshalJSON()
+	return b, err
+}
+
+func (t *AnyOfObject1) UnmarshalJSON(b []byte) error {
+	err := t.union.UnmarshalJSON(b)
+	return err
+}
+
+func (t OneOfObject1) AsOneOfVariant1() (OneOfVariant1, error) {
+	var body OneOfVariant1
+	err := json.Unmarshal(t.union, &body)
+	return body, err
+}
+
+func (t *OneOfObject1) FromOneOfVariant1(v OneOfVariant1) error {
+	b, err := json.Marshal(v)
+	t.union = b
+	return err
+}
+
+func (t OneOfObject1) AsOneOfVariant2() (OneOfVariant2, error) {
+	var body OneOfVariant2
+	err := json.Unmarshal(t.union, &body)
+	return body, err
+}
+
+func (t *OneOfObject1) FromOneOfVariant2(v OneOfVariant2) error {
+	b, err := json.Marshal(v)
+	t.union = b
+	return err
+}
+
+func (t OneOfObject1) AsOneOfVariant3() (OneOfVariant3, error) {
+	var body OneOfVariant3
+	err := json.Unmarshal(t.union, &body)
+	return body, err
+}
+
+func (t *OneOfObject1) FromOneOfVariant3(v OneOfVariant3) error {
+	b, err := json.Marshal(v)
+	t.union = b
+	return err
+}
+
+func (t OneOfObject1) MarshalJSON() ([]byte, error) {
+	b, err := t.union.MarshalJSON()
+	return b, err
+}
+
+func (t *OneOfObject1) UnmarshalJSON(b []byte) error {
+	err := t.union.UnmarshalJSON(b)
+	return err
+}
+
+func (t OneOfObject10) AsOneOfObject100() (OneOfObject100, error) {
+	var body OneOfObject100
+	err := json.Unmarshal(t.union, &body)
+	return body, err
+}
+
+func (t *OneOfObject10) FromOneOfObject100(v OneOfObject100) error {
+	b, err := json.Marshal(v)
+	t.union = b
+	return err
+}
+
+func (t OneOfObject10) AsOneOfObject101() (OneOfObject101, error) {
+	var body OneOfObject101
+	err := json.Unmarshal(t.union, &body)
+	return body, err
+}
+
+func (t *OneOfObject10) FromOneOfObject101(v OneOfObject101) error {
+	b, err := json.Marshal(v)
+	t.union = b
+	return err
+}
+
+func (t OneOfObject10) MarshalJSON() ([]byte, error) {
+	b, err := t.union.MarshalJSON()
+	if err != nil {
+		return nil, err
+	}
+	object := make(map[string]json.RawMessage)
+	err = json.Unmarshal(b, &object)
+	if err != nil {
+		return nil, err
+	}
+
+	object["one"], err = json.Marshal(t.One)
+	if err != nil {
+		return nil, fmt.Errorf("error marshaling 'one': %w", err)
+	}
+
+	object["three"], err = json.Marshal(t.Three)
+	if err != nil {
+		return nil, fmt.Errorf("error marshaling 'three': %w", err)
+	}
+
+	object["two"], err = json.Marshal(t.Two)
+	if err != nil {
+		return nil, fmt.Errorf("error marshaling 'two': %w", err)
+	}
+	b, err = json.Marshal(object)
+	return b, err
+}
+
+func (t *OneOfObject10) UnmarshalJSON(b []byte) error {
+	err := t.union.UnmarshalJSON(b)
+	if err != nil {
+		return err
+	}
+	object := make(map[string]json.RawMessage)
+	err = json.Unmarshal(b, &object)
+	if err != nil {
+		return err
+	}
+
+	if raw, found := object["one"]; found {
+		err = json.Unmarshal(raw, &t.One)
+		if err != nil {
+			return fmt.Errorf("error reading 'one': %w", err)
+		}
+	}
+
+	if raw, found := object["three"]; found {
+		err = json.Unmarshal(raw, &t.Three)
+		if err != nil {
+			return fmt.Errorf("error reading 'three': %w", err)
+		}
+	}
+
+	if raw, found := object["two"]; found {
+		err = json.Unmarshal(raw, &t.Two)
+		if err != nil {
+			return fmt.Errorf("error reading 'two': %w", err)
+		}
+	}
+
+	return err
+}
+
+func (t OneOfObject11_AdditionalProperties) AsOneOfObject110() (OneOfObject110, error) {
+	var body OneOfObject110
+	err := json.Unmarshal(t.union, &body)
+	return body, err
+}
+
+func (t *OneOfObject11_AdditionalProperties) FromOneOfObject110(v OneOfObject110) error {
+	b, err := json.Marshal(v)
+	t.union = b
+	return err
+}
+
+func (t OneOfObject11_AdditionalProperties) AsOneOfObject111() (OneOfObject111, error) {
+	var body OneOfObject111
+	err := json.Unmarshal(t.union, &body)
+	return body, err
+}
+
+func (t *OneOfObject11_AdditionalProperties) FromOneOfObject111(v OneOfObject111) error {
+	b, err := json.Marshal(v)
+	t.union = b
+	return err
+}
+
+func (t OneOfObject11_AdditionalProperties) AsOneOfObject112() (OneOfObject112, error) {
+	var body OneOfObject112
+	err := json.Unmarshal(t.union, &body)
+	return body, err
+}
+
+func (t *OneOfObject11_AdditionalProperties) FromOneOfObject112(v OneOfObject112) error {
+	b, err := json.Marshal(v)
+	t.union = b
+	return err
+}
+
+func (t OneOfObject11_AdditionalProperties) MarshalJSON() ([]byte, error) {
+	b, err := t.union.MarshalJSON()
+	return b, err
+}
+
+func (t *OneOfObject11_AdditionalProperties) UnmarshalJSON(b []byte) error {
+	err := t.union.UnmarshalJSON(b)
+	return err
+}
+
+func (t OneOfObject12) AsOneOfObject120() (OneOfObject120, error) {
+	var body OneOfObject120
+	err := json.Unmarshal(t.union, &body)
+	return body, err
+}
+
+func (t *OneOfObject12) FromOneOfObject120(v OneOfObject120) error {
+	b, err := json.Marshal(v)
+	t.union = b
+	return err
+}
+
+func (t OneOfObject12) AsOneOfObject121() (OneOfObject121, error) {
+	var body OneOfObject121
+	err := json.Unmarshal(t.union, &body)
+	return body, err
+}
+
+func (t *OneOfObject12) FromOneOfObject121(v OneOfObject121) error {
+	b, err := json.Marshal(v)
+	t.union = b
+	return err
+}
+
+func (t OneOfObject12) AsOneOfVariant3() (OneOfVariant3, error) {
+	var body OneOfVariant3
+	err := json.Unmarshal(t.union, &body)
+	return body, err
+}
+
+func (t *OneOfObject12) FromOneOfVariant3(v OneOfVariant3) error {
+	b, err := json.Marshal(v)
+	t.union = b
+	return err
+}
+
+func (t OneOfObject12) AsOneOfVariant4() (OneOfVariant4, error) {
+	var body OneOfVariant4
+	err := json.Unmarshal(t.union, &body)
+	return body, err
+}
+
+func (t *OneOfObject12) FromOneOfVariant4(v OneOfVariant4) error {
+	b, err := json.Marshal(v)
+	t.union = b
+	return err
+}
+
+func (t OneOfObject12) MarshalJSON() ([]byte, error) {
+	b, err := t.union.MarshalJSON()
+	return b, err
+}
+
+func (t *OneOfObject12) UnmarshalJSON(b []byte) error {
+	err := t.union.UnmarshalJSON(b)
+	return err
+}
+
+func (t OneOfObject2) AsOneOfObject20() (OneOfObject20, error) {
+	var body OneOfObject20
+	err := json.Unmarshal(t.union, &body)
+	return body, err
+}
+
+func (t *OneOfObject2) FromOneOfObject20(v OneOfObject20) error {
+	b, err := json.Marshal(v)
+	t.union = b
+	return err
+}
+
+func (t OneOfObject2) AsOneOfObject21() (OneOfObject21, error) {
+	var body OneOfObject21
+	err := json.Unmarshal(t.union, &body)
+	return body, err
+}
+
+func (t *OneOfObject2) FromOneOfObject21(v OneOfObject21) error {
+	b, err := json.Marshal(v)
+	t.union = b
+	return err
+}
+
+func (t OneOfObject2) AsOneOfObject22() (OneOfObject22, error) {
+	var body OneOfObject22
+	err := json.Unmarshal(t.union, &body)
+	return body, err
+}
+
+func (t *OneOfObject2) FromOneOfObject22(v OneOfObject22) error {
+	b, err := json.Marshal(v)
+	t.union = b
+	return err
+}
+
+func (t OneOfObject2) MarshalJSON() ([]byte, error) {
+	b, err := t.union.MarshalJSON()
+	return b, err
+}
+
+func (t *OneOfObject2) UnmarshalJSON(b []byte) error {
+	err := t.union.UnmarshalJSON(b)
+	return err
+}
+
+func (t OneOfObject3_Union) AsOneOfVariant1() (OneOfVariant1, error) {
+	var body OneOfVariant1
+	err := json.Unmarshal(t.union, &body)
+	return body, err
+}
+
+func (t *OneOfObject3_Union) FromOneOfVariant1(v OneOfVariant1) error {
+	b, err := json.Marshal(v)
+	t.union = b
+	return err
+}
+
+func (t OneOfObject3_Union) AsOneOfVariant2() (OneOfVariant2, error) {
+	var body OneOfVariant2
+	err := json.Unmarshal(t.union, &body)
+	return body, err
+}
+
+func (t *OneOfObject3_Union) FromOneOfVariant2(v OneOfVariant2) error {
+	b, err := json.Marshal(v)
+	t.union = b
+	return err
+}
+
+func (t OneOfObject3_Union) AsOneOfVariant3() (OneOfVariant3, error) {
+	var body OneOfVariant3
+	err := json.Unmarshal(t.union, &body)
+	return body, err
+}
+
+func (t *OneOfObject3_Union) FromOneOfVariant3(v OneOfVariant3) error {
+	b, err := json.Marshal(v)
+	t.union = b
+	return err
+}
+
+func (t OneOfObject3_Union) MarshalJSON() ([]byte, error) {
+	b, err := t.union.MarshalJSON()
+	return b, err
+}
+
+func (t *OneOfObject3_Union) UnmarshalJSON(b []byte) error {
+	err := t.union.UnmarshalJSON(b)
+	return err
+}
+
+func (t OneOfObject4) AsOneOfVariant1() (OneOfVariant1, error) {
+	var body OneOfVariant1
+	err := json.Unmarshal(t.union, &body)
+	return body, err
+}
+
+func (t *OneOfObject4) FromOneOfVariant1(v OneOfVariant1) error {
+	b, err := json.Marshal(v)
+	t.union = b
+	return err
+}
+
+func (t OneOfObject4) AsOneOfVariant2() (OneOfVariant2, error) {
+	var body OneOfVariant2
+	err := json.Unmarshal(t.union, &body)
+	return body, err
+}
+
+func (t *OneOfObject4) FromOneOfVariant2(v OneOfVariant2) error {
+	b, err := json.Marshal(v)
+	t.union = b
+	return err
+}
+
+func (t OneOfObject4) AsOneOfVariant3() (OneOfVariant3, error) {
+	var body OneOfVariant3
+	err := json.Unmarshal(t.union, &body)
+	return body, err
+}
+
+func (t *OneOfObject4) FromOneOfVariant3(v OneOfVariant3) error {
+	b, err := json.Marshal(v)
+	t.union = b
+	return err
+}
+
+func (t OneOfObject4) MarshalJSON() ([]byte, error) {
+	b, err := t.union.MarshalJSON()
+	if err != nil {
+		return nil, err
+	}
+	object := make(map[string]json.RawMessage)
+	err = json.Unmarshal(b, &object)
+	if err != nil {
+		return nil, err
+	}
+
+	object["fixedProperty"], err = json.Marshal(t.FixedProperty)
+	if err != nil {
+		return nil, fmt.Errorf("error marshaling 'fixedProperty': %w", err)
+	}
+	b, err = json.Marshal(object)
+	return b, err
+}
+
+func (t *OneOfObject4) UnmarshalJSON(b []byte) error {
+	err := t.union.UnmarshalJSON(b)
+	if err != nil {
+		return err
+	}
+	object := make(map[string]json.RawMessage)
+	err = json.Unmarshal(b, &object)
+	if err != nil {
+		return err
+	}
+
+	if raw, found := object["fixedProperty"]; found {
+		err = json.Unmarshal(raw, &t.FixedProperty)
+		if err != nil {
+			return fmt.Errorf("error reading 'fixedProperty': %w", err)
+		}
+	}
+
+	return err
+}
+
+func (t OneOfObject5) AsOneOfVariant4() (OneOfVariant4, error) {
+	var body OneOfVariant4
+	err := json.Unmarshal(t.union, &body)
+	return body, err
+}
+
+func (t *OneOfObject5) FromOneOfVariant4(v OneOfVariant4) error {
+	b, err := json.Marshal(v)
+	t.union = b
+	return err
+}
+
+func (t OneOfObject5) AsOneOfVariant5() (OneOfVariant5, error) {
+	var body OneOfVariant5
+	err := json.Unmarshal(t.union, &body)
+	return body, err
+}
+
+func (t *OneOfObject5) FromOneOfVariant5(v OneOfVariant5) error {
+	b, err := json.Marshal(v)
+	t.union = b
+	return err
+}
+
+func (t OneOfObject5) Discriminator() (string, error) {
+	var discriminator struct {
+		Discriminator string `json:"discriminator"`
+	}
+	err := json.Unmarshal(t.union, &discriminator)
+	return discriminator.Discriminator, err
+}
+
+func (t OneOfObject5) MarshalJSON() ([]byte, error) {
+	b, err := t.union.MarshalJSON()
+	return b, err
+}
+
+func (t *OneOfObject5) UnmarshalJSON(b []byte) error {
+	err := t.union.UnmarshalJSON(b)
+	return err
+}
+
+func (t OneOfObject6) AsOneOfVariant4() (OneOfVariant4, error) {
+	var body OneOfVariant4
+	err := json.Unmarshal(t.union, &body)
+	return body, err
+}
+
+func (t *OneOfObject6) FromOneOfVariant4(v OneOfVariant4) error {
+	v.Discriminator = "v4"
+	b, err := json.Marshal(v)
+	t.union = b
+	return err
+}
+
+func (t OneOfObject6) AsOneOfVariant5() (OneOfVariant5, error) {
+	var body OneOfVariant5
+	err := json.Unmarshal(t.union, &body)
+	return body, err
+}
+
+func (t *OneOfObject6) FromOneOfVariant5(v OneOfVariant5) error {
+	v.Discriminator = "v5"
+	b, err := json.Marshal(v)
+	t.union = b
+	return err
+}
+
+func (t OneOfObject6) Discriminator() (string, error) {
+	var discriminator struct {
+		Discriminator string `json:"discriminator"`
+	}
+	err := json.Unmarshal(t.union, &discriminator)
+	return discriminator.Discriminator, err
+}
+
+func (t OneOfObject6) ValueByDiscriminator() (interface{}, error) {
+	discriminator, err := t.Discriminator()
+	if err != nil {
+		return nil, err
+	}
+	switch discriminator {
+	case "v4":
+		return t.AsOneOfVariant4()
+	case "v5":
+		return t.AsOneOfVariant5()
+	default:
+		return nil, errors.New("unknown discriminator value: " + discriminator)
+	}
+}
+
+func (t OneOfObject6) MarshalJSON() ([]byte, error) {
+	b, err := t.union.MarshalJSON()
+	return b, err
+}
+
+func (t *OneOfObject6) UnmarshalJSON(b []byte) error {
+	err := t.union.UnmarshalJSON(b)
+	return err
+}
+
+func (t OneOfObject7_Item) AsOneOfVariant1() (OneOfVariant1, error) {
+	var body OneOfVariant1
+	err := json.Unmarshal(t.union, &body)
+	return body, err
+}
+
+func (t *OneOfObject7_Item) FromOneOfVariant1(v OneOfVariant1) error {
+	b, err := json.Marshal(v)
+	t.union = b
+	return err
+}
+
+func (t OneOfObject7_Item) AsOneOfVariant2() (OneOfVariant2, error) {
+	var body OneOfVariant2
+	err := json.Unmarshal(t.union, &body)
+	return body, err
+}
+
+func (t *OneOfObject7_Item) FromOneOfVariant2(v OneOfVariant2) error {
+	b, err := json.Marshal(v)
+	t.union = b
+	return err
+}
+
+func (t OneOfObject7_Item) MarshalJSON() ([]byte, error) {
+	b, err := t.union.MarshalJSON()
+	return b, err
+}
+
+func (t *OneOfObject7_Item) UnmarshalJSON(b []byte) error {
+	err := t.union.UnmarshalJSON(b)
+	return err
+}
+
+func (t OneOfObject8) AsOneOfVariant1() (OneOfVariant1, error) {
+	var body OneOfVariant1
+	err := json.Unmarshal(t.union, &body)
+	return body, err
+}
+
+func (t *OneOfObject8) FromOneOfVariant1(v OneOfVariant1) error {
+	b, err := json.Marshal(v)
+	t.union = b
+	return err
+}
+
+func (t OneOfObject8) AsOneOfVariant2() (OneOfVariant2, error) {
+	var body OneOfVariant2
+	err := json.Unmarshal(t.union, &body)
+	return body, err
+}
+
+func (t *OneOfObject8) FromOneOfVariant2(v OneOfVariant2) error {
+	b, err := json.Marshal(v)
+	t.union = b
+	return err
+}
+
+func (t OneOfObject8) MarshalJSON() ([]byte, error) {
+	b, err := t.union.MarshalJSON()
+	if err != nil {
+		return nil, err
+	}
+	object := make(map[string]json.RawMessage)
+	err = json.Unmarshal(b, &object)
+	if err != nil {
+		return nil, err
+	}
+
+	object["fixed"], err = json.Marshal(t.Fixed)
+	if err != nil {
+		return nil, fmt.Errorf("error marshaling 'fixed': %w", err)
+	}
+	b, err = json.Marshal(object)
+	return b, err
+}
+
+func (t *OneOfObject8) UnmarshalJSON(b []byte) error {
+	err := t.union.UnmarshalJSON(b)
+	if err != nil {
+		return err
+	}
+	object := make(map[string]json.RawMessage)
+	err = json.Unmarshal(b, &object)
+	if err != nil {
+		return err
+	}
+
+	if raw, found := object["fixed"]; found {
+		err = json.Unmarshal(raw, &t.Fixed)
+		if err != nil {
+			return fmt.Errorf("error reading 'fixed': %w", err)
+		}
+	}
+
+	return err
+}
+
+func (t OneOfObject9) AsOneOfVariant1() (OneOfVariant1, error) {
+	var body OneOfVariant1
+	err := json.Unmarshal(t.union, &body)
+	return body, err
+}
+
+func (t *OneOfObject9) FromOneOfVariant1(v OneOfVariant1) error {
+	t.Type = "v1"
+
+	b, err := json.Marshal(v)
+	t.union = b
+	return err
+}
+
+func (t OneOfObject9) AsOneOfVariant6() (OneOfVariant6, error) {
+	var body OneOfVariant6
+	err := json.Unmarshal(t.union, &body)
+	return body, err
+}
+
+func (t *OneOfObject9) FromOneOfVariant6(v OneOfVariant6) error {
+	t.Type = "v6"
+
+	b, err := json.Marshal(v)
+	t.union = b
+	return err
+}
+
+func (t OneOfObject9) Discriminator() (string, error) {
+	var discriminator struct {
+		Discriminator string `json:"type"`
+	}
+	err := json.Unmarshal(t.union, &discriminator)
+	return discriminator.Discriminator, err
+}
+
+func (t OneOfObject9) ValueByDiscriminator() (interface{}, error) {
+	discriminator, err := t.Discriminator()
+	if err != nil {
+		return nil, err
+	}
+	switch discriminator {
+	case "v1":
+		return t.AsOneOfVariant1()
+	case "v6":
+		return t.AsOneOfVariant6()
+	default:
+		return nil, errors.New("unknown discriminator value: " + discriminator)
+	}
+}
+
+func (t OneOfObject9) MarshalJSON() ([]byte, error) {
+	b, err := t.union.MarshalJSON()
+	if err != nil {
+		return nil, err
+	}
+	object := make(map[string]json.RawMessage)
+	err = json.Unmarshal(b, &object)
+	if err != nil {
+		return nil, err
+	}
+
+	object["type"], err = json.Marshal(t.Type)
+	if err != nil {
+		return nil, fmt.Errorf("error marshaling 'type': %w", err)
+	}
+	b, err = json.Marshal(object)
+	return b, err
+}
+
+func (t *OneOfObject9) UnmarshalJSON(b []byte) error {
+	err := t.union.UnmarshalJSON(b)
+	if err != nil {
+		return err
+	}
+	object := make(map[string]json.RawMessage)
+	err = json.Unmarshal(b, &object)
+	if err != nil {
+		return err
+	}
+
+	if raw, found := object["type"]; found {
+		err = json.Unmarshal(raw, &t.Type)
+		if err != nil {
+			return fmt.Errorf("error reading 'type': %w", err)
+		}
+	}
+
+	return err
+}
diff --git a/internal/test/components/components.yaml b/internal/test/components/components.yaml
index a11eb9f..f5d6441 100644
--- a/internal/test/components/components.yaml
+++ b/internal/test/components/components.yaml
@@ -34,6 +34,34 @@ paths:
                     $ref: "#/components/schemas/AdditionalPropertiesObject4"
                   five:
                     $ref: "#/components/schemas/AdditionalPropertiesObject5"
+                  six:
+                    $ref: "#/components/schemas/AdditionalPropertiesObject6"
+                  oneOf1:
+                    $ref: "#/components/schemas/OneOfObject1"
+                  oneOf2:
+                    $ref: "#/components/schemas/OneOfObject2"
+                  oneOf3:
+                    $ref: "#/components/schemas/OneOfObject3"
+                  oneOf4:
+                    $ref: "#/components/schemas/OneOfObject4"
+                  oneOf5:
+                    $ref: "#/components/schemas/OneOfObject5"
+                  oneOf6:
+                    $ref: "#/components/schemas/OneOfObject6"
+                  oneOf7:
+                    $ref: "#/components/schemas/OneOfObject7"
+                  oneOf8:
+                    $ref: "#/components/schemas/OneOfObject8"
+                  oneOf9:
+                    $ref: "#/components/schemas/OneOfObject9"
+                  oneOf10:
+                    $ref: "#/components/schemas/OneOfObject10"
+                  oneOf11:
+                    $ref: "#/components/schemas/OneOfObject11"
+                  oneOf12:
+                    $ref: "#/components/schemas/OneOfObject12"
+                  anyOf1:
+                    $ref: "#/components/schemas/AnyOfObject1"
                   jsonField:
                     $ref: "#/components/schemas/ObjectWithJsonField"
         default:
@@ -172,6 +200,176 @@ components:
       type: object
       additionalProperties:
         $ref: '#/components/schemas/SchemaObject'
+    AdditionalPropertiesObject6:
+      description: Array of object with additional properties
+      type: array
+      items:
+        type: object
+        additionalProperties:
+          $ref: '#/components/schemas/SchemaObject'
+    OneOfObject1:
+      description: oneOf with references and no disciminator
+      oneOf:
+        - $ref: '#/components/schemas/OneOfVariant1'
+        - $ref: '#/components/schemas/OneOfVariant2'
+        - $ref: '#/components/schemas/OneOfVariant3'
+    OneOfObject2:
+      description: oneOf with inline elements
+      oneOf:
+        - type: object
+          properties:
+            name:
+              type: string
+        - type: array
+          items:
+            type: number
+        - type: boolean
+    OneOfObject3:
+      description: inline OneOf
+      type: object
+      properties:
+        union:
+          oneOf:
+            - $ref: '#/components/schemas/OneOfVariant1'
+            - $ref: '#/components/schemas/OneOfVariant2'
+            - $ref: '#/components/schemas/OneOfVariant3'
+    OneOfObject4:
+      description: oneOf plus fixed type - custom marshaling/unmarshaling
+      type: object
+      properties:
+        fixedProperty:
+          type: string
+      oneOf:
+        - $ref: '#/components/schemas/OneOfVariant1'
+        - $ref: '#/components/schemas/OneOfVariant2'
+        - $ref: '#/components/schemas/OneOfVariant3'
+    OneOfObject5:
+      description: oneOf with disciminator but no mapping
+      oneOf:
+        - $ref: '#/components/schemas/OneOfVariant4'
+        - $ref: '#/components/schemas/OneOfVariant5'
+      discriminator:
+        propertyName: discriminator
+    OneOfObject6:
+      description: oneOf with discriminator and mapping
+      oneOf:
+        - $ref: '#/components/schemas/OneOfVariant4'
+        - $ref: '#/components/schemas/OneOfVariant5'
+      discriminator:
+        propertyName: discriminator
+        mapping:
+          v4: '#/components/schemas/OneOfVariant4'
+          v5: '#/components/schemas/OneOfVariant5'
+    OneOfObject7:
+      description: array of oneOf
+      type: array
+      items:
+        oneOf:
+          - $ref: '#/components/schemas/OneOfVariant1'
+          - $ref: '#/components/schemas/OneOfVariant2'
+    OneOfObject8:
+      description: oneOf with fixed properties
+      type: object
+      properties:
+        fixed:
+          type: string
+      oneOf:
+        - $ref: '#/components/schemas/OneOfVariant1'
+        - $ref: '#/components/schemas/OneOfVariant2'
+    OneOfObject9:
+      description: oneOf with fixed descriminator
+      type: object
+      properties:
+        type:
+          type: string
+      oneOf:
+        - $ref: '#/components/schemas/OneOfVariant1'
+        - $ref: '#/components/schemas/OneOfVariant6'
+      discriminator:
+        propertyName: type
+        mapping:
+          v1: '#/components/schemas/OneOfVariant1'
+          v6: '#/components/schemas/OneOfVariant6'
+      required:
+        - type
+    OneOfObject10:
+      description: fixed properties, variable required - will compile, but not much sense
+      type: object
+      properties:
+        one:
+          type: string
+        two:
+          type: integer
+        three:
+          type: boolean
+      oneOf:
+        - required:
+            - one
+        - required:
+            - two
+            - three
+    OneOfObject11:
+      description: additional properties of oneOf
+      type: object
+      additionalProperties:
+        oneOf:
+          - type: boolean
+          - type: number
+          - type: string
+    OneOfObject12:
+      description: allOf of oneOfs
+      allOf:
+        - oneOf:
+            - type: string
+            - type: number
+        - oneOf:
+            - $ref: '#/components/schemas/OneOfVariant3'
+            - $ref: '#/components/schemas/OneOfVariant4'
+    AnyOfObject1:
+      description: simple anyOf case
+      anyOf:
+        - $ref: '#/components/schemas/OneOfVariant4'
+        - $ref: '#/components/schemas/OneOfVariant5'
+    OneOfVariant1:
+      type: object
+      properties:
+        name:
+          type: string
+      required:
+        - name
+    OneOfVariant2:
+      type: array
+      items:
+        type: integer
+    OneOfVariant3:
+      type: boolean
+    OneOfVariant4:
+      type: object
+      properties:
+        discriminator:
+          type: string
+        name:
+          type: string
+      required:
+        - discriminator
+        - name
+    OneOfVariant5:
+      type: object
+      properties:
+        discriminator:
+          type: string
+        id:
+          type: integer
+      required:
+        - discriminator
+        - id
+    OneOfVariant6:
+      type: object
+      properties:
+        values:
+          $ref: '#/components/schemas/OneOfVariant2'
+      required:
+        - values
     ObjectWithJsonField:
       type: object
       properties:
diff --git a/internal/test/components/components_test.go b/internal/test/components/components_test.go
index 05912c8..eab7af3 100644
--- a/internal/test/components/components_test.go
+++ b/internal/test/components/components_test.go
@@ -70,3 +70,135 @@ func TestAdditionalProperties(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, bossSchema, obj5.AdditionalProperties["boss"])
 }
+
+func TestOneOf(t *testing.T) {
+	const variant1 = `{"name": "123"}`
+	const variant2 = `[1, 2, 3]`
+	const variant3 = `true`
+	var dst OneOfObject1
+
+	err := json.Unmarshal([]byte(variant1), &dst)
+	assert.NoError(t, err)
+	v1, err := dst.AsOneOfVariant1()
+	assert.NoError(t, err)
+	assert.Equal(t, "123", v1.Name)
+
+	err = json.Unmarshal([]byte(variant2), &dst)
+	assert.NoError(t, err)
+	v2, err := dst.AsOneOfVariant2()
+	assert.NoError(t, err)
+	assert.Equal(t, OneOfVariant2([]int{1, 2, 3}), v2)
+
+	err = json.Unmarshal([]byte(variant3), &dst)
+	assert.NoError(t, err)
+	v3, err := dst.AsOneOfVariant3()
+	assert.NoError(t, err)
+	assert.Equal(t, OneOfVariant3(true), v3)
+
+	err = dst.FromOneOfVariant1(OneOfVariant1{Name: "123"})
+	assert.NoError(t, err)
+	marshaled, err := json.Marshal(dst)
+	assert.NoError(t, err)
+	assertJsonEqual(t, []byte(variant1), marshaled)
+
+	err = dst.FromOneOfVariant2([]int{1, 2, 3})
+	assert.NoError(t, err)
+	marshaled, err = json.Marshal(dst)
+	assert.NoError(t, err)
+	assertJsonEqual(t, []byte(variant2), marshaled)
+
+	err = dst.FromOneOfVariant3(true)
+	assert.NoError(t, err)
+	marshaled, err = json.Marshal(dst)
+	assert.NoError(t, err)
+	assertJsonEqual(t, []byte(variant3), marshaled)
+}
+
+func TestOneOfWithDiscriminator(t *testing.T) {
+	const variant4 = `{"discriminator": "v4", "name": "123"}`
+	const variant5 = `{"discriminator": "v5", "id": 123}`
+	var dst OneOfObject6
+
+	err := json.Unmarshal([]byte(variant4), &dst)
+	assert.NoError(t, err)
+	discriminator, err := dst.Discriminator()
+	assert.NoError(t, err)
+	assert.Equal(t, "v4", discriminator)
+	v4, err := dst.ValueByDiscriminator()
+	assert.NoError(t, err)
+	assert.Equal(t, OneOfVariant4{Discriminator: "v4", Name: "123"}, v4)
+
+	err = json.Unmarshal([]byte(variant5), &dst)
+	assert.NoError(t, err)
+	discriminator, err = dst.Discriminator()
+	assert.NoError(t, err)
+	assert.Equal(t, "v5", discriminator)
+	v5, err := dst.ValueByDiscriminator()
+	assert.NoError(t, err)
+	assert.Equal(t, OneOfVariant5{Discriminator: "v5", Id: 123}, v5)
+
+	// discriminator value will be filled by the generated code
+	err = dst.FromOneOfVariant4(OneOfVariant4{Name: "123"})
+	assert.NoError(t, err)
+	marshaled, err := json.Marshal(dst)
+	assert.NoError(t, err)
+	assertJsonEqual(t, []byte(variant4), marshaled)
+
+	err = dst.FromOneOfVariant5(OneOfVariant5{Id: 123})
+	assert.NoError(t, err)
+	marshaled, err = json.Marshal(dst)
+	assert.NoError(t, err)
+	assertJsonEqual(t, []byte(variant5), marshaled)
+}
+
+func TestOneOfWithFixedProperties(t *testing.T) {
+	const variant1 = "{\"type\": \"v1\", \"name\": \"123\"}"
+	const variant6 = "{\"type\": \"v6\", \"values\": [1, 2, 3]}"
+	var dst OneOfObject9
+
+	err := json.Unmarshal([]byte(variant1), &dst)
+	assert.NoError(t, err)
+	discriminator, err := dst.Discriminator()
+	assert.NoError(t, err)
+	assert.Equal(t, "v1", discriminator)
+	v1, err := dst.ValueByDiscriminator()
+	assert.NoError(t, err)
+	assert.Equal(t, OneOfVariant1{Name: "123"}, v1)
+
+	err = json.Unmarshal([]byte(variant6), &dst)
+	assert.NoError(t, err)
+	discriminator, err = dst.Discriminator()
+	assert.NoError(t, err)
+	assert.Equal(t, "v6", discriminator)
+	v2, err := dst.AsOneOfVariant6()
+	assert.NoError(t, err)
+	assert.Equal(t, OneOfVariant6{[]int{1, 2, 3}}, v2)
+
+	err = dst.FromOneOfVariant1(OneOfVariant1{Name: "123"})
+	assert.NoError(t, err)
+	marshaled, err := json.Marshal(dst)
+	assert.NoError(t, err)
+	assertJsonEqual(t, []byte(variant1), marshaled)
+
+	err = dst.FromOneOfVariant6(OneOfVariant6{[]int{1, 2, 3}})
+	assert.NoError(t, err)
+	marshaled, err = json.Marshal(dst)
+	assert.NoError(t, err)
+	assertJsonEqual(t, []byte(variant6), marshaled)
+}
+
+func TestAnyOf(t *testing.T) {
+	const anyOfStr = `{"discriminator": "all", "name": "123", "id": 456}`
+
+	var dst AnyOfObject1
+	err := json.Unmarshal([]byte(anyOfStr), &dst)
+	assert.NoError(t, err)
+
+	v4, err := dst.AsOneOfVariant4()
+	assert.NoError(t, err)
+	assert.Equal(t, OneOfVariant4{Discriminator: "all", Name: "123"}, v4)
+
+	v5, err := dst.AsOneOfVariant5()
+	assert.NoError(t, err)
+	assert.Equal(t, OneOfVariant5{Discriminator: "all", Id: 456}, v5)
+}
diff --git a/internal/test/server/server.gen.go b/internal/test/server/server.gen.go
index ee11ddd..c9df6ac 100644
--- a/internal/test/server/server.gen.go
+++ b/internal/test/server/server.gen.go
@@ -169,36 +169,36 @@ type ServerInterfaceWrapper struct {
 	ErrorHandlerFunc   func(w http.ResponseWriter, r *http.Request, err error)
 }
 
-type MiddlewareFunc func(http.HandlerFunc) http.HandlerFunc
+type MiddlewareFunc func(http.Handler) http.Handler
 
 // GetEveryTypeOptional operation middleware
 func (siw *ServerInterfaceWrapper) GetEveryTypeOptional(w http.ResponseWriter, r *http.Request) {
 	ctx := r.Context()
 
-	var handler = func(w http.ResponseWriter, r *http.Request) {
+	var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		siw.Handler.GetEveryTypeOptional(w, r)
-	}
+	})
 
 	for _, middleware := range siw.HandlerMiddlewares {
 		handler = middleware(handler)
 	}
 
-	handler(w, r.WithContext(ctx))
+	handler.ServeHTTP(w, r.WithContext(ctx))
 }
 
 // GetSimple operation middleware
 func (siw *ServerInterfaceWrapper) GetSimple(w http.ResponseWriter, r *http.Request) {
 	ctx := r.Context()
 
-	var handler = func(w http.ResponseWriter, r *http.Request) {
+	var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		siw.Handler.GetSimple(w, r)
-	}
+	})
 
 	for _, middleware := range siw.HandlerMiddlewares {
 		handler = middleware(handler)
 	}
 
-	handler(w, r.WithContext(ctx))
+	handler.ServeHTTP(w, r.WithContext(ctx))
 }
 
 // GetWithArgs operation middleware
@@ -256,15 +256,15 @@ func (siw *ServerInterfaceWrapper) GetWithArgs(w http.ResponseWriter, r *http.Re
 
 	}
 
-	var handler = func(w http.ResponseWriter, r *http.Request) {
+	var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		siw.Handler.GetWithArgs(w, r, params)
-	}
+	})
 
 	for _, middleware := range siw.HandlerMiddlewares {
 		handler = middleware(handler)
 	}
 
-	handler(w, r.WithContext(ctx))
+	handler.ServeHTTP(w, r.WithContext(ctx))
 }
 
 // GetWithReferences operation middleware
@@ -291,15 +291,15 @@ func (siw *ServerInterfaceWrapper) GetWithReferences(w http.ResponseWriter, r *h
 		return
 	}
 
-	var handler = func(w http.ResponseWriter, r *http.Request) {
+	var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		siw.Handler.GetWithReferences(w, r, globalArgument, argument)
-	}
+	})
 
 	for _, middleware := range siw.HandlerMiddlewares {
 		handler = middleware(handler)
 	}
 
-	handler(w, r.WithContext(ctx))
+	handler.ServeHTTP(w, r.WithContext(ctx))
 }
 
 // GetWithContentType operation middleware
@@ -317,30 +317,30 @@ func (siw *ServerInterfaceWrapper) GetWithContentType(w http.ResponseWriter, r *
 		return
 	}
 
-	var handler = func(w http.ResponseWriter, r *http.Request) {
+	var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		siw.Handler.GetWithContentType(w, r, contentType)
-	}
+	})
 
 	for _, middleware := range siw.HandlerMiddlewares {
 		handler = middleware(handler)
 	}
 
-	handler(w, r.WithContext(ctx))
+	handler.ServeHTTP(w, r.WithContext(ctx))
 }
 
 // GetReservedKeyword operation middleware
 func (siw *ServerInterfaceWrapper) GetReservedKeyword(w http.ResponseWriter, r *http.Request) {
 	ctx := r.Context()
 
-	var handler = func(w http.ResponseWriter, r *http.Request) {
+	var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		siw.Handler.GetReservedKeyword(w, r)
-	}
+	})
 
 	for _, middleware := range siw.HandlerMiddlewares {
 		handler = middleware(handler)
 	}
 
-	handler(w, r.WithContext(ctx))
+	handler.ServeHTTP(w, r.WithContext(ctx))
 }
 
 // CreateResource operation middleware
@@ -358,15 +358,15 @@ func (siw *ServerInterfaceWrapper) CreateResource(w http.ResponseWriter, r *http
 		return
 	}
 
-	var handler = func(w http.ResponseWriter, r *http.Request) {
+	var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		siw.Handler.CreateResource(w, r, argument)
-	}
+	})
 
 	for _, middleware := range siw.HandlerMiddlewares {
 		handler = middleware(handler)
 	}
 
-	handler(w, r.WithContext(ctx))
+	handler.ServeHTTP(w, r.WithContext(ctx))
 }
 
 // CreateResource2 operation middleware
@@ -398,15 +398,15 @@ func (siw *ServerInterfaceWrapper) CreateResource2(w http.ResponseWriter, r *htt
 		return
 	}
 
-	var handler = func(w http.ResponseWriter, r *http.Request) {
+	var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		siw.Handler.CreateResource2(w, r, inlineArgument, params)
-	}
+	})
 
 	for _, middleware := range siw.HandlerMiddlewares {
 		handler = middleware(handler)
 	}
 
-	handler(w, r.WithContext(ctx))
+	handler.ServeHTTP(w, r.WithContext(ctx))
 }
 
 // UpdateResource3 operation middleware
@@ -424,30 +424,30 @@ func (siw *ServerInterfaceWrapper) UpdateResource3(w http.ResponseWriter, r *htt
 		return
 	}
 
-	var handler = func(w http.ResponseWriter, r *http.Request) {
+	var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		siw.Handler.UpdateResource3(w, r, pFallthrough)
-	}
+	})
 
 	for _, middleware := range siw.HandlerMiddlewares {
 		handler = middleware(handler)
 	}
 
-	handler(w, r.WithContext(ctx))
+	handler.ServeHTTP(w, r.WithContext(ctx))
 }
 
 // GetResponseWithReference operation middleware
 func (siw *ServerInterfaceWrapper) GetResponseWithReference(w http.ResponseWriter, r *http.Request) {
 	ctx := r.Context()
 
-	var handler = func(w http.ResponseWriter, r *http.Request) {
+	var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		siw.Handler.GetResponseWithReference(w, r)
-	}
+	})
 
 	for _, middleware := range siw.HandlerMiddlewares {
 		handler = middleware(handler)
 	}
 
-	handler(w, r.WithContext(ctx))
+	handler.ServeHTTP(w, r.WithContext(ctx))
 }
 
 type UnescapedCookieParamError struct {
diff --git a/pkg/codegen/utils_test.go b/pkg/codegen/utils_test.go
index b391852..0b13ee8 100644
--- a/pkg/codegen/utils_test.go
+++ b/pkg/codegen/utils_test.go
@@ -263,6 +263,23 @@ func TestSwaggerUriToGinUri(t *testing.T) {
 	assert.Equal(t, "/path/:arg/foo", SwaggerUriToGinUri("/path/{?arg*}/foo"))
 }
 
+func TestSwaggerUriToGorillaUri(t *testing.T) { // TODO
+	assert.Equal(t, "/path", SwaggerUriToGorillaUri("/path"))
+	assert.Equal(t, "/path/{arg}", SwaggerUriToGorillaUri("/path/{arg}"))
+	assert.Equal(t, "/path/{arg1}/{arg2}", SwaggerUriToGorillaUri("/path/{arg1}/{arg2}"))
+	assert.Equal(t, "/path/{arg1}/{arg2}/foo", SwaggerUriToGorillaUri("/path/{arg1}/{arg2}/foo"))
+
+	// Make sure all the exploded and alternate formats match too
+	assert.Equal(t, "/path/{arg}/foo", SwaggerUriToGorillaUri("/path/{arg}/foo"))
+	assert.Equal(t, "/path/{arg}/foo", SwaggerUriToGorillaUri("/path/{arg*}/foo"))
+	assert.Equal(t, "/path/{arg}/foo", SwaggerUriToGorillaUri("/path/{.arg}/foo"))
+	assert.Equal(t, "/path/{arg}/foo", SwaggerUriToGorillaUri("/path/{.arg*}/foo"))
+	assert.Equal(t, "/path/{arg}/foo", SwaggerUriToGorillaUri("/path/{;arg}/foo"))
+	assert.Equal(t, "/path/{arg}/foo", SwaggerUriToGorillaUri("/path/{;arg*}/foo"))
+	assert.Equal(t, "/path/{arg}/foo", SwaggerUriToGorillaUri("/path/{?arg}/foo"))
+	assert.Equal(t, "/path/{arg}/foo", SwaggerUriToGorillaUri("/path/{?arg*}/foo"))
+}
+
 func TestOrderedParamsFromUri(t *testing.T) {
 	result := OrderedParamsFromUri("/path/{param1}/{.param2}/{;param3*}/foo")
 	assert.EqualValues(t, []string{"param1", "param2", "param3"}, result)