Repository: deepmap/oapi-codegen
Pull request #564 (2022-05-03 23:13:36)

use BindStyledParameterWithLocation with path params
We came across an issue where path parameters were being path-encoded on the client side, but on the server they were decoded as query parameters. I believe using BindStyledParameterWithLocation instead of BindStyledParameter in the server template addresses the issue, as it allows us to specify the parameter type. 


Patch:
diff --git a/README.md b/README.md
index b7b9387..0bf46d9 100644
--- a/README.md
+++ b/README.md
@@ -10,13 +10,19 @@ incompatibilities. It's tedious to generate Go models which precisely correspond
 OpenAPI specifications, so let our code generator do that work for you, so that
 you can focus on implementing the business logic for your service.
 
-We have chosen to use [Echo](https://github.com/labstack/echo) as
+We have chosen to focus on [Echo](https://github.com/labstack/echo) as
 our default HTTP routing engine, due to its speed and simplicity for the generated
-stubs, and [Chi](https://github.com/go-chi/chi) is also supported as an alternative.
+stubs, and [Chi](https://github.com/go-chi/chi), and [Gin](https://github.com/gin-gonic/gin)
+have also been added by contributors as additional routers. We chose Echo because
+the `Context` object is a mockable interface, and it allows for some advanced
+testing.
 
 This package tries to be too simple rather than too generic, so we've made some
 design decisions in favor of simplicity, knowing that we can't generate strongly
-typed Go code for all possible OpenAPI Schemas.
+typed Go code for all possible OpenAPI Schemas. If there is a way to accomplish
+something via utility code or reflection, it's probably a better approach than
+code generation, which is fragile due to the very dynamic nature of OpenAPI and
+the very static nature of Go.
 
 ## Overview
 
@@ -42,24 +48,34 @@ The `/components/schemas` section in OpenAPI defines reusable objects, so Go
 types are generated for these. The Pet Store example defines `Error`, `Pet`,
 `Pets` and `NewPet`, so we do the same in Go:
 ```go
-// Type definition for component schema "Error"
+// Error defines model for Error.
 type Error struct {
-    Code    int32  `json:"code"`
+    // Error code
+    Code int32 `json:"code"`
+
+    // Error message
     Message string `json:"message"`
 }
 
-// Type definition for component schema "NewPet"
+// NewPet defines model for NewPet.
 type NewPet struct {
-    Name string  `json:"name"`
-    Tag  *string `json:"tag,omitempty"`
+    // Name of the pet
+    Name string `json:"name"`
+
+    // Type of the pet
+    Tag *string `json:"tag,omitempty"`
 }
 
-// Type definition for component schema "Pet"
+// Pet defines model for Pet.
 type Pet struct {
-    // Embedded struct due to allOf(#/components/schemas/NewPet)
-    NewPet
-    // Embedded fields due to inline allOf schema
+    // 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"`
 }
 
 // Type definition for component schema "Pets"
@@ -419,7 +435,7 @@ If you generate client-code, you can use some default-provided security provider
 which help you to use the various OpenAPI 3 Authentication mechanism.
 
 
-```
+```go
     import (
         "github.com/deepmap/oapi-codegen/pkg/securityprovider"
     )
diff --git a/cmd/oapi-codegen/oapi-codegen.go b/cmd/oapi-codegen/oapi-codegen.go
index f1a1ca2..fe43be6 100644
--- a/cmd/oapi-codegen/oapi-codegen.go
+++ b/cmd/oapi-codegen/oapi-codegen.go
@@ -19,7 +19,6 @@ import (
 	"io/ioutil"
 	"os"
 	"path"
-	"path/filepath"
 	"runtime/debug"
 	"strings"
 
@@ -35,44 +34,67 @@ func errExit(format string, args ...interface{}) {
 }
 
 var (
-	flagPackageName    string
-	flagGenerate       string
 	flagOutputFile     string
-	flagIncludeTags    string
-	flagExcludeTags    string
-	flagTemplatesDir   string
-	flagImportMapping  string
-	flagExcludeSchemas string
 	flagConfigFile     string
-	flagAliasTypes     bool
+	flagOldConfigStyle bool
+	flagOutputConfig   bool
 	flagPrintVersion   bool
+	flagPackageName    string
+
+	// The options below are deprecated, and they will be removed in a future
+	// release. Please use the new config file format.
+	flagGenerate           string
+	flagIncludeTags        string
+	flagExcludeTags        string
+	flagTemplatesDir       string
+	flagImportMapping      string
+	flagExcludeSchemas     string
+	flagResponseTypeSuffix string
+	flagAliasTypes         bool
 )
 
 type configuration struct {
-	PackageName     string            `yaml:"package"`
-	GenerateTargets []string          `yaml:"generate"`
-	OutputFile      string            `yaml:"output"`
-	IncludeTags     []string          `yaml:"include-tags"`
-	ExcludeTags     []string          `yaml:"exclude-tags"`
-	TemplatesDir    string            `yaml:"templates"`
-	ImportMapping   map[string]string `yaml:"import-mapping"`
-	ExcludeSchemas  []string          `yaml:"exclude-schemas"`
+	codegen.Configuration `yaml:",inline"`
+
+	// OutputFile is the filename to output.
+	OutputFile string `yaml:"output,omitempty"`
 }
 
-func main() {
+// This structure is deprecated. Please add no more flags here. It is here
+// for backwards compatibility and it will be removed in the future.
+type oldConfiguration struct {
+	PackageName        string                       `yaml:"package"`
+	GenerateTargets    []string                     `yaml:"generate"`
+	OutputFile         string                       `yaml:"output"`
+	IncludeTags        []string                     `yaml:"include-tags"`
+	ExcludeTags        []string                     `yaml:"exclude-tags"`
+	TemplatesDir       string                       `yaml:"templates"`
+	ImportMapping      map[string]string            `yaml:"import-mapping"`
+	ExcludeSchemas     []string                     `yaml:"exclude-schemas"`
+	ResponseTypeSuffix string                       `yaml:"response-type-suffix"`
+	Compatibility      codegen.CompatibilityOptions `yaml:"compatibility"`
+}
 
+func main() {
+	flag.StringVar(&flagOutputFile, "o", "", "Where to output generated code, stdout is default")
+	flag.BoolVar(&flagOldConfigStyle, "old-config-style", false, "whether to use the older style config file format")
+	flag.BoolVar(&flagOutputConfig, "output-config", false, "when true, outputs a configuration file for oapi-codegen using current settings")
+	flag.StringVar(&flagConfigFile, "config", "", "a YAML config file that controls oapi-codegen behavior")
+	flag.BoolVar(&flagPrintVersion, "version", false, "when specified, print version and exit")
 	flag.StringVar(&flagPackageName, "package", "", "The package name for generated code")
+
+	// 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"`)
-	flag.StringVar(&flagOutputFile, "o", "", "Where to output generated code, stdout is default")
 	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")
 	flag.StringVar(&flagImportMapping, "import-mapping", "", "A dict from the external reference to golang package path")
 	flag.StringVar(&flagExcludeSchemas, "exclude-schemas", "", "A comma separated list of schemas which must be excluded from generation")
-	flag.StringVar(&flagConfigFile, "config", "", "a YAML config file that controls oapi-codegen behavior")
+	flag.StringVar(&flagResponseTypeSuffix, "response-type-suffix", "", "the suffix used for responses types")
 	flag.BoolVar(&flagAliasTypes, "alias-types", false, "Alias type declarations of possible")
-	flag.BoolVar(&flagPrintVersion, "version", false, "when specified, print version and exit")
+
 	flag.Parse()
 
 	if flagPrintVersion {
@@ -91,52 +113,56 @@ func main() {
 		os.Exit(1)
 	}
 
-	cfg := configFromFlags()
-
-	// If the package name has not been specified, we will use the name of the
-	// swagger file.
-	if cfg.PackageName == "" {
-		path := flag.Arg(0)
-		baseName := filepath.Base(path)
-		// Split the base name on '.' to get the first part of the file.
-		nameParts := strings.Split(baseName, ".")
-		cfg.PackageName = codegen.ToCamelCase(nameParts[0])
-	}
-
-	opts := codegen.Options{
-		AliasTypes: flagAliasTypes,
-	}
-	for _, g := range cfg.GenerateTargets {
-		switch g {
-		case "client":
-			opts.GenerateClient = true
-		case "chi-server":
-			opts.GenerateChiServer = true
-		case "server":
-			opts.GenerateEchoServer = true
-		case "gin":
-			opts.GenerateGinServer = true
-		case "types":
-			opts.GenerateTypes = true
-		case "spec":
-			opts.EmbedSpec = true
-		case "skip-fmt":
-			opts.SkipFmt = true
-		case "skip-prune":
-			opts.SkipPrune = true
-		default:
-			fmt.Printf("unknown generate option %s\n", g)
-			flag.PrintDefaults()
-			os.Exit(1)
+	var opts configuration
+	if !flagOldConfigStyle {
+		// We simply read the configuration from disk.
+		if flagConfigFile != "" {
+			buf, err := ioutil.ReadFile(flagConfigFile)
+			if err != nil {
+				errExit("error reading config file '%s': %v", flagConfigFile, err)
+			}
+			err = yaml.Unmarshal(buf, &opts)
+			if err != nil {
+				errExit("error parsing'%s' as YAML: %v", flagConfigFile, err)
+			}
+		}
+		var err error
+		opts, err = updateConfigFromFlags(opts)
+		if err != nil {
+			errExit("error processing flags: %v", err)
+		}
+	} else {
+		var oldConfig oldConfiguration
+		if flagConfigFile != "" {
+			buf, err := ioutil.ReadFile(flagConfigFile)
+			if err != nil {
+				errExit("error reading config file '%s': %v", flagConfigFile, err)
+			}
+			err = yaml.Unmarshal(buf, &oldConfig)
+			if err != nil {
+				errExit("error parsing'%s' as YAML: %v", flagConfigFile, err)
+			}
 		}
+		opts = newConfigFromOldConfig(oldConfig)
 	}
 
-	opts.IncludeTags = cfg.IncludeTags
-	opts.ExcludeTags = cfg.ExcludeTags
-	opts.ExcludeSchemas = cfg.ExcludeSchemas
+	// Ensure default values are set if user hasn't specified some needed
+	// fields.
+	opts.Configuration = opts.UpdateDefaults()
 
-	if opts.GenerateEchoServer && opts.GenerateChiServer {
-		errExit("can not specify both server and chi-server targets simultaneously")
+	// Now, ensure that the config options are valid.
+	if err := opts.Validate(); err != nil {
+		errExit("configuration error: %v", err)
+	}
+
+	// If the user asked to output configuration, output it to stdout and exit
+	if flagOutputConfig {
+		buf, err := yaml.Marshal(opts)
+		if err != nil {
+			errExit("error YAML marshaling configuration: %v", err)
+		}
+		fmt.Print(string(buf))
+		return
 	}
 
 	swagger, err := util.LoadSwagger(flag.Arg(0))
@@ -144,21 +170,13 @@ func main() {
 		errExit("error loading swagger spec in %s\n: %s", flag.Arg(0), err)
 	}
 
-	templates, err := loadTemplateOverrides(cfg.TemplatesDir)
-	if err != nil {
-		errExit("error loading template overrides: %s\n", err)
-	}
-	opts.UserTemplates = templates
-
-	opts.ImportMapping = cfg.ImportMapping
-
-	code, err := codegen.Generate(swagger, cfg.PackageName, opts)
+	code, err := codegen.Generate(swagger, opts.Configuration)
 	if err != nil {
 		errExit("error generating code: %s\n", err)
 	}
 
-	if cfg.OutputFile != "" {
-		err = ioutil.WriteFile(cfg.OutputFile, []byte(code), 0644)
+	if opts.OutputFile != "" {
+		err = ioutil.WriteFile(opts.OutputFile, []byte(code), 0644)
 		if err != nil {
 			errExit("error writing generated code to file: %s", err)
 		}
@@ -203,25 +221,54 @@ func loadTemplateOverrides(templatesDir string) (map[string]string, error) {
 	return templates, nil
 }
 
-// configFromFlags parses the flags and the config file. Anything which is
-// a zerovalue in the configuration file will be replaced with the flag
-// value, this means that the config file overrides flag values.
-func configFromFlags() *configuration {
-	var cfg configuration
+// updateConfigFromFlags updates a loaded configuration from flags. Flags
+// override anything in the file. We generate errors for command line options
+// associated with the old style configuration
+func updateConfigFromFlags(cfg configuration) (configuration, error) {
+	if flagPackageName != "" {
+		cfg.PackageName = flagPackageName
+	}
 
-	// Load the configuration file first.
-	if flagConfigFile != "" {
-		f, err := os.Open(flagConfigFile)
-		if err != nil {
-			errExit("failed to open config file with error: %v\n", err)
-		}
-		defer f.Close()
-		err = yaml.NewDecoder(f).Decode(&cfg)
-		if err != nil {
-			errExit("error parsing config file: %v\n", err)
-		}
+	var unsupportedFlags []string
+
+	if flagGenerate != "types,client,server,spec" {
+		unsupportedFlags = append(unsupportedFlags, "--generate")
+	}
+	if flagIncludeTags != "" {
+		unsupportedFlags = append(unsupportedFlags, "--include-tags")
+	}
+	if flagExcludeTags != "" {
+		unsupportedFlags = append(unsupportedFlags, "--exclude-tags")
+	}
+	if flagTemplatesDir != "" {
+		unsupportedFlags = append(unsupportedFlags, "--templates")
 	}
+	if flagImportMapping != "" {
+		unsupportedFlags = append(unsupportedFlags, "--import-mapping")
+	}
+	if flagExcludeSchemas != "" {
+		unsupportedFlags = append(unsupportedFlags, "--exclude-schemas")
+	}
+	if flagResponseTypeSuffix != "" {
+		unsupportedFlags = append(unsupportedFlags, "--response-type-suffix")
+	}
+	if flagAliasTypes {
+		unsupportedFlags = append(unsupportedFlags, "--alias-types")
+	}
+
+	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",
+			strings.Join(unsupportedFlags, ", "))
+	}
+
+	return cfg, nil
+}
 
+// updateOldConfigFromFlags parses the flags and the config file. Anything which is
+// a zerovalue in the configuration file will be replaced with the flag
+// value, this means that the config file overrides flag values.
+func updateOldConfigFromFlags(cfg oldConfiguration) oldConfiguration {
 	if cfg.PackageName == "" {
 		cfg.PackageName = flagPackageName
 	}
@@ -250,5 +297,61 @@ func configFromFlags() *configuration {
 	if cfg.OutputFile == "" {
 		cfg.OutputFile = flagOutputFile
 	}
-	return &cfg
+	return cfg
+}
+
+func newConfigFromOldConfig(c oldConfiguration) configuration {
+	// Take flags into account.
+	cfg := updateOldConfigFromFlags(c)
+
+	// Now, copy over field by field, translating flags and old values as
+	// necessary.
+	opts := codegen.Configuration{
+		PackageName: cfg.PackageName,
+	}
+	opts.OutputOptions.ResponseTypeSuffix = flagResponseTypeSuffix
+
+	for _, g := range cfg.GenerateTargets {
+		switch g {
+		case "client":
+			opts.Generate.Client = true
+		case "chi-server":
+			opts.Generate.ChiServer = true
+		case "server":
+			opts.Generate.EchoServer = true
+		case "gin":
+			opts.Generate.GinServer = true
+		case "types":
+			opts.Generate.Models = true
+		case "spec":
+			opts.Generate.EmbeddedSpec = true
+		case "skip-fmt":
+			opts.OutputOptions.SkipFmt = true
+		case "skip-prune":
+			opts.OutputOptions.SkipPrune = true
+		default:
+			fmt.Printf("unknown generate option %s\n", g)
+			flag.PrintDefaults()
+			os.Exit(1)
+		}
+	}
+
+	opts.OutputOptions.IncludeTags = cfg.IncludeTags
+	opts.OutputOptions.ExcludeTags = cfg.ExcludeTags
+	opts.OutputOptions.ExcludeSchemas = cfg.ExcludeSchemas
+
+	templates, err := loadTemplateOverrides(cfg.TemplatesDir)
+	if err != nil {
+		errExit("error loading template overrides: %s\n", err)
+	}
+	opts.OutputOptions.UserTemplates = templates
+
+	opts.ImportMapping = cfg.ImportMapping
+
+	opts.Compatibility = cfg.Compatibility
+
+	return configuration{
+		Configuration: opts,
+		OutputFile:    cfg.OutputFile,
+	}
 }
diff --git a/examples/authenticated-api/echo/api/api.gen.go b/examples/authenticated-api/echo/api/api.gen.go
index 29d6ae4..45bc235 100644
--- a/examples/authenticated-api/echo/api/api.gen.go
+++ b/examples/authenticated-api/echo/api/api.gen.go
@@ -41,17 +41,15 @@ type Thing struct {
 
 // ThingWithID defines model for ThingWithID.
 type ThingWithID struct {
-	// Embedded struct due to allOf(#/components/schemas/Thing)
-	Thing `yaml:",inline"`
-	// Embedded fields due to inline allOf schema
-	Id int64 `json:"id"`
+	Id   int64  `json:"id"`
+	Name string `json:"name"`
 }
 
 // AddThingJSONBody defines parameters for AddThing.
-type AddThingJSONBody Thing
+type AddThingJSONBody = Thing
 
 // AddThingJSONRequestBody defines body for AddThing for application/json ContentType.
-type AddThingJSONRequestBody AddThingJSONBody
+type AddThingJSONRequestBody = AddThingJSONBody
 
 // RequestEditorFn  is the function signature for the RequestEditor callback function
 type RequestEditorFn func(ctx context.Context, req *http.Request) error
diff --git a/examples/authenticated-api/echo/api/config.yaml b/examples/authenticated-api/echo/api/config.yaml
index fa4680e..0b176ca 100644
--- a/examples/authenticated-api/echo/api/config.yaml
+++ b/examples/authenticated-api/echo/api/config.yaml
@@ -1,9 +1,9 @@
-output:
-  api.gen.go
 package: api
 generate:
-  - types
-  - client
-  - server
-  - spec
-  - skip-prune
+  echo-server: true
+  client: true
+  models: true
+  embedded-spec: true
+output: api.gen.go
+output-options:
+  skip-prune: true
diff --git a/examples/authenticated-api/echo/server/server.go b/examples/authenticated-api/echo/server/server.go
index d4399b8..c57a897 100644
--- a/examples/authenticated-api/echo/server/server.go
+++ b/examples/authenticated-api/echo/server/server.go
@@ -6,10 +6,11 @@ import (
 	"sort"
 	"sync"
 
-	"github.com/deepmap/oapi-codegen/examples/authenticated-api/echo/api"
-	"github.com/deepmap/oapi-codegen/pkg/middleware"
 	"github.com/getkin/kin-openapi/openapi3filter"
 	"github.com/labstack/echo/v4"
+
+	"github.com/deepmap/oapi-codegen/examples/authenticated-api/echo/api"
+	"github.com/deepmap/oapi-codegen/pkg/middleware"
 )
 
 type server struct {
@@ -59,7 +60,10 @@ func (s *server) ListThings(ctx echo.Context) error {
 
 	for _, key := range thingKeys {
 		thing := s.things[key]
-		things = append(things, api.ThingWithID{Thing: thing, Id: key})
+		things = append(things, api.ThingWithID{
+			Id:   key,
+			Name: thing.Name,
+		})
 	}
 
 	s.RUnlock()
@@ -97,8 +101,8 @@ func (s *server) AddThing(ctx echo.Context) error {
 
 	s.things[s.lastID] = thing
 	thingWithId := api.ThingWithID{
-		Thing: thing,
-		Id:    s.lastID,
+		Name: thing.Name,
+		Id:   s.lastID,
 	}
 	s.lastID++
 
diff --git a/examples/petstore-expanded/chi/api/cfg.yaml b/examples/petstore-expanded/chi/api/cfg.yaml
new file mode 100644
index 0000000..6d2fa08
--- /dev/null
+++ b/examples/petstore-expanded/chi/api/cfg.yaml
@@ -0,0 +1,6 @@
+package: api
+generate:
+  chi-server: true
+  models: true
+  embedded-spec: true
+output: petstore.gen.go
diff --git a/examples/petstore-expanded/chi/api/petstore.gen.go b/examples/petstore-expanded/chi/api/petstore.gen.go
index be839b0..bf52a20 100644
--- a/examples/petstore-expanded/chi/api/petstore.gen.go
+++ b/examples/petstore-expanded/chi/api/petstore.gen.go
@@ -38,27 +38,30 @@ type NewPet struct {
 
 // Pet defines model for Pet.
 type Pet struct {
-	// Embedded struct due to allOf(#/components/schemas/NewPet)
-	NewPet `yaml:",inline"`
-	// Embedded fields due to inline allOf schema
 	// 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 `json:"tags,omitempty"`
+	Tags *[]string `form:"tags,omitempty" json:"tags,omitempty"`
 
 	// maximum number of results to return
-	Limit *int32 `json:"limit,omitempty"`
+	Limit *int32 `form:"limit,omitempty" json:"limit,omitempty"`
 }
 
 // AddPetJSONBody defines parameters for AddPet.
-type AddPetJSONBody NewPet
+type AddPetJSONBody = NewPet
 
 // AddPetJSONRequestBody defines body for AddPet for application/json ContentType.
-type AddPetJSONRequestBody AddPetJSONBody
+type AddPetJSONRequestBody = AddPetJSONBody
 
 // ServerInterface represents all server handlers.
 type ServerInterface interface {
@@ -151,7 +154,7 @@ func (siw *ServerInterfaceWrapper) DeletePet(w http.ResponseWriter, r *http.Requ
 	// ------------- Path parameter "id" -------------
 	var id int64
 
-	err = runtime.BindStyledParameter("simple", false, "id", chi.URLParam(r, "id"), &id)
+	err = runtime.BindStyledParameterWithLocation("simple", false, "id", runtime.ParamLocationPath, chi.URLParam(r, "id"), &id)
 	if err != nil {
 		siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "id", Err: err})
 		return
@@ -177,7 +180,7 @@ func (siw *ServerInterfaceWrapper) FindPetByID(w http.ResponseWriter, r *http.Re
 	// ------------- Path parameter "id" -------------
 	var id int64
 
-	err = runtime.BindStyledParameter("simple", false, "id", chi.URLParam(r, "id"), &id)
+	err = runtime.BindStyledParameterWithLocation("simple", false, "id", runtime.ParamLocationPath, chi.URLParam(r, "id"), &id)
 	if err != nil {
 		siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "id", Err: err})
 		return
diff --git a/examples/petstore-expanded/chi/api/petstore.go b/examples/petstore-expanded/chi/api/petstore.go
index a24e1f2..ea8c34a 100644
--- a/examples/petstore-expanded/chi/api/petstore.go
+++ b/examples/petstore-expanded/chi/api/petstore.go
@@ -1,4 +1,4 @@
-//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen --package=api --generate types,chi-server,spec -o petstore.gen.go ../../petstore-expanded.yaml
+//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen --config=cfg.yaml ../../petstore-expanded.yaml
 
 package api
 
diff --git a/examples/petstore-expanded/echo/api/petstore-types.gen.go b/examples/petstore-expanded/echo/api/petstore-types.gen.go
index 24add64..67ee510 100644
--- a/examples/petstore-expanded/echo/api/petstore-types.gen.go
+++ b/examples/petstore-expanded/echo/api/petstore-types.gen.go
@@ -23,24 +23,27 @@ type NewPet struct {
 
 // Pet defines model for Pet.
 type Pet struct {
-	// Embedded struct due to allOf(#/components/schemas/NewPet)
-	NewPet `yaml:",inline"`
-	// Embedded fields due to inline allOf schema
 	// 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 `json:"tags,omitempty"`
+	Tags *[]string `form:"tags,omitempty" json:"tags,omitempty"`
 
 	// maximum number of results to return
-	Limit *int32 `json:"limit,omitempty"`
+	Limit *int32 `form:"limit,omitempty" json:"limit,omitempty"`
 }
 
 // AddPetJSONBody defines parameters for AddPet.
-type AddPetJSONBody NewPet
+type AddPetJSONBody = NewPet
 
 // AddPetJSONRequestBody defines body for AddPet for application/json ContentType.
-type AddPetJSONRequestBody AddPetJSONBody
+type AddPetJSONRequestBody = AddPetJSONBody
diff --git a/examples/petstore-expanded/echo/api/server.cfg.yaml b/examples/petstore-expanded/echo/api/server.cfg.yaml
index 653a7c5..4c16329 100644
--- a/examples/petstore-expanded/echo/api/server.cfg.yaml
+++ b/examples/petstore-expanded/echo/api/server.cfg.yaml
@@ -1,6 +1,5 @@
-output:
-  petstore-server.gen.go
-generate:
-  - server
-  - spec
 package: api
+generate:
+  echo-server: true
+  embedded-spec: true
+output: petstore-server.gen.go
diff --git a/examples/petstore-expanded/echo/api/types.cfg.yaml b/examples/petstore-expanded/echo/api/types.cfg.yaml
index e812b1a..9ac30e1 100644
--- a/examples/petstore-expanded/echo/api/types.cfg.yaml
+++ b/examples/petstore-expanded/echo/api/types.cfg.yaml
@@ -1,5 +1,4 @@
-output:
-  petstore-types.gen.go
-generate:
-  - types
 package: api
+generate:
+  models: true
+output: petstore-types.gen.go
diff --git a/examples/petstore-expanded/gin/api/petstore-server.gen.go b/examples/petstore-expanded/gin/api/petstore-server.gen.go
index 9ed0d50..eeba01a 100644
--- a/examples/petstore-expanded/gin/api/petstore-server.gen.go
+++ b/examples/petstore-expanded/gin/api/petstore-server.gen.go
@@ -158,7 +158,9 @@ func RegisterHandlersWithOptions(router *gin.Engine, si ServerInterface, options
 	router.GET(options.BaseURL+"/pets/:id", wrapper.FindPetByID)
 
 	return router
-} // Base64 encoded, gzipped, json marshaled Swagger object
+}
+
+// Base64 encoded, gzipped, json marshaled Swagger object
 var swaggerSpec = []string{
 
 	"H4sIAAAAAAAC/+RXW48budH9KwV+32OnNbEXedBTvB4vICBrT+LdvKznoYZdkmrBSw9Z1FgY6L8HRbZu",
diff --git a/examples/petstore-expanded/gin/api/petstore-types.gen.go b/examples/petstore-expanded/gin/api/petstore-types.gen.go
index 24add64..67ee510 100644
--- a/examples/petstore-expanded/gin/api/petstore-types.gen.go
+++ b/examples/petstore-expanded/gin/api/petstore-types.gen.go
@@ -23,24 +23,27 @@ type NewPet struct {
 
 // Pet defines model for Pet.
 type Pet struct {
-	// Embedded struct due to allOf(#/components/schemas/NewPet)
-	NewPet `yaml:",inline"`
-	// Embedded fields due to inline allOf schema
 	// 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 `json:"tags,omitempty"`
+	Tags *[]string `form:"tags,omitempty" json:"tags,omitempty"`
 
 	// maximum number of results to return
-	Limit *int32 `json:"limit,omitempty"`
+	Limit *int32 `form:"limit,omitempty" json:"limit,omitempty"`
 }
 
 // AddPetJSONBody defines parameters for AddPet.
-type AddPetJSONBody NewPet
+type AddPetJSONBody = NewPet
 
 // AddPetJSONRequestBody defines body for AddPet for application/json ContentType.
-type AddPetJSONRequestBody AddPetJSONBody
+type AddPetJSONRequestBody = AddPetJSONBody
diff --git a/examples/petstore-expanded/gin/api/petstore.go b/examples/petstore-expanded/gin/api/petstore.go
index ae54a97..f36fcf0 100644
--- a/examples/petstore-expanded/gin/api/petstore.go
+++ b/examples/petstore-expanded/gin/api/petstore.go
@@ -12,6 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen --config=types.cfg.yaml ../../petstore-expanded.yaml
+//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen --config=server.cfg.yaml ../../petstore-expanded.yaml
+
 package api
 
 import (
diff --git a/examples/petstore-expanded/gin/api/server.cfg.yaml b/examples/petstore-expanded/gin/api/server.cfg.yaml
index 1be3672..61944bc 100644
--- a/examples/petstore-expanded/gin/api/server.cfg.yaml
+++ b/examples/petstore-expanded/gin/api/server.cfg.yaml
@@ -1,5 +1,5 @@
-output: examples/petstore-expanded/gin/api/petstore-server.gen.go
-generate:
-  - gin
-  - spec
 package: api
+generate:
+  gin-server: true
+  embedded-spec: true
+output: petstore-server.gen.go
diff --git a/examples/petstore-expanded/gin/api/types.cfg.yaml b/examples/petstore-expanded/gin/api/types.cfg.yaml
index 3769b14..9ac30e1 100644
--- a/examples/petstore-expanded/gin/api/types.cfg.yaml
+++ b/examples/petstore-expanded/gin/api/types.cfg.yaml
@@ -1,4 +1,4 @@
-output: examples/petstore-expanded/gin/api/petstore-types.gen.go
-generate:
-  - types
 package: api
+generate:
+  models: true
+output: petstore-types.gen.go
diff --git a/examples/petstore-expanded/internal/doc.go b/examples/petstore-expanded/internal/doc.go
index ac961b7..db05a3a 100644
--- a/examples/petstore-expanded/internal/doc.go
+++ b/examples/petstore-expanded/internal/doc.go
@@ -17,4 +17,4 @@ package internal
 // server. The file petstore.gen.go is automatically generated from the schema
 
 // Run oapi-codegen to regenerate the petstore boilerplate
-//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen --package=petstore --generate types,client -o ../petstore-client.gen.go ../petstore-expanded.yaml
+//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen --old-config-style --package=petstore --generate types,client -o ../petstore-client.gen.go ../petstore-expanded.yaml
diff --git a/examples/petstore-expanded/petstore-client.gen.go b/examples/petstore-expanded/petstore-client.gen.go
index 9c163ed..6a73e9b 100644
--- a/examples/petstore-expanded/petstore-client.gen.go
+++ b/examples/petstore-expanded/petstore-client.gen.go
@@ -37,27 +37,30 @@ type NewPet struct {
 
 // Pet defines model for Pet.
 type Pet struct {
-	// Embedded struct due to allOf(#/components/schemas/NewPet)
-	NewPet `yaml:",inline"`
-	// Embedded fields due to inline allOf schema
 	// 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 `json:"tags,omitempty"`
+	Tags *[]string `form:"tags,omitempty" json:"tags,omitempty"`
 
 	// maximum number of results to return
-	Limit *int32 `json:"limit,omitempty"`
+	Limit *int32 `form:"limit,omitempty" json:"limit,omitempty"`
 }
 
 // AddPetJSONBody defines parameters for AddPet.
-type AddPetJSONBody NewPet
+type AddPetJSONBody = NewPet
 
 // AddPetJSONRequestBody defines body for AddPet for application/json ContentType.
-type AddPetJSONRequestBody AddPetJSONBody
+type AddPetJSONRequestBody = AddPetJSONBody
 
 // RequestEditorFn  is the function signature for the RequestEditor callback function
 type RequestEditorFn func(ctx context.Context, req *http.Request) error
diff --git a/go.mod b/go.mod
index 81500e6..7d46fee 100644
--- a/go.mod
+++ b/go.mod
@@ -7,7 +7,7 @@ require (
 	github.com/gin-gonic/gin v1.7.7
 	github.com/go-chi/chi/v5 v5.0.7
 	github.com/go-openapi/swag v0.21.1 // indirect
-	github.com/go-playground/validator/v10 v10.10.1 // indirect
+	github.com/go-playground/validator/v10 v10.11.0 // indirect
 	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
@@ -15,16 +15,16 @@ require (
 	github.com/labstack/echo/v4 v4.7.2
 	github.com/lestrrat-go/blackmagic v1.0.1 // indirect
 	github.com/lestrrat-go/iter v1.0.2 // indirect
-	github.com/lestrrat-go/jwx v1.2.23
+	github.com/lestrrat-go/jwx v1.2.24
 	github.com/mailru/easyjson v0.7.7 // indirect
 	github.com/matryer/moq v0.2.7
 	github.com/mattn/go-colorable v0.1.12 // indirect
 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 	github.com/stretchr/testify v1.7.1
 	github.com/ugorji/go v1.2.7 // indirect
-	golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect
-	golang.org/x/net v0.0.0-20220418201149-a630d4f3e7a2 // indirect
-	golang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect
+	golang.org/x/crypto v0.0.0-20220513210258-46612604a0f9 // indirect
+	golang.org/x/net v0.0.0-20220513224357-95641704303c // indirect
+	golang.org/x/sys v0.0.0-20220513210249-45d2b4557a2a // indirect
 	golang.org/x/time v0.0.0-20220411224347-583f2d630306 // indirect
 	golang.org/x/tools v0.1.10
 	golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f // indirect
diff --git a/go.sum b/go.sum
index 76609b2..98ca6d5 100644
--- a/go.sum
+++ b/go.sum
@@ -32,10 +32,10 @@ github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+
 github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
 github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
 github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
-github.com/go-playground/validator/v10 v10.10.1 h1:uA0+amWMiglNZKZ9FJRKUAe9U3RX91eVn1JYXMWt7ig=
-github.com/go-playground/validator/v10 v10.10.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
-github.com/goccy/go-json v0.9.6 h1:5/4CtRQdtsX0sal8fdVhTaiMN01Ri8BExZZ8iRmHQ6E=
-github.com/goccy/go-json v0.9.6/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
+github.com/go-playground/validator/v10 v10.11.0 h1:0W+xRM511GY47Yy3bZUbJVitCNg2BOGlCyvTqsp/xIw=
+github.com/go-playground/validator/v10 v10.11.0/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
+github.com/goccy/go-json v0.9.7 h1:IcB+Aqpx/iMHu5Yooh7jEzJk1JZ7Pjtmys2ukPr7EeM=
+github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
 github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
 github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
 github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
@@ -81,8 +81,8 @@ github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W
 github.com/lestrrat-go/iter v1.0.1/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc=
 github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI=
 github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4=
-github.com/lestrrat-go/jwx v1.2.23 h1:8oP5fY1yzCRraUNNyfAVdOkLCqY7xMZz11lVcvHqC1Y=
-github.com/lestrrat-go/jwx v1.2.23/go.mod h1:sAXjRwzSvCN6soO4RLoWWm1bVPpb8iOuv0IYfH8OWd8=
+github.com/lestrrat-go/jwx v1.2.24 h1:N6Qsn6TUsDzz+qgS/1xcfBtkQfnbwW01fLFJpuYgKsg=
+github.com/lestrrat-go/jwx v1.2.24/go.mod h1:zoNuZymNl5lgdcu6P7K6ie2QRll5HVfF4xwxBBK1NxY=
 github.com/lestrrat-go/option v1.0.0 h1:WqAWL8kh8VcSoD6xjSH34/1m8yxluXQbDeKNfvFeEO4=
 github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
 github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
@@ -138,9 +138,9 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
 golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA=
-golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.0.0-20220513210258-46612604a0f9 h1:NUzdAbFtCJSXU20AOXgeqaUwg8Ypg4MPYmL+d+rsB5c=
+golang.org/x/crypto v0.0.0-20220513210258-46612604a0f9/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o=
 golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@@ -148,8 +148,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20220418201149-a630d4f3e7a2 h1:6mzvA99KwZxbOrxww4EvWVQUnN1+xEu9tafK5ZxkYeA=
-golang.org/x/net v0.0.0-20220418201149-a630d4f3e7a2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
+golang.org/x/net v0.0.0-20220513224357-95641704303c h1:nF9mHSvoKBLkQNQhJZNsc66z2UzAMUbLGjC95CF3pU0=
+golang.org/x/net v0.0.0-20220513224357-95641704303c/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -164,8 +164,8 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0=
-golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220513210249-45d2b4557a2a h1:N2T1jUrTQE9Re6TFF5PhvEHXHCguynGhKjWVsIUt5cY=
+golang.org/x/sys v0.0.0-20220513210249-45d2b4557a2a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
diff --git a/pkg/chi-middleware/oapi_validate.go b/pkg/chi-middleware/oapi_validate.go
index d9e8306..dfce720 100644
--- a/pkg/chi-middleware/oapi_validate.go
+++ b/pkg/chi-middleware/oapi_validate.go
@@ -15,9 +15,13 @@ import (
 	"github.com/getkin/kin-openapi/routers/gorillamux"
 )
 
+// ErrorHandler is called when there is an error in validation
+type ErrorHandler func(w http.ResponseWriter, message string, statusCode int)
+
 // Options to customize request validation, openapi3filter specified options will be passed through.
 type Options struct {
-	Options openapi3filter.Options
+	Options      openapi3filter.Options
+	ErrorHandler ErrorHandler
 }
 
 // OapiRequestValidator Creates middleware to validate request by swagger spec.
@@ -39,7 +43,11 @@ func OapiRequestValidatorWithOptions(swagger *openapi3.T, options *Options) func
 
 			// validate request
 			if statusCode, err := validateRequest(r, router, options); err != nil {
-				http.Error(w, err.Error(), statusCode)
+				if options != nil && options.ErrorHandler != nil {
+					options.ErrorHandler(w, err.Error(), statusCode)
+				} else {
+					http.Error(w, err.Error(), statusCode)
+				}
 				return
 			}
 
diff --git a/pkg/codegen/codegen.go b/pkg/codegen/codegen.go
index abe0072..0fc91dc 100644
--- a/pkg/codegen/codegen.go
+++ b/pkg/codegen/codegen.go
@@ -33,22 +33,11 @@ import (
 //go:embed templates
 var templates embed.FS
 
-// Options defines the optional code to generate.
-type Options struct {
-	GenerateChiServer  bool              // GenerateChiServer specifies whether to generate chi server boilerplate
-	GenerateEchoServer bool              // GenerateEchoServer specifies whether to generate echo server boilerplate
-	GenerateGinServer  bool              // GenerateGinServer specifies whether to generate echo server boilerplate
-	GenerateClient     bool              // GenerateClient specifies whether to generate client boilerplate
-	GenerateTypes      bool              // GenerateTypes specifies whether to generate type definitions
-	EmbedSpec          bool              // Whether to embed the swagger spec in the generated code
-	SkipFmt            bool              // Whether to skip go imports on the generated code
-	SkipPrune          bool              // Whether to skip pruning unused components on the generated code
-	AliasTypes         bool              // Whether to alias types if possible
-	IncludeTags        []string          // Only include operations that have one of these tags. Ignored when empty.
-	ExcludeTags        []string          // Exclude operations that have one of these tags. Ignored when empty.
-	UserTemplates      map[string]string // Override built-in templates from user-provided files
-	ImportMapping      map[string]string // ImportMapping specifies the golang package path for each external reference
-	ExcludeSchemas     []string          // Exclude from generation schemas with given names. Ignored when empty.
+// globalState stores all global state. Please don't put global state anywhere
+// else so that we can easily track it.
+var globalState struct {
+	options Configuration
+	spec    *openapi3.T
 }
 
 // goImport represents a go package to be imported in the generated code
@@ -79,7 +68,7 @@ func (im importMap) GoImports() []string {
 
 var importMapping importMap
 
-func constructImportMapping(input map[string]string) importMap {
+func constructImportMapping(importMapping map[string]string) importMap {
 	var (
 		pathToName = map[string]string{}
 		result     = importMap{}
@@ -87,7 +76,7 @@ func constructImportMapping(input map[string]string) importMap {
 
 	{
 		var packagePaths []string
-		for _, packageName := range input {
+		for _, packageName := range importMapping {
 			packagePaths = append(packagePaths, packageName)
 		}
 		sort.Strings(packagePaths)
@@ -98,25 +87,34 @@ func constructImportMapping(input map[string]string) importMap {
 			}
 		}
 	}
-	for specPath, packagePath := range input {
+	for specPath, packagePath := range importMapping {
 		result[specPath] = goImport{Name: pathToName[packagePath], Path: packagePath}
 	}
 	return result
 }
 
-// Uses the Go templating engine to generate all of our server wrappers from
+// Generate uses the Go templating engine to generate all of our server wrappers from
 // the descriptions we've built up above from the schema objects.
 // opts defines
-func Generate(swagger *openapi3.T, packageName string, opts Options) (string, error) {
+func Generate(spec *openapi3.T, opts Configuration) (string, error) {
+	// This is global state
+	globalState.options = opts
+	globalState.spec = spec
+
 	importMapping = constructImportMapping(opts.ImportMapping)
 
-	filterOperationsByTag(swagger, opts)
-	if !opts.SkipPrune {
-		pruneUnusedComponents(swagger)
+	filterOperationsByTag(spec, opts)
+	if !opts.OutputOptions.SkipPrune {
+		pruneUnusedComponents(spec)
+	}
+
+	// if we are provided an override for the response type suffix update it
+	if opts.OutputOptions.ResponseTypeSuffix != "" {
+		responseTypeSuffix = opts.OutputOptions.ResponseTypeSuffix
 	}
 
 	// This creates the golang templates text package
-	TemplateFunctions["opts"] = func() Options { return opts }
+	TemplateFunctions["opts"] = func() Configuration { return globalState.options }
 	t := template.New("oapi-codegen").Funcs(TemplateFunctions)
 	// This parses all of our own template files into the template object
 	// above
@@ -127,22 +125,22 @@ func Generate(swagger *openapi3.T, packageName string, opts Options) (string, er
 
 	// Override built-in templates with user-provided versions
 	for _, tpl := range t.Templates() {
-		if _, ok := opts.UserTemplates[tpl.Name()]; ok {
+		if _, ok := opts.OutputOptions.UserTemplates[tpl.Name()]; ok {
 			utpl := t.New(tpl.Name())
-			if _, err := utpl.Parse(opts.UserTemplates[tpl.Name()]); err != nil {
+			if _, err := utpl.Parse(opts.OutputOptions.UserTemplates[tpl.Name()]); err != nil {
 				return "", fmt.Errorf("error parsing user-provided template %q: %w", tpl.Name(), err)
 			}
 		}
 	}
 
-	ops, err := OperationDefinitions(swagger)
+	ops, err := OperationDefinitions(spec)
 	if err != nil {
 		return "", fmt.Errorf("error creating operation definitions: %w", err)
 	}
 
 	var typeDefinitions, constantDefinitions string
-	if opts.GenerateTypes {
-		typeDefinitions, err = GenerateTypeDefinitions(t, swagger, ops, opts.ExcludeSchemas)
+	if opts.Generate.Models {
+		typeDefinitions, err = GenerateTypeDefinitions(t, spec, ops, opts.OutputOptions.ExcludeSchemas)
 		if err != nil {
 			return "", fmt.Errorf("error generating type definitions: %w", err)
 		}
@@ -155,7 +153,7 @@ func Generate(swagger *openapi3.T, packageName string, opts Options) (string, er
 	}
 
 	var echoServerOut string
-	if opts.GenerateEchoServer {
+	if opts.Generate.EchoServer {
 		echoServerOut, err = GenerateEchoServer(t, ops)
 		if err != nil {
 			return "", fmt.Errorf("error generating Go handlers for Paths: %w", err)
@@ -163,7 +161,7 @@ func Generate(swagger *openapi3.T, packageName string, opts Options) (string, er
 	}
 
 	var chiServerOut string
-	if opts.GenerateChiServer {
+	if opts.Generate.ChiServer {
 		chiServerOut, err = GenerateChiServer(t, ops)
 		if err != nil {
 			return "", fmt.Errorf("error generating Go handlers for Paths: %w", err)
@@ -171,7 +169,7 @@ func Generate(swagger *openapi3.T, packageName string, opts Options) (string, er
 	}
 
 	var ginServerOut string
-	if opts.GenerateGinServer {
+	if opts.Generate.GinServer {
 		ginServerOut, err = GenerateGinServer(t, ops)
 		if err != nil {
 			return "", fmt.Errorf("error generating Go handlers for Paths: %w", err)
@@ -179,7 +177,7 @@ func Generate(swagger *openapi3.T, packageName string, opts Options) (string, er
 	}
 
 	var clientOut string
-	if opts.GenerateClient {
+	if opts.Generate.Client {
 		clientOut, err = GenerateClient(t, ops)
 		if err != nil {
 			return "", fmt.Errorf("error generating client: %w", err)
@@ -187,7 +185,7 @@ func Generate(swagger *openapi3.T, packageName string, opts Options) (string, er
 	}
 
 	var clientWithResponsesOut string
-	if opts.GenerateClient {
+	if opts.Generate.Client {
 		clientWithResponsesOut, err = GenerateClientWithResponses(t, ops)
 		if err != nil {
 			return "", fmt.Errorf("error generating client with responses: %w", err)
@@ -195,8 +193,8 @@ func Generate(swagger *openapi3.T, packageName string, opts Options) (string, er
 	}
 
 	var inlinedSpec string
-	if opts.EmbedSpec {
-		inlinedSpec, err = GenerateInlinedSpec(t, importMapping, swagger)
+	if opts.Generate.EmbeddedSpec {
+		inlinedSpec, err = GenerateInlinedSpec(t, importMapping, spec)
 		if err != nil {
 			return "", fmt.Errorf("error generating Go handlers for Paths: %w", err)
 		}
@@ -206,7 +204,7 @@ func Generate(swagger *openapi3.T, packageName string, opts Options) (string, er
 	w := bufio.NewWriter(&buf)
 
 	externalImports := importMapping.GoImports()
-	importsOut, err := GenerateImports(t, externalImports, packageName)
+	importsOut, err := GenerateImports(t, externalImports, opts.PackageName)
 	if err != nil {
 		return "", fmt.Errorf("error generating imports: %w", err)
 	}
@@ -226,7 +224,7 @@ func Generate(swagger *openapi3.T, packageName string, opts Options) (string, er
 		return "", fmt.Errorf("error writing type definitions: %w", err)
 	}
 
-	if opts.GenerateClient {
+	if opts.Generate.Client {
 		_, err = w.WriteString(clientOut)
 		if err != nil {
 			return "", fmt.Errorf("error writing client: %w", err)
@@ -237,28 +235,28 @@ func Generate(swagger *openapi3.T, packageName string, opts Options) (string, er
 		}
 	}
 
-	if opts.GenerateEchoServer {
+	if opts.Generate.EchoServer {
 		_, err = w.WriteString(echoServerOut)
 		if err != nil {
 			return "", fmt.Errorf("error writing server path handlers: %w", err)
 		}
 	}
 
-	if opts.GenerateChiServer {
+	if opts.Generate.ChiServer {
 		_, err = w.WriteString(chiServerOut)
 		if err != nil {
 			return "", fmt.Errorf("error writing server path handlers: %w", err)
 		}
 	}
 
-	if opts.GenerateGinServer {
+	if opts.Generate.GinServer {
 		_, err = w.WriteString(ginServerOut)
 		if err != nil {
 			return "", fmt.Errorf("error writing server path handlers: %w", err)
 		}
 	}
 
-	if opts.EmbedSpec {
+	if opts.Generate.EmbeddedSpec {
 		_, err = w.WriteString(inlinedSpec)
 		if err != nil {
 			return "", fmt.Errorf("error writing inlined spec: %w", err)
@@ -275,11 +273,11 @@ func Generate(swagger *openapi3.T, packageName string, opts Options) (string, er
 
 	// The generation code produces unindented horrors. Use the Go Imports
 	// to make it all pretty.
-	if opts.SkipFmt {
+	if opts.OutputOptions.SkipFmt {
 		return goCode, nil
 	}
 
-	outBytes, err := imports.Process(packageName+".go", []byte(goCode), nil)
+	outBytes, err := imports.Process(opts.PackageName+".go", []byte(goCode), nil)
 	if err != nil {
 		fmt.Println(goCode)
 		return "", fmt.Errorf("error formatting Go code: %w", err)
@@ -361,7 +359,7 @@ func GenerateConstants(t *template.Template, ops []OperationDefinition) (string,
 	return GenerateTemplates([]string{"constants.tmpl"}, t, constants)
 }
 
-// Generates type definitions for any custom types defined in the
+// GenerateTypesForSchemas generates type definitions for any custom types defined in the
 // components/schemas section of the Swagger spec.
 func GenerateTypesForSchemas(t *template.Template, schemas map[string]*openapi3.SchemaRef, excludeSchemas []string) ([]TypeDefinition, error) {
 	excludeSchemasMap := make(map[string]bool)
@@ -381,9 +379,14 @@ func GenerateTypesForSchemas(t *template.Template, schemas map[string]*openapi3.
 			return nil, fmt.Errorf("error converting Schema %s to Go type: %w", schemaName, err)
 		}
 
+		goTypeName, err := renameSchema(schemaName, schemaRef)
+		if err != nil {
+			return nil, fmt.Errorf("error making name for components/schemas/%s: %w", schemaName, err)
+		}
+
 		types = append(types, TypeDefinition{
 			JsonName: schemaName,
-			TypeName: SchemaNameToTypeName(schemaName),
+			TypeName: goTypeName,
 			Schema:   goSchema,
 		})
 
@@ -392,7 +395,7 @@ func GenerateTypesForSchemas(t *template.Template, schemas map[string]*openapi3.
 	return types, nil
 }
 
-// Generates type definitions for any custom types defined in the
+// GenerateTypesForParameters generates type definitions for any custom types defined in the
 // components/parameters section of the Swagger spec.
 func GenerateTypesForParameters(t *template.Template, params map[string]*openapi3.ParameterRef) ([]TypeDefinition, error) {
 	var types []TypeDefinition
@@ -404,10 +407,15 @@ func GenerateTypesForParameters(t *template.Template, params map[string]*openapi
 			return nil, fmt.Errorf("error generating Go type for schema in parameter %s: %w", paramName, err)
 		}
 
+		goTypeName, err := renameParameter(paramName, paramOrRef)
+		if err != nil {
+			return nil, fmt.Errorf("error making name for components/parameters/%s: %w", paramName, err)
+		}
+
 		typeDef := TypeDefinition{
 			JsonName: paramName,
 			Schema:   goType,
-			TypeName: SchemaNameToTypeName(paramName),
+			TypeName: goTypeName,
 		}
 
 		if paramOrRef.Ref != "" {
@@ -424,7 +432,7 @@ func GenerateTypesForParameters(t *template.Template, params map[string]*openapi
 	return types, nil
 }
 
-// Generates type definitions for any custom types defined in the
+// GenerateTypesForResponses generates type definitions for any custom types defined in the
 // components/responses section of the Swagger spec.
 func GenerateTypesForResponses(t *template.Template, responses openapi3.Responses) ([]TypeDefinition, error) {
 	var types []TypeDefinition
@@ -443,10 +451,15 @@ func GenerateTypesForResponses(t *template.Template, responses openapi3.Response
 				return nil, fmt.Errorf("error generating Go type for schema in response %s: %w", responseName, err)
 			}
 
+			goTypeName, err := renameResponse(responseName, responseOrRef)
+			if err != nil {
+				return nil, fmt.Errorf("error making name for components/responses/%s: %w", responseName, err)
+			}
+
 			typeDef := TypeDefinition{
 				JsonName: responseName,
 				Schema:   goType,
-				TypeName: SchemaNameToTypeName(responseName),
+				TypeName: goTypeName,
 			}
 
 			if responseOrRef.Ref != "" {
@@ -463,35 +476,40 @@ func GenerateTypesForResponses(t *template.Template, responses openapi3.Response
 	return types, nil
 }
 
-// Generates type definitions for any custom types defined in the
+// GenerateTypesForRequestBodies generates type definitions for any custom types defined in the
 // components/requestBodies section of the Swagger spec.
 func GenerateTypesForRequestBodies(t *template.Template, bodies map[string]*openapi3.RequestBodyRef) ([]TypeDefinition, error) {
 	var types []TypeDefinition
 
-	for _, bodyName := range SortedRequestBodyKeys(bodies) {
-		bodyOrRef := bodies[bodyName]
+	for _, requestBodyName := range SortedRequestBodyKeys(bodies) {
+		requestBodyRef := bodies[requestBodyName]
 
 		// As for responses, we will only generate Go code for JSON bodies,
 		// the other body formats are up to the user.
-		response := bodyOrRef.Value
+		response := requestBodyRef.Value
 		jsonBody, found := response.Content["application/json"]
 		if found {
-			goType, err := GenerateGoSchema(jsonBody.Schema, []string{bodyName})
+			goType, err := GenerateGoSchema(jsonBody.Schema, []string{requestBodyName})
+			if err != nil {
+				return nil, fmt.Errorf("error generating Go type for schema in body %s: %w", requestBodyName, err)
+			}
+
+			goTypeName, err := renameRequestBody(requestBodyName, requestBodyRef)
 			if err != nil {
-				return nil, fmt.Errorf("error generating Go type for schema in body %s: %w", bodyName, err)
+				return nil, fmt.Errorf("error making name for components/schemas/%s: %w", requestBodyName, err)
 			}
 
 			typeDef := TypeDefinition{
-				JsonName: bodyName,
+				JsonName: requestBodyName,
 				Schema:   goType,
-				TypeName: SchemaNameToTypeName(bodyName),
+				TypeName: goTypeName,
 			}
 
-			if bodyOrRef.Ref != "" {
+			if requestBodyRef.Ref != "" {
 				// Generate a reference type for referenced bodies
-				refType, err := RefPathToGoType(bodyOrRef.Ref)
+				refType, err := RefPathToGoType(requestBodyRef.Ref)
 				if err != nil {
-					return nil, fmt.Errorf("error generating Go type for (%s) in body %s: %w", bodyOrRef.Ref, bodyName, err)
+					return nil, fmt.Errorf("error generating Go type for (%s) in body %s: %w", requestBodyRef.Ref, requestBodyName, err)
 				}
 				typeDef.TypeName = SchemaNameToTypeName(refType)
 			}
@@ -501,7 +519,7 @@ func GenerateTypesForRequestBodies(t *template.Template, bodies map[string]*open
 	return types, nil
 }
 
-// Helper function to pass a bunch of types to the template engine, and buffer
+// GenerateTypes passes a bunch of types to the template engine, and buffers
 // its output into a string.
 func GenerateTypes(t *template.Template, types []TypeDefinition) (string, error) {
 	m := map[string]bool{}
@@ -527,12 +545,12 @@ func GenerateTypes(t *template.Template, types []TypeDefinition) (string, error)
 }
 
 func GenerateEnums(t *template.Template, types []TypeDefinition) (string, error) {
-	c := Constants{
-		EnumDefinitions: []EnumDefinition{},
-	}
+	enums := []EnumDefinition{}
 
+	// Keep track of which enums we've generated
 	m := map[string]bool{}
 
+	// These are all types defined globally
 	for _, tp := range types {
 		if found := m[tp.TypeName]; found {
 			continue
@@ -545,7 +563,7 @@ func GenerateEnums(t *template.Template, types []TypeDefinition) (string, error)
 			if tp.Schema.GoType == "string" {
 				wrapper = `"`
 			}
-			c.EnumDefinitions = append(c.EnumDefinitions, EnumDefinition{
+			enums = append(enums, EnumDefinition{
 				Schema:       tp.Schema,
 				TypeName:     tp.TypeName,
 				ValueWrapper: wrapper,
@@ -553,7 +571,52 @@ func GenerateEnums(t *template.Template, types []TypeDefinition) (string, error)
 		}
 	}
 
-	return GenerateTemplates([]string{"constants.tmpl"}, t, c)
+	// Now, go through all the enums, and figure out if we have conflicts with
+	// any others.
+	for i := range enums {
+		// Look through all other enums not compared so far. Make sure we don't
+		// compare against self.
+		e1 := enums[i]
+		for j := i + 1; j < len(enums); j++ {
+			e2 := enums[j]
+
+			for e1key := range e1.GetValues() {
+				_, found := e2.GetValues()[e1key]
+				if found {
+					e1.Conflicts = true
+					e2.Conflicts = true
+					enums[i] = e1
+					enums[j] = e2
+					break
+				}
+			}
+		}
+
+		// now see if this enum conflicts with any global type names.
+		for _, tp := range types {
+			// Skip over enums, since we've handled those above.
+			if len(tp.Schema.EnumValues) > 0 {
+				continue
+			}
+			_, found := e1.Schema.EnumValues[tp.TypeName]
+			if found {
+				e1.Conflicts = true
+				enums[i] = e1
+			}
+		}
+
+		// Another edge case is that an enum value can conflict with its own
+		// type name.
+		_, found := e1.GetValues()[e1.TypeName]
+		if found {
+			e1.Conflicts = true
+			enums[i] = e1
+		}
+	}
+
+	// Now see if enums conflict with any non-enum typenames
+
+	return GenerateTemplates([]string{"constants.tmpl"}, t, Constants{EnumDefinitions: enums})
 }
 
 // Generate our import statements and package definition.
diff --git a/pkg/codegen/configuration.go b/pkg/codegen/configuration.go
new file mode 100644
index 0000000..084ab5b
--- /dev/null
+++ b/pkg/codegen/configuration.go
@@ -0,0 +1,96 @@
+package codegen
+
+import (
+	"errors"
+	"reflect"
+)
+
+// Configuration defines code generation customizations
+type Configuration struct {
+	PackageName   string               `yaml:"package"` // PackageName to generate
+	Generate      GenerateOptions      `yaml:"generate,omitempty"`
+	Compatibility CompatibilityOptions `yaml:"compatibility,omitempty"`
+	OutputOptions OutputOptions        `yaml:"output-options,omitempty"`
+	ImportMapping map[string]string    `yaml:"import-mapping,omitempty"` // ImportMapping specifies the golang package path for each external reference
+}
+
+// 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
+}
+
+// CompatibilityOptions specifies backward compatibility settings for the
+// code generator.
+type CompatibilityOptions struct {
+	// In the past, we merged schemas for `allOf` by inlining each schema
+	// within the schema list. This approach, though, is incorrect because
+	// `allOf` merges at the schema definition level, not at the resulting model
+	// level. So, new behavior merges OpenAPI specs but generates different code
+	// than we have in the past. Set OldMergeSchemas to true for the old behavior.
+	// Please see https://github.com/deepmap/oapi-codegen/issues/531
+	OldMergeSchemas bool `yaml:"old-merge-schemas"`
+	// Enum values can generate conflicting typenames, so we've updated the
+	// code for enum generation to avoid these conflicts, but it will result
+	// in some enum types being renamed in existing code. Set OldEnumConflicts to true
+	// to revert to old behavior. Please see:
+	// Please see https://github.com/deepmap/oapi-codegen/issues/549
+	OldEnumConflicts bool `yaml:"old-enum-conflicts"`
+	// It was a mistake to generate a go type definition for every $ref in
+	// the OpenAPI schema. New behavior uses type aliases where possible, but
+	// this can generate code which breaks existing builds. Set OldAliasing to true
+	// for old behavior.
+	// Please see https://github.com/deepmap/oapi-codegen/issues/549
+	OldAliasing bool `yaml:"old-aliasing"`
+}
+
+// OutputOptions are used to modify the output code in some way.
+type OutputOptions struct {
+	SkipFmt       bool              `yaml:"skip-fmt,omitempty"`       // Whether to skip go imports on the generated code
+	SkipPrune     bool              `yaml:"skip-prune,omitempty"`     // Whether to skip pruning unused components on the generated code
+	IncludeTags   []string          `yaml:"include-tags,omitempty"`   // Only include operations that have one of these tags. Ignored when empty.
+	ExcludeTags   []string          `yaml:"exclude-tags,omitempty"`   // Exclude operations that have one of these tags. Ignored when empty.
+	UserTemplates map[string]string `yaml:"user-templates,omitempty"` // Override built-in templates from user-provided files
+
+	ExcludeSchemas     []string `yaml:"exclude-schemas,omitempty"`      // Exclude from generation schemas with given names. Ignored when empty.
+	ResponseTypeSuffix string   `yaml:"response-type-suffix,omitempty"` // The suffix used for responses types
+}
+
+// UpdateDefaults sets reasonable default values for unset fields in Configuration
+func (o Configuration) UpdateDefaults() Configuration {
+	if reflect.ValueOf(o.Generate).IsZero() {
+		o.Generate = GenerateOptions{
+			EchoServer:   true,
+			Models:       true,
+			EmbeddedSpec: true,
+		}
+	}
+	return o
+}
+
+// Validate checks whether Configuration represent a valid configuration
+func (o Configuration) Validate() error {
+	if o.PackageName == "" {
+		return errors.New("package name must be specified")
+	}
+
+	// Only one server type should be specified at a time.
+	nServers := 0
+	if o.Generate.ChiServer {
+		nServers++
+	}
+	if o.Generate.EchoServer {
+		nServers++
+	}
+	if o.Generate.GinServer {
+		nServers++
+	}
+	if nServers > 1 {
+		return errors.New("only one server type is supported at a time")
+	}
+	return nil
+}
diff --git a/pkg/codegen/extension.go b/pkg/codegen/extension.go
index 28294b1..e986b3f 100644
--- a/pkg/codegen/extension.go
+++ b/pkg/codegen/extension.go
@@ -7,7 +7,7 @@ import (
 
 const (
 	extPropGoType    = "x-go-type"
-	extGoFieldName   = "x-go-name"
+	extGoName        = "x-go-name"
 	extPropOmitEmpty = "x-omitempty"
 	extPropExtraTags = "x-oapi-codegen-extra-tags"
 )
diff --git a/pkg/codegen/filter.go b/pkg/codegen/filter.go
index bf65b59..56c9730 100644
--- a/pkg/codegen/filter.go
+++ b/pkg/codegen/filter.go
@@ -2,12 +2,12 @@ package codegen
 
 import "github.com/getkin/kin-openapi/openapi3"
 
-func filterOperationsByTag(swagger *openapi3.T, opts Options) {
-	if len(opts.ExcludeTags) > 0 {
-		excludeOperationsWithTags(swagger.Paths, opts.ExcludeTags)
+func filterOperationsByTag(swagger *openapi3.T, opts Configuration) {
+	if len(opts.OutputOptions.ExcludeTags) > 0 {
+		excludeOperationsWithTags(swagger.Paths, opts.OutputOptions.ExcludeTags)
 	}
-	if len(opts.IncludeTags) > 0 {
-		includeOperationsWithTags(swagger.Paths, opts.IncludeTags, false)
+	if len(opts.OutputOptions.IncludeTags) > 0 {
+		includeOperationsWithTags(swagger.Paths, opts.OutputOptions.IncludeTags, false)
 	}
 }
 
diff --git a/pkg/codegen/merge_schemas.go b/pkg/codegen/merge_schemas.go
new file mode 100644
index 0000000..1ac358d
--- /dev/null
+++ b/pkg/codegen/merge_schemas.go
@@ -0,0 +1,195 @@
+package codegen
+
+import (
+	"errors"
+	"fmt"
+
+	"github.com/getkin/kin-openapi/openapi3"
+)
+
+// MergeSchemas merges all the fields in the schemas supplied into one giant schema.
+// The idea is that we merge all fields together into one schema.
+func MergeSchemas(allOf []*openapi3.SchemaRef, path []string) (Schema, error) {
+	// If someone asked for the old way, for backward compatibility, return the
+	// old style result.
+	if globalState.options.Compatibility.OldMergeSchemas {
+		return mergeSchemas_V1(allOf, path)
+	}
+	return mergeSchemas(allOf, path)
+}
+
+func mergeSchemas(allOf []*openapi3.SchemaRef, path []string) (Schema, error) {
+	n := len(allOf)
+
+	if n == 1 {
+		return GenerateGoSchema(allOf[0], path)
+	}
+
+	schema := *allOf[0].Value
+
+	for i := 1; i < n; i++ {
+		var err error
+		schema, err = mergeOpenapiSchemas(schema, *allOf[i].Value)
+		if err != nil {
+			return Schema{}, fmt.Errorf("error merging schemas for AllOf: %w", err)
+		}
+	}
+	return GenerateGoSchema(openapi3.NewSchemaRef("", &schema), path)
+}
+
+func mergeAllOf(allOf []*openapi3.SchemaRef) (openapi3.Schema, error) {
+	var schema openapi3.Schema
+	for _, schemaRef := range allOf {
+		var err error
+		schema, err = mergeOpenapiSchemas(schema, *schemaRef.Value)
+		if err != nil {
+			return openapi3.Schema{}, fmt.Errorf("error merging schemas for AllOf: %w", err)
+		}
+	}
+	return schema, nil
+}
+
+// mergeOpenapiSchemas merges two openAPI schemas and returns the schema
+// all of whose fields are composed.
+func mergeOpenapiSchemas(s1, s2 openapi3.Schema) (openapi3.Schema, error) {
+	var result openapi3.Schema
+	if s1.Extensions != nil || s2.Extensions != nil {
+		result.Extensions = make(map[string]interface{})
+		if s1.Extensions != nil {
+			for k, v := range s1.Extensions {
+				result.Extensions[k] = v
+			}
+		}
+		if s2.Extensions != nil {
+			for k, v := range s2.Extensions {
+				// TODO: Check for collisions
+				result.Extensions[k] = v
+			}
+		}
+	}
+
+	result.OneOf = append(s1.OneOf, s2.OneOf...)
+
+	// We are going to make AllOf transitive, so that merging an AllOf that
+	// contains AllOf's will result in a flat object.
+	var err error
+	if s1.AllOf != nil {
+		var merged openapi3.Schema
+		merged, err = mergeAllOf(s1.AllOf)
+		if err != nil {
+			return openapi3.Schema{}, fmt.Errorf("error transitive merging AllOf on schema 1")
+		}
+		s1 = merged
+	}
+	if s2.AllOf != nil {
+		var merged openapi3.Schema
+		merged, err = mergeAllOf(s2.AllOf)
+		if err != nil {
+			return openapi3.Schema{}, fmt.Errorf("error transitive merging AllOf on schema 2")
+		}
+		s2 = merged
+	}
+
+	result.AllOf = append(s1.AllOf, s2.AllOf...)
+
+	if s1.Type != "" && s2.Type != "" && s1.Type != s2.Type {
+		return openapi3.Schema{}, errors.New("can not merge incompatible types")
+	}
+	result.Type = s1.Type
+
+	if s1.Format != s2.Format {
+		return openapi3.Schema{}, errors.New("can not merge incompatible formats")
+	}
+	result.Format = s1.Format
+
+	// For Enums, do we union, or intersect? This is a bit vague. I choose
+	// to be more permissive and union.
+	result.Enum = append(s1.Enum, s2.Enum...)
+
+	// I don't know how to handle two different defaults.
+	if s1.Default != nil || s2.Default != nil {
+		return openapi3.Schema{}, errors.New("merging two sets of defaults is undefined")
+	}
+	if s1.Default != nil {
+		result.Default = s1.Default
+	}
+	if s2.Default != nil {
+		result.Default = s2.Default
+	}
+
+	// We skip Example
+	// We skip ExternalDocs
+
+	// If two schemas disagree on any of these flags, we error out.
+	if s1.UniqueItems != s2.UniqueItems {
+		return openapi3.Schema{}, errors.New("merging two schemas with different UniqueItems")
+
+	}
+	result.UniqueItems = s1.UniqueItems
+
+	if s1.ExclusiveMin != s2.ExclusiveMin {
+		return openapi3.Schema{}, errors.New("merging two schemas with different ExclusiveMin")
+
+	}
+	result.ExclusiveMin = s1.ExclusiveMin
+
+	if s1.ExclusiveMax != s2.ExclusiveMax {
+		return openapi3.Schema{}, errors.New("merging two schemas with different ExclusiveMax")
+
+	}
+	result.ExclusiveMax = s1.ExclusiveMax
+
+	if s1.Nullable != s2.Nullable {
+		return openapi3.Schema{}, errors.New("merging two schemas with different Nullable")
+
+	}
+	result.Nullable = s1.Nullable
+
+	if s1.ReadOnly != s2.ReadOnly {
+		return openapi3.Schema{}, errors.New("merging two schemas with different ReadOnly")
+
+	}
+	result.ReadOnly = s1.ReadOnly
+
+	if s1.WriteOnly != s2.WriteOnly {
+		return openapi3.Schema{}, errors.New("merging two schemas with different WriteOnly")
+
+	}
+	result.WriteOnly = s1.WriteOnly
+
+	if s1.AllowEmptyValue != s2.AllowEmptyValue {
+		return openapi3.Schema{}, errors.New("merging two schemas with different AllowEmptyValue")
+
+	}
+	result.AllowEmptyValue = s1.AllowEmptyValue
+
+	// Required. We merge these.
+	result.Required = append(s1.Required, s2.Required...)
+
+	// We merge all properties
+	result.Properties = make(map[string]*openapi3.SchemaRef)
+	for k, v := range s1.Properties {
+		result.Properties[k] = v
+	}
+	for k, v := range s2.Properties {
+		// TODO: detect conflicts
+		result.Properties[k] = v
+	}
+
+	if SchemaHasAdditionalProperties(&s1) && SchemaHasAdditionalProperties(&s2) {
+		return openapi3.Schema{}, errors.New("merging two schemas with additional properties, this is unhandled")
+	}
+	if s1.AdditionalProperties != nil {
+		result.AdditionalProperties = s1.AdditionalProperties
+	}
+	if s2.AdditionalProperties != nil {
+		result.AdditionalProperties = s2.AdditionalProperties
+	}
+
+	// Unhandled for now
+	if s1.Discriminator != nil || s2.Discriminator != nil {
+		return openapi3.Schema{}, errors.New("merging two schemas with discriminators is not supported")
+	}
+
+	return result, nil
+}
diff --git a/pkg/codegen/merge_schemas_v1.go b/pkg/codegen/merge_schemas_v1.go
new file mode 100644
index 0000000..ae2f125
--- /dev/null
+++ b/pkg/codegen/merge_schemas_v1.go
@@ -0,0 +1,111 @@
+package codegen
+
+import (
+	"errors"
+	"fmt"
+	"strings"
+
+	"github.com/getkin/kin-openapi/openapi3"
+)
+
+func mergeSchemas_V1(allOf []*openapi3.SchemaRef, path []string) (Schema, error) {
+	var outSchema Schema
+	for _, schemaOrRef := range allOf {
+		ref := schemaOrRef.Ref
+
+		var refType string
+		var err error
+		if IsGoTypeReference(ref) {
+			refType, err = RefPathToGoType(ref)
+			if err != nil {
+				return Schema{}, fmt.Errorf("error converting reference path to a go type: %w", err)
+			}
+		}
+
+		schema, err := GenerateGoSchema(schemaOrRef, path)
+		if err != nil {
+			return Schema{}, fmt.Errorf("error generating Go schema in allOf: %w", err)
+		}
+		schema.RefType = refType
+
+		for _, p := range schema.Properties {
+			err = outSchema.AddProperty(p)
+			if err != nil {
+				return Schema{}, fmt.Errorf("error merging properties: %w", err)
+			}
+		}
+
+		if schema.HasAdditionalProperties {
+			if outSchema.HasAdditionalProperties {
+				// Both this schema, and the aggregate schema have additional
+				// properties, they must match.
+				if schema.AdditionalPropertiesType.TypeDecl() != outSchema.AdditionalPropertiesType.TypeDecl() {
+					return Schema{}, errors.New("additional properties in allOf have incompatible types")
+				}
+			} else {
+				// We're switching from having no additional properties to having
+				// them
+				outSchema.HasAdditionalProperties = true
+				outSchema.AdditionalPropertiesType = schema.AdditionalPropertiesType
+			}
+		}
+	}
+
+	// Now, we generate the struct which merges together all the fields.
+	var err error
+	outSchema.GoType, err = GenStructFromAllOf(allOf, path)
+	if err != nil {
+		return Schema{}, fmt.Errorf("unable to generate aggregate type for AllOf: %w", err)
+	}
+	return outSchema, nil
+}
+
+// This function generates an object that is the union of the objects in the
+// input array. In the case of Ref objects, we use an embedded struct, otherwise,
+// we inline the fields.
+func GenStructFromAllOf(allOf []*openapi3.SchemaRef, path []string) (string, error) {
+	// Start out with struct {
+	objectParts := []string{"struct {"}
+	for _, schemaOrRef := range allOf {
+		ref := schemaOrRef.Ref
+		if IsGoTypeReference(ref) {
+			// We have a referenced type, we will generate an inlined struct
+			// member.
+			// struct {
+			//   InlinedMember
+			//   ...
+			// }
+			goType, err := RefPathToGoType(ref)
+			if err != nil {
+				return "", err
+			}
+			objectParts = append(objectParts,
+				fmt.Sprintf("   // Embedded struct due to allOf(%s)", ref))
+			objectParts = append(objectParts,
+				fmt.Sprintf("   %s `yaml:\",inline\"`", goType))
+		} else {
+			// Inline all the fields from the schema into the output struct,
+			// just like in the simple case of generating an object.
+			goSchema, err := GenerateGoSchema(schemaOrRef, path)
+			if err != nil {
+				return "", err
+			}
+			objectParts = append(objectParts, "   // Embedded fields due to inline allOf schema")
+			objectParts = append(objectParts, GenFieldsFromProperties(goSchema.Properties)...)
+
+			if goSchema.HasAdditionalProperties {
+				addPropsType := goSchema.AdditionalPropertiesType.GoType
+				if goSchema.AdditionalPropertiesType.RefType != "" {
+					addPropsType = goSchema.AdditionalPropertiesType.RefType
+				}
+
+				additionalPropertiesPart := fmt.Sprintf("AdditionalProperties map[string]%s `json:\"-\"`", addPropsType)
+				if !StringInArray(additionalPropertiesPart, objectParts) {
+					objectParts = append(objectParts, additionalPropertiesPart)
+				}
+			}
+		}
+	}
+	objectParts = append(objectParts, "}")
+	return strings.Join(objectParts, "\n"), nil
+}
diff --git a/pkg/codegen/operations.go b/pkg/codegen/operations.go
index 59b03ff..3196d5c 100644
--- a/pkg/codegen/operations.go
+++ b/pkg/codegen/operations.go
@@ -119,7 +119,13 @@ func (pd ParameterDefinition) GoVariableName() string {
 }
 
 func (pd ParameterDefinition) GoName() string {
-	return SchemaNameToTypeName(pd.ParamName)
+	goName := pd.ParamName
+	if _, ok := pd.Spec.ExtensionProps.Extensions[extGoName]; ok {
+		if extGoFieldName, err := extParseGoFieldName(pd.Spec.ExtensionProps.Extensions[extGoName]); err == nil {
+			goName = extGoFieldName
+		}
+	}
+	return SchemaNameToTypeName(goName)
 }
 
 func (pd ParameterDefinition) IndirectOptional() bool {
@@ -575,8 +581,8 @@ func GenerateTypeDefsForOperation(op OperationDefinition) []TypeDefinition {
 	return typeDefs
 }
 
-// This defines the schema for a parameters definition object which encapsulates
-// all the query, header and cookie parameters for an operation.
+// GenerateParamsTypes defines the schema for a parameters definition object
+// which encapsulates all the query, header and cookie parameters for an operation.
 func GenerateParamsTypes(op OperationDefinition) []TypeDefinition {
 	var typeDefs []TypeDefinition
 
@@ -589,6 +595,7 @@ func GenerateParamsTypes(op OperationDefinition) []TypeDefinition {
 	s := Schema{}
 	for _, param := range objectParams {
 		pSchema := param.Schema
+		param.Style()
 		if pSchema.HasAdditionalProperties {
 			propRefName := strings.Join([]string{typeName, param.GoName()}, "_")
 			pSchema.RefType = propRefName
@@ -602,6 +609,7 @@ func GenerateParamsTypes(op OperationDefinition) []TypeDefinition {
 			JsonFieldName:  param.ParamName,
 			Required:       param.Required,
 			Schema:         pSchema,
+			NeedsFormTag:   param.Style() == "form",
 			ExtensionProps: &param.Spec.ExtensionProps,
 		}
 		s.Properties = append(s.Properties, prop)
diff --git a/pkg/codegen/schema.go b/pkg/codegen/schema.go
index ca3ed46..2b44397 100644
--- a/pkg/codegen/schema.go
+++ b/pkg/codegen/schema.go
@@ -8,7 +8,8 @@ import (
 	"github.com/getkin/kin-openapi/openapi3"
 )
 
-// This describes a Schema, a type definition.
+// Schema describes an OpenAPI schema, with lots of helper fields to use in the
+// templating engine.
 type Schema struct {
 	GoType  string // The Go type needed to represent the schema
 	RefType string // If the type has a type name, this is set
@@ -26,6 +27,11 @@ type Schema struct {
 
 	Description string // The description of the element
 
+	// 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`
+	DefineViaAlias bool
+
 	// The original OpenAPIv3 Schema.
 	OAPISchema *openapi3.Schema
 }
@@ -41,7 +47,12 @@ func (s Schema) TypeDecl() string {
 	return s.GoType
 }
 
-func (s *Schema) MergeProperty(p Property) error {
+// AddProperty adds a new property to the current Schema, and returns an error
+// if it collides. Two identical fields will not collide, but two properties by
+// the same name, but different definition, will collide. It's safe to merge the
+// fields of two schemas with overalapping properties if those properties are
+// identical.
+func (s *Schema) AddProperty(p Property) error {
 	// Scan all existing properties for a conflict
 	for _, e := range s.Properties {
 		if e.JsonFieldName == p.JsonFieldName && !PropertiesEqual(e, p) {
@@ -69,6 +80,7 @@ type Property struct {
 	Nullable       bool
 	ReadOnly       bool
 	WriteOnly      bool
+	NeedsFormTag   bool
 	ExtensionProps *openapi3.ExtensionProps
 }
 
@@ -88,9 +100,32 @@ func (p Property) GoTypeDef() string {
 
 // EnumDefinition holds type information for enum
 type EnumDefinition struct {
-	Schema       Schema
-	TypeName     string
+	// Schema is the scheme of a type which has a list of enum values, eg, the
+	// "container" of the enum.
+	Schema Schema
+	// TypeName is the name of the enum's type, usually aliased from something.
+	TypeName string
+	// ValueWrapper wraps the value. It's used to conditionally apply quotes
+	// around strings.
 	ValueWrapper string
+	// Conflicts is set to true when this enum conflicts with another in
+	// terms of TypeNames
+	Conflicts bool
+}
+
+// GetValues generates enum names in a way to minimize global conflicts
+func (e *EnumDefinition) GetValues() map[string]string {
+	// in case there are no conflicts, it's safe to use the values as-is
+	if !e.Conflicts {
+		return e.Schema.EnumValues
+	}
+	// If we do have conflicts, we will prefix the enum's typename to the values.
+	newValues := make(map[string]string, len(e.Schema.EnumValues))
+	for k, v := range e.Schema.EnumValues {
+		newName := e.TypeName + UppercaseFirstCharacter(k)
+		newValues[newName] = v
+	}
+	return newValues
 }
 
 type Constants struct {
@@ -133,9 +168,8 @@ type ResponseTypeDefinition struct {
 	ResponseName string
 }
 
-func (t *TypeDefinition) CanAlias() bool {
-	return t.Schema.IsRef() || /* actual reference */
-		(t.Schema.ArrayType != nil && t.Schema.ArrayType.IsRef()) /* array to ref */
+func (t *TypeDefinition) IsAlias() bool {
+	return !globalState.options.Compatibility.OldAliasing && t.Schema.DefineViaAlias
 }
 
 func PropertiesEqual(a, b Property) bool {
@@ -162,8 +196,9 @@ func GenerateGoSchema(sref *openapi3.SchemaRef, path []string) (Schema, error) {
 				sref.Ref, err)
 		}
 		return Schema{
-			GoType:      refType,
-			Description: StringToGoComment(schema.Description),
+			GoType:         refType,
+			Description:    StringToGoComment(schema.Description),
+			DefineViaAlias: true,
 		}, nil
 	}
 
@@ -225,7 +260,11 @@ func GenerateGoSchema(sref *openapi3.SchemaRef, path []string) (Schema, error) {
 				outType = "interface{}"
 			}
 			outSchema.GoType = outType
+			outSchema.DefineViaAlias = true
 		} else {
+			// When we define an object, we want it to be a type definition,
+			// not a type alias, eg, "type Foo struct {...}"
+			outSchema.DefineViaAlias = false
 			// We've got an object with some properties.
 			for _, pName := range SortedSchemaKeys(schema.Properties) {
 				p := schema.Properties[pName]
@@ -286,7 +325,12 @@ func GenerateGoSchema(sref *openapi3.SchemaRef, path []string) (Schema, error) {
 		}
 		return outSchema, nil
 	} else if len(schema.Enum) > 0 {
-		err := resolveType(schema, path, &outSchema)
+		err := oapiSchemaToGoType(schema, path, &outSchema)
+		// Enums need to be typed, so that the values aren't interchangeable,
+		// so no matter what schema conversion thinks, we need to define a
+		// new type.
+		outSchema.DefineViaAlias = false
+
 		if err != nil {
 			return Schema{}, fmt.Errorf("error resolving primitive type: %w", err)
 		}
@@ -297,14 +341,19 @@ func GenerateGoSchema(sref *openapi3.SchemaRef, path []string) (Schema, error) {
 
 		sanitizedValues := SanitizeEnumNames(enumValues)
 		outSchema.EnumValues = make(map[string]string, len(sanitizedValues))
-		var constNamePath []string
+
 		for k, v := range sanitizedValues {
+			var enumName string
 			if v == "" {
-				constNamePath = append(path, "Empty")
+				enumName = "Empty"
 			} else {
-				constNamePath = append(path, k)
+				enumName = k
+			}
+			if globalState.options.Compatibility.OldEnumConflicts {
+				outSchema.EnumValues[SchemaNameToTypeName(PathToTypeName(append(path, enumName)))] = v
+			} else {
+				outSchema.EnumValues[SchemaNameToTypeName(k)] = v
 			}
-			outSchema.EnumValues[SchemaNameToTypeName(PathToTypeName(constNamePath))] = v
 		}
 		if len(path) > 1 { // handle additional type only on non-toplevel types
 			typeName := SchemaNameToTypeName(PathToTypeName(path))
@@ -318,7 +367,7 @@ func GenerateGoSchema(sref *openapi3.SchemaRef, path []string) (Schema, error) {
 		}
 		//outSchema.RefType = typeName
 	} else {
-		err := resolveType(schema, path, &outSchema)
+		err := oapiSchemaToGoType(schema, path, &outSchema)
 		if err != nil {
 			return Schema{}, fmt.Errorf("error resolving primitive type: %w", err)
 		}
@@ -326,8 +375,9 @@ func GenerateGoSchema(sref *openapi3.SchemaRef, path []string) (Schema, error) {
 	return outSchema, nil
 }
 
-// resolveType resolves primitive  type or array for schema
-func resolveType(schema *openapi3.Schema, path []string, outSchema *Schema) error {
+// oapiSchemaToGoType converts an OpenApi schema into a Go type definition for
+// all non-object types.
+func oapiSchemaToGoType(schema *openapi3.Schema, path []string, outSchema *Schema) error {
 	f := schema.Format
 	t := schema.Type
 
@@ -343,6 +393,7 @@ func resolveType(schema *openapi3.Schema, path []string, outSchema *Schema) erro
 		outSchema.GoType = "[]" + arrayType.TypeDecl()
 		outSchema.AdditionalTypes = arrayType.AdditionalTypes
 		outSchema.Properties = arrayType.Properties
+		outSchema.DefineViaAlias = true
 	case "integer":
 		// We default to int if format doesn't ask for something else.
 		if f == "int64" {
@@ -370,6 +421,7 @@ func resolveType(schema *openapi3.Schema, path []string, outSchema *Schema) erro
 		} else {
 			return fmt.Errorf("invalid integer format: %s", f)
 		}
+		outSchema.DefineViaAlias = true
 	case "number":
 		// We default to float for "number"
 		if f == "double" {
@@ -379,11 +431,13 @@ func resolveType(schema *openapi3.Schema, path []string, outSchema *Schema) erro
 		} else {
 			return fmt.Errorf("invalid number format: %s", f)
 		}
+		outSchema.DefineViaAlias = true
 	case "boolean":
 		if f != "" {
 			return fmt.Errorf("invalid format (%s) for boolean", f)
 		}
 		outSchema.GoType = "bool"
+		outSchema.DefineViaAlias = true
 	case "string":
 		// Special case string formats here.
 		switch f {
@@ -404,13 +458,14 @@ func resolveType(schema *openapi3.Schema, path []string, outSchema *Schema) erro
 			// All unrecognized formats are simply a regular string.
 			outSchema.GoType = "string"
 		}
+		outSchema.DefineViaAlias = true
 	default:
 		return fmt.Errorf("unhandled Schema type: %s", t)
 	}
 	return nil
 }
 
-// This describes a Schema, a type definition.
+// SchemaDescriptor describes a Schema, a type definition.
 type SchemaDescriptor struct {
 	Fields                   []FieldDescriptor
 	HasAdditionalProperties  bool
@@ -442,8 +497,8 @@ func GenFieldsFromProperties(props []Property) []string {
 		}
 
 		goFieldName := p.GoFieldName()
-		if _, ok := p.ExtensionProps.Extensions[extGoFieldName]; ok {
-			if extGoFieldName, err := extParseGoFieldName(p.ExtensionProps.Extensions[extGoFieldName]); err == nil {
+		if _, ok := p.ExtensionProps.Extensions[extGoName]; ok {
+			if extGoFieldName, err := extParseGoFieldName(p.ExtensionProps.Extensions[extGoName]); err == nil {
 				goFieldName = extGoFieldName
 			}
 		}
@@ -451,20 +506,27 @@ func GenFieldsFromProperties(props []Property) []string {
 		field += fmt.Sprintf("    %s %s", goFieldName, p.GoTypeDef())
 
 		// Support x-omitempty
-		omitEmpty := true
+		overrideOmitEmpty := true
 		if _, ok := p.ExtensionProps.Extensions[extPropOmitEmpty]; ok {
 			if extOmitEmpty, err := extParseOmitEmpty(p.ExtensionProps.Extensions[extPropOmitEmpty]); err == nil {
-				omitEmpty = extOmitEmpty
+				overrideOmitEmpty = extOmitEmpty
 			}
 		}
 
 		fieldTags := make(map[string]string)
 
-		if (p.Required && !p.ReadOnly && !p.WriteOnly) || p.Nullable || !omitEmpty {
+		if (p.Required && !p.ReadOnly && !p.WriteOnly) || p.Nullable || !overrideOmitEmpty {
 			fieldTags["json"] = p.JsonFieldName
+			if p.NeedsFormTag {
+				fieldTags["form"] = p.JsonFieldName
+			}
 		} else {
 			fieldTags["json"] = p.JsonFieldName + ",omitempty"
+			if p.NeedsFormTag {
+				fieldTags["form"] = p.JsonFieldName + ",omitempty"
+			}
 		}
+
 		if extension, ok := p.ExtensionProps.Extensions[extPropExtraTags]; ok {
 			if tags, err := extExtraTags(extension); err == nil {
 				keys := SortedStringKeys(tags)
@@ -504,109 +566,6 @@ func GenStructFromSchema(schema Schema) string {
 	return strings.Join(objectParts, "\n")
 }
 
-// Merge all the fields in the schemas supplied into one giant schema.
-func MergeSchemas(allOf []*openapi3.SchemaRef, path []string) (Schema, error) {
-	var outSchema Schema
-	for _, schemaOrRef := range allOf {
-		ref := schemaOrRef.Ref
-
-		var refType string
-		var err error
-		if IsGoTypeReference(ref) {
-			refType, err = RefPathToGoType(ref)
-			if err != nil {
-				return Schema{}, fmt.Errorf("error converting reference path to a go type: %w", err)
-			}
-		}
-
-		schema, err := GenerateGoSchema(schemaOrRef, path)
-		if err != nil {
-			return Schema{}, fmt.Errorf("error generating Go schema in allOf: %w", err)
-		}
-		schema.RefType = refType
-
-		for _, p := range schema.Properties {
-			err = outSchema.MergeProperty(p)
-			if err != nil {
-				return Schema{}, fmt.Errorf("error merging properties: %w", err)
-			}
-		}
-
-		if schema.HasAdditionalProperties {
-			if outSchema.HasAdditionalProperties {
-				// Both this schema, and the aggregate schema have additional
-				// properties, they must match.
-				if schema.AdditionalPropertiesType.TypeDecl() != outSchema.AdditionalPropertiesType.TypeDecl() {
-					return Schema{}, errors.New("additional properties in allOf have incompatible types")
-				}
-			} else {
-				// We're switching from having no additional properties to having
-				// them
-				outSchema.HasAdditionalProperties = true
-				outSchema.AdditionalPropertiesType = schema.AdditionalPropertiesType
-			}
-		}
-	}
-
-	// Now, we generate the struct which merges together all the fields.
-	var err error
-	outSchema.GoType, err = GenStructFromAllOf(allOf, path)
-	if err != nil {
-		return Schema{}, fmt.Errorf("unable to generate aggregate type for AllOf: %w", err)
-	}
-	return outSchema, nil
-}
-
-// This function generates an object that is the union of the objects in the
-// input array. In the case of Ref objects, we use an embedded struct, otherwise,
-// we inline the fields.
-func GenStructFromAllOf(allOf []*openapi3.SchemaRef, path []string) (string, error) {
-	// Start out with struct {
-	objectParts := []string{"struct {"}
-	for _, schemaOrRef := range allOf {
-		ref := schemaOrRef.Ref
-		if IsGoTypeReference(ref) {
-			// We have a referenced type, we will generate an inlined struct
-			// member.
-			// struct {
-			//   InlinedMember
-			//   ...
-			// }
-			goType, err := RefPathToGoType(ref)
-			if err != nil {
-				return "", err
-			}
-			objectParts = append(objectParts,
-				fmt.Sprintf("   // Embedded struct due to allOf(%s)", ref))
-			objectParts = append(objectParts,
-				fmt.Sprintf("   %s `yaml:\",inline\"`", goType))
-		} else {
-			// Inline all the fields from the schema into the output struct,
-			// just like in the simple case of generating an object.
-			goSchema, err := GenerateGoSchema(schemaOrRef, path)
-			if err != nil {
-				return "", err
-			}
-			objectParts = append(objectParts, "   // Embedded fields due to inline allOf schema")
-			objectParts = append(objectParts, GenFieldsFromProperties(goSchema.Properties)...)
-
-			if goSchema.HasAdditionalProperties {
-				addPropsType := goSchema.AdditionalPropertiesType.GoType
-				if goSchema.AdditionalPropertiesType.RefType != "" {
-					addPropsType = goSchema.AdditionalPropertiesType.RefType
-				}
-
-				additionalPropertiesPart := fmt.Sprintf("AdditionalProperties map[string]%s `json:\"-\"`", addPropsType)
-				if !StringInArray(additionalPropertiesPart, objectParts) {
-					objectParts = append(objectParts, additionalPropertiesPart)
-				}
-			}
-		}
-	}
-	objectParts = append(objectParts, "}")
-	return strings.Join(objectParts, "\n"), nil
-}
-
 // This constructs a Go type for a parameter, looking at either the schema or
 // the content, whichever is available
 func paramToGoType(param *openapi3.Parameter, path []string) (Schema, error) {
diff --git a/pkg/codegen/template_helpers.go b/pkg/codegen/template_helpers.go
index 87724c5..eeb7810 100644
--- a/pkg/codegen/template_helpers.go
+++ b/pkg/codegen/template_helpers.go
@@ -26,13 +26,14 @@ import (
 const (
 	// These allow the case statements to be sorted later:
 	prefixMostSpecific, prefixLessSpecific, prefixLeastSpecific = "3", "6", "9"
-	responseTypeSuffix                                          = "Response"
 )
 
 var (
 	contentTypesJSON = []string{echo.MIMEApplicationJSON, "text/x-json"}
 	contentTypesYAML = []string{"application/yaml", "application/x-yaml", "text/yaml", "text/x-yaml"}
 	contentTypesXML  = []string{echo.MIMEApplicationXML, echo.MIMETextXML}
+
+	responseTypeSuffix = "Response"
 )
 
 // This function takes an array of Parameter definition, and generates a valid
@@ -149,9 +150,7 @@ func genResponseUnmarshal(op *OperationDefinition) string {
 			// JSON:
 			case StringInArray(contentTypeName, contentTypesJSON):
 				if typeDefinition.ContentTypeName == contentTypeName {
-					var caseAction string
-
-					caseAction = fmt.Sprintf("var dest %s\n"+
+					caseAction := fmt.Sprintf("var dest %s\n"+
 						"if err := json.Unmarshal(bodyBytes, &dest); err != nil { \n"+
 						" return nil, err \n"+
 						"}\n"+
@@ -166,8 +165,7 @@ func genResponseUnmarshal(op *OperationDefinition) string {
 			// YAML:
 			case StringInArray(contentTypeName, contentTypesYAML):
 				if typeDefinition.ContentTypeName == contentTypeName {
-					var caseAction string
-					caseAction = fmt.Sprintf("var dest %s\n"+
+					caseAction := fmt.Sprintf("var dest %s\n"+
 						"if err := yaml.Unmarshal(bodyBytes, &dest); err != nil { \n"+
 						" return nil, err \n"+
 						"}\n"+
@@ -181,8 +179,7 @@ func genResponseUnmarshal(op *OperationDefinition) string {
 			// XML:
 			case StringInArray(contentTypeName, contentTypesXML):
 				if typeDefinition.ContentTypeName == contentTypeName {
-					var caseAction string
-					caseAction = fmt.Sprintf("var dest %s\n"+
+					caseAction := fmt.Sprintf("var dest %s\n"+
 						"if err := xml.Unmarshal(bodyBytes, &dest); err != nil { \n"+
 						" return nil, err \n"+
 						"}\n"+
diff --git a/pkg/codegen/templates/chi/chi-middleware.tmpl b/pkg/codegen/templates/chi/chi-middleware.tmpl
index 53a44d3..7c1385b 100644
--- a/pkg/codegen/templates/chi/chi-middleware.tmpl
+++ b/pkg/codegen/templates/chi/chi-middleware.tmpl
@@ -30,7 +30,7 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(w http.ResponseWriter, r *http.Requ
   }
   {{end}}
   {{if .IsStyled}}
-  err = runtime.BindStyledParameter("{{.Style}}",{{.Explode}}, "{{.ParamName}}", chi.URLParam(r, "{{.ParamName}}"), &{{$varName}})
+  err = runtime.BindStyledParameterWithLocation("{{.Style}}",{{.Explode}}, "{{.ParamName}}", runtime.ParamLocationPath, chi.URLParam(r, "{{.ParamName}}"), &{{$varName}})
   if err != nil {
     siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "{{.ParamName}}", Err: err})
     return
diff --git a/pkg/codegen/templates/client-with-responses.tmpl b/pkg/codegen/templates/client-with-responses.tmpl
index 19722ec..0d49887 100644
--- a/pkg/codegen/templates/client-with-responses.tmpl
+++ b/pkg/codegen/templates/client-with-responses.tmpl
@@ -40,7 +40,7 @@ type ClientWithResponsesInterface interface {
 }
 
 {{range .}}{{$opid := .OperationId}}{{$op := .}}
-type {{$opid | ucFirst}}Response struct {
+type {{genResponseTypeName $opid | ucFirst}} struct {
     Body         []byte
 	HTTPResponse *http.Response
     {{- range getResponseTypeDefinitions .}}
@@ -49,7 +49,7 @@ type {{$opid | ucFirst}}Response struct {
 }
 
 // Status returns HTTPResponse.Status
-func (r {{$opid | ucFirst}}Response) Status() string {
+func (r {{genResponseTypeName $opid | ucFirst}}) Status() string {
     if r.HTTPResponse != nil {
         return r.HTTPResponse.Status
     }
@@ -57,7 +57,7 @@ func (r {{$opid | ucFirst}}Response) Status() string {
 }
 
 // StatusCode returns HTTPResponse.StatusCode
-func (r {{$opid | ucFirst}}Response) StatusCode() int {
+func (r {{genResponseTypeName $opid | ucFirst}}) StatusCode() int {
     if r.HTTPResponse != nil {
         return r.HTTPResponse.StatusCode
     }
@@ -70,7 +70,7 @@ func (r {{$opid | ucFirst}}Response) StatusCode() int {
 {{$opid := .OperationId -}}
 {{/* Generate client methods (with responses)*/}}
 
-// {{$opid}}{{if .HasBody}}WithBody{{end}}WithResponse request{{if .HasBody}} with arbitrary body{{end}} returning *{{$opid}}Response
+// {{$opid}}{{if .HasBody}}WithBody{{end}}WithResponse request{{if .HasBody}} with arbitrary body{{end}} returning *{{genResponseTypeName $opid}}
 func (c *ClientWithResponses) {{$opid}}{{if .HasBody}}WithBody{{end}}WithResponse(ctx context.Context{{genParamArgs .PathParams}}{{if .RequiresParamObject}}, params *{{$opid}}Params{{end}}{{if .HasBody}}, contentType string, body io.Reader{{end}}, reqEditors... RequestEditorFn) (*{{genResponseTypeName $opid}}, error){
     rsp, err := c.{{$opid}}{{if .HasBody}}WithBody{{end}}(ctx{{genParamNames .PathParams}}{{if .RequiresParamObject}}, params{{end}}{{if .HasBody}}, contentType, body{{end}}, reqEditors...)
     if err != nil {
diff --git a/pkg/codegen/templates/constants.tmpl b/pkg/codegen/templates/constants.tmpl
index 314d817..a3a0859 100644
--- a/pkg/codegen/templates/constants.tmpl
+++ b/pkg/codegen/templates/constants.tmpl
@@ -5,13 +5,11 @@ const (
 {{end}}
 )
 {{end}}
-{{if gt (len .EnumDefinitions) 0 }}
 {{range $Enum := .EnumDefinitions}}
 // Defines values for {{$Enum.TypeName}}.
 const (
-{{range $index, $value := $Enum.Schema.EnumValues}}
-  {{$index}} {{$Enum.TypeName}} = {{$Enum.ValueWrapper}}{{$value}}{{$Enum.ValueWrapper}}
+{{range $name, $value := $Enum.GetValues}}
+  {{$name}} {{$Enum.TypeName}} = {{$Enum.ValueWrapper}}{{$value}}{{$Enum.ValueWrapper -}}
 {{end}}
 )
 {{end}}
-{{end}}
diff --git a/pkg/codegen/templates/param-types.tmpl b/pkg/codegen/templates/param-types.tmpl
index f49ba30..bb107b8 100644
--- a/pkg/codegen/templates/param-types.tmpl
+++ b/pkg/codegen/templates/param-types.tmpl
@@ -1,6 +1,6 @@
 {{range .}}{{$opid := .OperationId}}
 {{range .TypeDefinitions}}
 // {{.TypeName}} defines parameters for {{$opid}}.
-type {{.TypeName}} {{if and (opts.AliasTypes) (.CanAlias)}}={{end}} {{.Schema.TypeDecl}}
+type {{.TypeName}} {{if .IsAlias}}={{end}} {{.Schema.TypeDecl}}
 {{end}}
 {{end}}
diff --git a/pkg/codegen/templates/request-bodies.tmpl b/pkg/codegen/templates/request-bodies.tmpl
index 313dd11..d970ce7 100644
--- a/pkg/codegen/templates/request-bodies.tmpl
+++ b/pkg/codegen/templates/request-bodies.tmpl
@@ -2,7 +2,7 @@
 {{range .Bodies}}
 {{with .TypeDef $opid}}
 // {{.TypeName}} defines body for {{$opid}} for application/json ContentType.
-type {{.TypeName}} {{if and (opts.AliasTypes) (.CanAlias)}}={{end}} {{.Schema.TypeDecl}}
+type {{.TypeName}} {{if .IsAlias}}={{end}} {{.Schema.TypeDecl}}
 {{end}}
 {{end}}
 {{end}}
diff --git a/pkg/codegen/templates/typedef.tmpl b/pkg/codegen/templates/typedef.tmpl
index 0b153b7..e5a6a82 100644
--- a/pkg/codegen/templates/typedef.tmpl
+++ b/pkg/codegen/templates/typedef.tmpl
@@ -1,4 +1,4 @@
 {{range .Types}}
 {{ with .Schema.Description }}{{ . }}{{ else }}// {{.TypeName}} defines model for {{.JsonName}}.{{ end }}
-type {{.TypeName}} {{if and (opts.AliasTypes) (.CanAlias)}}={{end}} {{.Schema.TypeDecl}}
+type {{.TypeName}} {{if .IsAlias }}={{end}} {{.Schema.TypeDecl}}
 {{end}}
diff --git a/pkg/codegen/utils.go b/pkg/codegen/utils.go
index a7dc3cf..034442e 100644
--- a/pkg/codegen/utils.go
+++ b/pkg/codegen/utils.go
@@ -205,7 +205,7 @@ func StringInArray(str string, array []string) bool {
 	return false
 }
 
-// This function takes a $ref value and converts it to a Go typename.
+// RefPathToGoType takes a $ref value and converts it to a Go typename.
 // #/components/schemas/Foo -> Foo
 // #/components/parameters/Bar -> Bar
 // #/components/responses/Baz -> Baz
@@ -228,7 +228,20 @@ func refPathToGoType(refPath string, local bool) (string, error) {
 		} else if depth != 4 && depth != 2 {
 			return "", fmt.Errorf("unexpected reference depth: %d for ref: %s local: %t", depth, refPath, local)
 		}
-		return SchemaNameToTypeName(pathParts[len(pathParts)-1]), nil
+
+		// Schemas may have been renamed locally, so look up the actual name in
+		// the spec.
+		name, err := findSchemaNameByRefPath(refPath, globalState.spec)
+		if err != nil {
+			return "", fmt.Errorf("error finding ref: %s in spec: %v", refPath, err)
+		}
+		if name != "" {
+			return name, nil
+		}
+		// lastPart now stores the final element of the type path. This is what
+		// we use as the base for a type name.
+		lastPart := pathParts[len(pathParts)-1]
+		return SchemaNameToTypeName(lastPart), nil
 	}
 	pathParts := strings.Split(refPath, "#")
 	if len(pathParts) != 2 {
@@ -246,7 +259,7 @@ func refPathToGoType(refPath string, local bool) (string, error) {
 	}
 }
 
-// This function takes a $ref value and checks if it has link to go type.
+// IsGoTypeReference takes a $ref value and checks if it has link to go type.
 // #/components/schemas/Foo                     -> true
 // ./local/file.yml#/components/parameters/Bar  -> true
 // ./local/file.yml                             -> false
@@ -256,7 +269,7 @@ func IsGoTypeReference(ref string) bool {
 	return ref != "" && !IsWholeDocumentReference(ref)
 }
 
-// This function takes a $ref value and checks if it is whole document reference.
+// IsWholeDocumentReference takes a $ref value and checks if it is whole document reference.
 // #/components/schemas/Foo                             -> false
 // ./local/file.yml#/components/parameters/Bar          -> false
 // ./local/file.yml                                     -> true
@@ -267,8 +280,8 @@ func IsWholeDocumentReference(ref string) bool {
 	return ref != "" && !strings.ContainsAny(ref, "#")
 }
 
-// This function converts a swagger style path URI with parameters to a
-// Echo compatible path URI. We need to replace all of Swagger parameters with
+// SwaggerUriToEchoUri converts a OpenAPI style path URI with parameters to a
+// Echo compatible path URI. We need to replace all of OpenAPI parameters with
 // ":param". Valid input parameters are:
 //   {param}
 //   {param*}
@@ -282,7 +295,7 @@ func SwaggerUriToEchoUri(uri string) string {
 	return pathParamRE.ReplaceAllString(uri, ":$1")
 }
 
-// This function converts a swagger style path URI with parameters to a
+// SwaggerUriToChiUri converts a swagger style path URI with parameters to a
 // Chi compatible path URI. We need to replace all of Swagger parameters with
 // "{param}". Valid input parameters are:
 //   {param}
@@ -535,6 +548,9 @@ func SanitizeEnumNames(enumNames []string) map[string]string {
 }
 
 func typeNamePrefix(name string) (prefix string) {
+	if len(name) == 0 {
+		return "Empty"
+	}
 	for _, r := range name {
 		switch r {
 		case '$':
@@ -547,6 +563,8 @@ func typeNamePrefix(name string) (prefix string) {
 			prefix += "Plus"
 		case '&':
 			prefix += "And"
+		case '|':
+			prefix += "Or"
 		case '~':
 			prefix += "Tilde"
 		case '=':
@@ -555,6 +573,10 @@ func typeNamePrefix(name string) (prefix string) {
 			prefix += "Hash"
 		case '.':
 			prefix += "Dot"
+		case '*':
+			prefix += "Asterisk"
+		case '^':
+			prefix += "Caret"
 		default:
 			// Prepend "N" to schemas starting with a number
 			if prefix == "" && unicode.IsDigit(r) {
@@ -638,3 +660,118 @@ func EscapePathElements(path string) string {
 	}
 	return strings.Join(elems, "/")
 }
+
+// renameSchema takes as input the name of a schema as provided in the spec,
+// and the definition of the schema. If the schema overrides the name via
+// x-go-name, the new name is returned, otherwise, the original name is
+// returned.
+func renameSchema(schemaName string, schemaRef *openapi3.SchemaRef) (string, error) {
+	// References will not change type names.
+	if schemaRef.Ref != "" {
+		return SchemaNameToTypeName(schemaName), nil
+	}
+	schema := schemaRef.Value
+
+	if extension, ok := schema.Extensions[extGoName]; ok {
+		typeName, err := extTypeName(extension)
+		if err != nil {
+			return "", fmt.Errorf("invalid value for %q: %w", extPropGoType, err)
+		}
+		return typeName, nil
+	}
+	return SchemaNameToTypeName(schemaName), nil
+}
+
+// renameParameter generates the name for a parameter, taking x-go-name into
+// account
+func renameParameter(parameterName string, parameterRef *openapi3.ParameterRef) (string, error) {
+	if parameterRef.Ref != "" {
+		return SchemaNameToTypeName(parameterName), nil
+	}
+	parameter := parameterRef.Value
+
+	if extension, ok := parameter.Extensions[extGoName]; ok {
+		typeName, err := extTypeName(extension)
+		if err != nil {
+			return "", fmt.Errorf("invalid value for %q: %w", extPropGoType, err)
+		}
+		return typeName, nil
+	}
+	return SchemaNameToTypeName(parameterName), nil
+}
+
+// renameResponse generates the name for a parameter, taking x-go-name into
+// account
+func renameResponse(responseName string, responseRef *openapi3.ResponseRef) (string, error) {
+	if responseRef.Ref != "" {
+		return SchemaNameToTypeName(responseName), nil
+	}
+	response := responseRef.Value
+
+	if extension, ok := response.Extensions[extGoName]; ok {
+		typeName, err := extTypeName(extension)
+		if err != nil {
+			return "", fmt.Errorf("invalid value for %q: %w", extPropGoType, err)
+		}
+		return typeName, nil
+	}
+	return SchemaNameToTypeName(responseName), nil
+}
+
+// renameRequestBody generates the name for a parameter, taking x-go-name into
+// account
+func renameRequestBody(requestBodyName string, requestBodyRef *openapi3.RequestBodyRef) (string, error) {
+	if requestBodyRef.Ref != "" {
+		return SchemaNameToTypeName(requestBodyName), nil
+	}
+	requestBody := requestBodyRef.Value
+
+	if extension, ok := requestBody.Extensions[extGoName]; ok {
+		typeName, err := extTypeName(extension)
+		if err != nil {
+			return "", fmt.Errorf("invalid value for %q: %w", extPropGoType, err)
+		}
+		return typeName, nil
+	}
+	return SchemaNameToTypeName(requestBodyName), nil
+}
+
+// findSchemaByRefPath turns a $ref path into a schema. This will return ""
+// if the schema wasn't found, and it'll only work successfully for schemas
+// defined within the spec that we parsed.
+func findSchemaNameByRefPath(refPath string, spec *openapi3.T) (string, error) {
+	pathElements := strings.Split(refPath, "/")
+	// All local references will have 4 path elements.
+	if len(pathElements) != 4 {
+		return "", nil
+	}
+
+	// We only support local references
+	if pathElements[0] != "#" {
+		return "", nil
+	}
+	// Only components are supported
+	if pathElements[1] != "components" {
+		return "", nil
+	}
+	propertyName := pathElements[3]
+	switch pathElements[2] {
+	case "schemas":
+		if schema, found := spec.Components.Schemas[propertyName]; found {
+			return renameSchema(propertyName, schema)
+		}
+	case "parameters":
+		if parameter, found := spec.Components.Parameters[propertyName]; found {
+			return renameParameter(propertyName, parameter)
+		}
+	case "responses":
+		if response, found := spec.Components.Responses[propertyName]; found {
+			return renameResponse(propertyName, response)
+		}
+	case "requestBodies":
+		if requestBody, found := spec.Components.RequestBodies[propertyName]; found {
+			return renameRequestBody(propertyName, requestBody)
+		}
+	}
+	return "", nil
+}
diff --git a/pkg/gin-middleware/oapi_validate.go b/pkg/gin-middleware/oapi_validate.go
index 82a885e..1f1687d 100644
--- a/pkg/gin-middleware/oapi_validate.go
+++ b/pkg/gin-middleware/oapi_validate.go
@@ -29,8 +29,10 @@ import (
 	"github.com/gin-gonic/gin"
 )
 
-const GinContextKey = "oapi-codegen/gin-context"
-const UserDataKey = "oapi-codegen/user-data"
+const (
+	GinContextKey = "oapi-codegen/gin-context"
+	UserDataKey   = "oapi-codegen/user-data"
+)
 
 // Create validator middleware from a YAML file path
 func OapiValidatorFromYamlFile(path string) (gin.HandlerFunc, error) {
@@ -54,9 +56,13 @@ func OapiRequestValidator(swagger *openapi3.T) gin.HandlerFunc {
 	return OapiRequestValidatorWithOptions(swagger, nil)
 }
 
+// ErrorHandler is called when there is an error in validation
+type ErrorHandler func(c *gin.Context, message string, statusCode int)
+
 // Options to customize request validation. These are passed through to
 // openapi3filter.
 type Options struct {
+	ErrorHandler ErrorHandler
 	Options      openapi3filter.Options
 	ParamDecoder openapi3filter.ContentParameterDecoder
 	UserData     interface{}
@@ -71,8 +77,14 @@ func OapiRequestValidatorWithOptions(swagger *openapi3.T, options *Options) gin.
 	return func(c *gin.Context) {
 		err := ValidateRequestFromContext(c, router, options)
 		if err != nil {
-			// note: i am not sure if this is the best way to handle this
-			c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()})
+			if options != nil && options.ErrorHandler != nil {
+				options.ErrorHandler(c, err.Error(), http.StatusBadRequest)
+				// in case the handler didn't internally call Abort, stop the chain
+				c.Abort()
+			} else {
+				// note: i am not sure if this is the best way to handle this
+				c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()})
+			}
 		}
 		c.Next()
 	}
diff --git a/pkg/middleware/oapi_validate.go b/pkg/middleware/oapi_validate.go
index 03b6f6a..b0bde5d 100644
--- a/pkg/middleware/oapi_validate.go
+++ b/pkg/middleware/oapi_validate.go
@@ -29,8 +29,10 @@ import (
 	echomiddleware "github.com/labstack/echo/v4/middleware"
 )
 
-const EchoContextKey = "oapi-codegen/echo-context"
-const UserDataKey = "oapi-codegen/user-data"
+const (
+	EchoContextKey = "oapi-codegen/echo-context"
+	UserDataKey    = "oapi-codegen/user-data"
+)
 
 // This is an Echo middleware function which validates incoming HTTP requests
 // to make sure that they conform to the given OAPI 3.0 specification. When
@@ -56,16 +58,20 @@ func OapiRequestValidator(swagger *openapi3.T) echo.MiddlewareFunc {
 	return OapiRequestValidatorWithOptions(swagger, nil)
 }
 
+// ErrorHandler is called when there is an error in validation
+type ErrorHandler func(c echo.Context, err *echo.HTTPError) error
+
 // Options to customize request validation. These are passed through to
 // openapi3filter.
 type Options struct {
+	ErrorHandler ErrorHandler
 	Options      openapi3filter.Options
 	ParamDecoder openapi3filter.ContentParameterDecoder
 	UserData     interface{}
 	Skipper      echomiddleware.Skipper
 }
 
-// Create a validator from a swagger object, with validation options
+// OapiRequestValidatorWithOptions creates a validator from a swagger object, with validation options
 func OapiRequestValidatorWithOptions(swagger *openapi3.T, options *Options) echo.MiddlewareFunc {
 	router, err := gorillamux.NewRouter(swagger)
 	if err != nil {
@@ -81,6 +87,9 @@ func OapiRequestValidatorWithOptions(swagger *openapi3.T, options *Options) echo
 
 			err := ValidateRequestFromContext(c, router, options)
 			if err != nil {
+				if options != nil && options.ErrorHandler != nil {
+					return options.ErrorHandler(c, err)
+				}
 				return err
 			}
 			return next(c)
@@ -90,7 +99,7 @@ func OapiRequestValidatorWithOptions(swagger *openapi3.T, options *Options) echo
 
 // ValidateRequestFromContext is called from the middleware above and actually does the work
 // of validating a request.
-func ValidateRequestFromContext(ctx echo.Context, router routers.Router, options *Options) error {
+func ValidateRequestFromContext(ctx echo.Context, router routers.Router, options *Options) *echo.HTTPError {
 	req := ctx.Request()
 	route, pathParams, err := router.FindRoute(req)
 
diff --git a/pkg/runtime/bindparam.go b/pkg/runtime/bindparam.go
index 3148c3a..33d51de 100644
--- a/pkg/runtime/bindparam.go
+++ b/pkg/runtime/bindparam.go
@@ -263,7 +263,7 @@ func bindSplitPartsToDestinationStruct(paramName string, parts []string, explode
 	return nil
 }
 
-// This works much like BindStyledParameter, however it takes a query argument
+// BindQueryParameter works much like BindStyledParameter, however it takes a query argument
 // input array from the url package, since query arguments come through a
 // different path than the styled arguments. They're also exceptionally fussy.
 // For example, consider the exploded and unexploded form parameter examples:
@@ -336,10 +336,12 @@ func BindQueryParameter(style string, explode bool, required bool, paramName str
 			case reflect.Slice:
 				// In the slice case, we simply use the arguments provided by
 				// http library.
+
 				if !found {
 					if required {
 						return fmt.Errorf("query parameter '%s' is required", paramName)
 					} else {
+						// If an optional parameter is not found, we do nothing,
 						return nil
 					}
 				}
@@ -349,7 +351,13 @@ func BindQueryParameter(style string, explode bool, required bool, paramName str
 				// form style object binding doesn't tell us which arguments
 				// in the query string correspond to the object's fields. We'll
 				// try to bind field by field.
-				err = bindParamsToExplodedObject(paramName, queryParams, output)
+				var fieldsPresent bool
+				fieldsPresent, err = bindParamsToExplodedObject(paramName, queryParams, output)
+				// If no fields were set, and there is no error, we will not fall
+				// through to assign the destination.
+				if !fieldsPresent {
+					return nil
+				}
 			default:
 				// Primitive object case. We expect to have 1 value to
 				// unmarshal.
@@ -363,6 +371,15 @@ func BindQueryParameter(style string, explode bool, required bool, paramName str
 				if len(values) != 1 {
 					return fmt.Errorf("multiple values for single value parameter '%s'", paramName)
 				}
+
+				if !found {
+					if required {
+						return fmt.Errorf("query parameter '%s' is required", paramName)
+					} else {
+						// If an optional parameter is not found, we do nothing,
+						return nil
+					}
+				}
 				err = BindStringToObject(values[0], output)
 			}
 			if err != nil {
@@ -427,21 +444,28 @@ func BindQueryParameter(style string, explode bool, required bool, paramName str
 	}
 }
 
-// This function reflects the destination structure, and pulls the value for
+// bindParamsToExplodedObject reflects the destination structure, and pulls the value for
 // each settable field from the given parameters map. This is to deal with the
 // exploded form styled object which may occupy any number of parameter names.
 // We don't try to be smart here, if the field exists as a query argument,
-// set its value.
-func bindParamsToExplodedObject(paramName string, values url.Values, dest interface{}) error {
+// set its value. This function returns a boolean, telling us whether there was
+// anything to bind. There will be nothing to bind if a parameter isn't found by name,
+// or none of an exploded object's fields are present.
+func bindParamsToExplodedObject(paramName string, values url.Values, dest interface{}) (bool, error) {
 	// Dereference pointers to their destination values
 	binder, v, t := indirect(dest)
 	if binder != nil {
-		return BindStringToObject(values.Get(paramName), dest)
+		_, found := values[paramName]
+		if !found {
+			return false, nil
+		}
+		return true, BindStringToObject(values.Get(paramName), dest)
 	}
 	if t.Kind() != reflect.Struct {
-		return fmt.Errorf("unmarshaling query arg '%s' into wrong type", paramName)
+		return false, fmt.Errorf("unmarshaling query arg '%s' into wrong type", paramName)
 	}
 
+	fieldsPresent := false
 	for i := 0; i < t.NumField(); i++ {
 		fieldT := t.Field(i)
 
@@ -466,15 +490,16 @@ func bindParamsToExplodedObject(paramName string, values url.Values, dest interf
 		fieldVal, found := values[fieldName]
 		if found {
 			if len(fieldVal) != 1 {
-				return fmt.Errorf("field '%s' specified multiple times for param '%s'", fieldName, paramName)
+				return false, fmt.Errorf("field '%s' specified multiple times for param '%s'", fieldName, paramName)
 			}
 			err := BindStringToObject(fieldVal[0], v.Field(i).Addr().Interface())
 			if err != nil {
-				return fmt.Errorf("could not bind query arg '%s' to request object: %s'", paramName, err)
+				return false, fmt.Errorf("could not bind query arg '%s' to request object: %s'", paramName, err)
 			}
+			fieldsPresent = true
 		}
 	}
-	return nil
+	return fieldsPresent, nil
 }
 
 // indirect
diff --git a/pkg/runtime/styleparam.go b/pkg/runtime/styleparam.go
index 4e5e3fd..6c792a5 100644
--- a/pkg/runtime/styleparam.go
+++ b/pkg/runtime/styleparam.go
@@ -360,8 +360,10 @@ func primitiveToString(value interface{}) (string, error) {
 	kind := t.Kind()
 
 	switch kind {
-	case reflect.Int8, reflect.Int32, reflect.Int64, reflect.Int:
+	case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
 		output = strconv.FormatInt(v.Int(), 10)
+	case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
+		output = strconv.FormatUint(v.Uint(), 10)
 	case reflect.Float64:
 		output = strconv.FormatFloat(v.Float(), 'f', -1, 64)
 	case reflect.Float32:
diff --git a/pkg/types/uuid.go b/pkg/types/uuid.go
index 50515d8..1c11f38 100644
--- a/pkg/types/uuid.go
+++ b/pkg/types/uuid.go
@@ -1,27 +1,7 @@
 package types
 
 import (
-	"encoding/json"
-	"errors"
-
 	"github.com/google/uuid"
 )
 
-type UUID uuid.UUID
-
-func (u UUID) MarshalJSON() ([]byte, error) {
-	return json.Marshal(uuid.UUID(u).String())
-}
-
-func (u *UUID) UnmarshalJSON(data []byte) error {
-	var s string
-	if err := json.Unmarshal(data, &s); err != nil {
-		return err
-	}
-	parsed, err := uuid.Parse(s)
-	if err != nil {
-		return errors.New("uuid: failed to pass validation")
-	}
-	*u = UUID(parsed)
-	return nil
-}
+type UUID = uuid.UUID


Test patch:
diff --git a/examples/petstore-expanded/chi/petstore_test.go b/examples/petstore-expanded/chi/petstore_test.go
index 71c9613..b12c4ce 100644
--- a/examples/petstore-expanded/chi/petstore_test.go
+++ b/examples/petstore-expanded/chi/petstore_test.go
@@ -103,12 +103,10 @@ func TestPetStore(t *testing.T) {
 		tag := "TagOfFido"
 
 		store.Pets = map[int64]api.Pet{
-			1: api.Pet{
-				NewPet: api.NewPet{
-					Tag: &tag,
-				},
+			1: {
+				Tag: &tag,
 			},
-			2: api.Pet{},
+			2: {},
 		}
 
 		// Filter pets by tag, we should have 1
diff --git a/examples/petstore-expanded/gin/petstore_test.go b/examples/petstore-expanded/gin/petstore_test.go
index bc1e27b..71bee77 100644
--- a/examples/petstore-expanded/gin/petstore_test.go
+++ b/examples/petstore-expanded/gin/petstore_test.go
@@ -83,9 +83,7 @@ func TestPetStore(t *testing.T) {
 
 		store.Pets = map[int64]api.Pet{
 			1: {
-				NewPet: api.NewPet{
-					Tag: &tag,
-				},
+				Tag: &tag,
 			},
 			2: {},
 		}
diff --git a/internal/test/all_of/config1.yaml b/internal/test/all_of/config1.yaml
new file mode 100644
index 0000000..570e560
--- /dev/null
+++ b/internal/test/all_of/config1.yaml
@@ -0,0 +1,9 @@
+output:
+  v1/openapi.gen.go
+package: v1
+generate:
+  - types
+  - skip-prune
+  - spec
+compatibility:
+  old-merge-schemas: true
diff --git a/internal/test/all_of/config2.yaml b/internal/test/all_of/config2.yaml
new file mode 100644
index 0000000..04f5687
--- /dev/null
+++ b/internal/test/all_of/config2.yaml
@@ -0,0 +1,7 @@
+output:
+  v2/openapi.gen.go
+package: v2
+generate:
+  - types
+  - skip-prune
+  - spec
diff --git a/internal/test/all_of/doc.go b/internal/test/all_of/doc.go
new file mode 100644
index 0000000..af048d8
--- /dev/null
+++ b/internal/test/all_of/doc.go
@@ -0,0 +1,4 @@
+package all_of
+
+//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen --old-config-style --config=config1.yaml openapi.yaml
+//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen --old-config-style --config=config2.yaml openapi.yaml
diff --git a/internal/test/all_of/openapi.yaml b/internal/test/all_of/openapi.yaml
new file mode 100644
index 0000000..e405c53
--- /dev/null
+++ b/internal/test/all_of/openapi.yaml
@@ -0,0 +1,53 @@
+openapi: "3.0.1"
+info:
+  version: 1.0.0
+  title: Tests AllOf composition
+paths:
+  /placeholder:
+    get:
+      operationId: placeholder
+      description: |
+        Validators want at least one path, so this makes them happy.
+      responses:
+        default:
+          description: placeholder
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/PersonWithID"
+components:
+  schemas:
+    PersonProperties:
+      type: object
+      description: |
+        These are fields that specify a person. They are all optional, and
+        would be used by an `Edit` style API endpoint, where each is optional.
+      properties:
+        FirstName:
+          type: string
+        LastName:
+          type: string
+        GovernmentIDNumber:
+          type: integer
+          format: int64
+    Person:
+      type: object
+      description: |
+        This is a person, with mandatory first and last name, but optional ID
+        number. This would be returned by a `Get` style API. We merge the person
+        properties with another Schema which only provides required fields.
+      allOf:
+        - $ref: "#/components/schemas/PersonProperties"
+        - required: [FirstName, LastName]
+    PersonWithID:
+      type: object
+      description: |
+        This is a person record as returned from a Create endpoint. It contains
+        all the fields of a Person, with an additional resource UUID.
+      allOf:
+        - $ref: "#/components/schemas/Person"
+        - properties:
+            ID:
+              type: integer
+              format: int64
+          required: [ ID ]
diff --git a/internal/test/all_of/v1/openapi.gen.go b/internal/test/all_of/v1/openapi.gen.go
new file mode 100644
index 0000000..8092ab3
--- /dev/null
+++ b/internal/test/all_of/v1/openapi.gen.go
@@ -0,0 +1,127 @@
+// Package v1 provides primitives to interact with the openapi HTTP API.
+//
+// Code generated by github.com/deepmap/oapi-codegen version (devel) DO NOT EDIT.
+package v1
+
+import (
+	"bytes"
+	"compress/gzip"
+	"encoding/base64"
+	"fmt"
+	"net/url"
+	"path"
+	"strings"
+
+	"github.com/getkin/kin-openapi/openapi3"
+)
+
+// Person defines model for Person.
+type Person struct {
+	// Embedded struct due to allOf(#/components/schemas/PersonProperties)
+	PersonProperties `yaml:",inline"`
+	// Embedded fields due to inline allOf schema
+}
+
+// These are fields that specify a person. They are all optional, and
+// would be used by an `Edit` style API endpoint, where each is optional.
+type PersonProperties struct {
+	FirstName          *string `json:"FirstName,omitempty"`
+	GovernmentIDNumber *int64  `json:"GovernmentIDNumber,omitempty"`
+	LastName           *string `json:"LastName,omitempty"`
+}
+
+// PersonWithID defines model for PersonWithID.
+type PersonWithID struct {
+	// Embedded struct due to allOf(#/components/schemas/Person)
+	Person `yaml:",inline"`
+	// Embedded fields due to inline allOf schema
+	ID int64 `json:"ID"`
+}
+
+// Base64 encoded, gzipped, json marshaled Swagger object
+var swaggerSpec = []string{
+
+	"H4sIAAAAAAAC/5SUT2/bOBDFv8qAu0dBTrCLPegWrNtAQJEaaNIc4gAZSyOLKTVkyVEMwfB3L0jJ/+AW",
+	"aX0awOTjvDe/0VZVtnOWiSWoYqtC1VKHqVyQD5ZjhcZ8blTxtFV/e2pUof6aHW/Npiuz8fzCW0deNAW1",
+	"y7bK0/dee6pV8aQ+ah/kDjtSmfqEU/m8e85UTaHy2omO76n7VgfQARBcksxgo6WFDrlGsX6AJgoBcg0G",
+	"gwBjRxmsegGbJNBAOV8y992KfA5JbmN7U8OKwJP0nqmG1QAIL7ckLxBkMAQ3izKHR4KO/JpAWpqeX7I7",
+	"eBo7QbbSkocvyTlsWl21YNkM4Lx90zUF2PuGRpOpQ75klSkZHKlC2dUrVaJ2mbqIrNheZEGBAD1NQiAt",
+	"CgRHlW6GQ0LRJA3pGBpziCGLGS354L0Pk2+Glw+1PnUOxLWzmiWDTUuegLBq4xD2WqMDd9bqcaDFdm8u",
+	"iNe8juZu7Rt57oilnN+lWcRjjfUdiiqUZvnv32MomoXW5OPFAxuXqrtfhviopS3nf0prYvTc1Cjybpu7",
+	"7Iztcv47JIOnyvoaMBw5bLztAOF/Tyh0GEMOpUBlWVBzWHKcaiRygsA2gLA4XQ5kwLrWE/6egu19RfDw",
+	"UM5/yl7sX3NjU8ZaTPzvnoIEuInxQUosJD2VqTfyYXR0nV/lVzF164jRaVWof/Kr/DqygdKmBGfOYEWt",
+	"NfU48jXJJdhf0ei0zgE2yAIoYChus2WCKJVBsCAxwA6/UQSfOmjRuWE0FGeGUaysVaEWJ0/GyQRnOewX",
+	"qsHepBZioMSpROeMrpLA7HX6zo1sxOp9cibeUpDnzk7d79LvRwAAAP//lzc18GUFAAA=",
+}
+
+// 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/internal/test/all_of/v2/openapi.gen.go b/internal/test/all_of/v2/openapi.gen.go
new file mode 100644
index 0000000..f2bd6cb
--- /dev/null
+++ b/internal/test/all_of/v2/openapi.gen.go
@@ -0,0 +1,127 @@
+// Package v2 provides primitives to interact with the openapi HTTP API.
+//
+// Code generated by github.com/deepmap/oapi-codegen version (devel) DO NOT EDIT.
+package v2
+
+import (
+	"bytes"
+	"compress/gzip"
+	"encoding/base64"
+	"fmt"
+	"net/url"
+	"path"
+	"strings"
+
+	"github.com/getkin/kin-openapi/openapi3"
+)
+
+// Person defines model for Person.
+type Person struct {
+	FirstName          string `json:"FirstName"`
+	GovernmentIDNumber *int64 `json:"GovernmentIDNumber,omitempty"`
+	LastName           string `json:"LastName"`
+}
+
+// These are fields that specify a person. They are all optional, and
+// would be used by an `Edit` style API endpoint, where each is optional.
+type PersonProperties struct {
+	FirstName          *string `json:"FirstName,omitempty"`
+	GovernmentIDNumber *int64  `json:"GovernmentIDNumber,omitempty"`
+	LastName           *string `json:"LastName,omitempty"`
+}
+
+// PersonWithID defines model for PersonWithID.
+type PersonWithID struct {
+	FirstName          string `json:"FirstName"`
+	GovernmentIDNumber *int64 `json:"GovernmentIDNumber,omitempty"`
+	ID                 int64  `json:"ID"`
+	LastName           string `json:"LastName"`
+}
+
+// Base64 encoded, gzipped, json marshaled Swagger object
+var swaggerSpec = []string{
+
+	"H4sIAAAAAAAC/5SUT2/bOBDFv8qAu0dBTrCLPegWrNtAQJEaaNIc4gAZSyOLKTVkyVEMwfB3L0jJ/+AW",
+	"aX0awOTjvDe/0VZVtnOWiSWoYqtC1VKHqVyQD5ZjhcZ8blTxtFV/e2pUof6aHW/Npiuz8fzCW0deNAW1",
+	"y7bK0/dee6pV8aQ+ah/kDjtSmfqEU/m8e85UTaHy2omO76n7VgfQARBcksxgo6WFDrlGsX6AJgoBcg0G",
+	"gwBjRxmsegGbJNBAOV8y992KfA5JbmN7U8OKwJP0nqmG1QAIL7ckLxBkMAQ3izKHR4KO/JpAWpqeX7I7",
+	"eBo7QbbSkocvyTlsWl21YNkM4Lx90zUF2PuGRpOpQ75klSkZHKlC2dUrVaJ2mbqIrNheZEGBAD1NQiAt",
+	"CgRHlW6GQ0LRJA3pGBpziCGLGS354L0Pk2+Glw+1PnUOxLWzmiWDTUuegLBq4xD2WqMDd9bqcaDFdm8u",
+	"iNe8juZu7Rt57oilnN+lWcRjjfUdiiqUZvnv32MomoXW5OPFAxuXqrtfhviopS3nf0prYvTc1Cjybpu7",
+	"7Iztcv47JIOnyvoaMBw5bLztAOF/Tyh0GEMOpUBlWVBzWHKcaiRygsA2gLA4XQ5kwLrWE/6egu19RfDw",
+	"UM5/yl7sX3NjU8ZaTPzvnoIEuInxQUosJD2VqTfyYXR0nV/lVzF164jRaVWof/Kr/DqygdKmBGfOYEWt",
+	"NfU48jXJJdhf0ei0zgE2yAIoYChus2WCKJVBsCAxwA6/UQSfOmjRuWE0FGeGUaysVaEWJ0/GyQRnOewX",
+	"qsHepBZioMSpROeMrpLA7HX6zo1sxOp9cibeUpDnzk7d79LvRwAAAP//lzc18GUFAAA=",
+}
+
+// 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/internal/test/client/client.gen.go b/internal/test/client/client.gen.go
index abea3b4..2319a55 100644
--- a/internal/test/client/client.gen.go
+++ b/internal/test/client/client.gen.go
@@ -32,16 +32,16 @@ type SchemaObject struct {
 }
 
 // PostBothJSONBody defines parameters for PostBoth.
-type PostBothJSONBody SchemaObject
+type PostBothJSONBody = SchemaObject
 
 // PostJsonJSONBody defines parameters for PostJson.
-type PostJsonJSONBody SchemaObject
+type PostJsonJSONBody = SchemaObject
 
 // PostBothJSONRequestBody defines body for PostBoth for application/json ContentType.
-type PostBothJSONRequestBody PostBothJSONBody
+type PostBothJSONRequestBody = PostBothJSONBody
 
 // PostJsonJSONRequestBody defines body for PostJson for application/json ContentType.
-type PostJsonJSONRequestBody PostJsonJSONBody
+type PostJsonJSONRequestBody = PostJsonJSONBody
 
 // RequestEditorFn  is the function signature for the RequestEditor callback function
 type RequestEditorFn func(ctx context.Context, req *http.Request) error
diff --git a/internal/test/client/doc.go b/internal/test/client/doc.go
index 8244197..6a97107 100644
--- a/internal/test/client/doc.go
+++ b/internal/test/client/doc.go
@@ -1,3 +1,3 @@
 package client
 
-//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen --package=client -o client.gen.go client.yaml
+//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen --old-config-style --package=client -o client.gen.go client.yaml
diff --git a/internal/test/components/components.gen.go b/internal/test/components/components.gen.go
index fbb91f6..aad97ec 100644
--- a/internal/test/components/components.gen.go
+++ b/internal/test/components/components.gen.go
@@ -4,22 +4,88 @@
 package components
 
 import (
-	"bytes"
-	"compress/gzip"
-	"context"
-	"encoding/base64"
 	"encoding/json"
 	"fmt"
-	"io"
-	"io/ioutil"
-	"net/http"
-	"net/url"
-	"path"
-	"strings"
-
-	"github.com/deepmap/oapi-codegen/pkg/runtime"
-	"github.com/getkin/kin-openapi/openapi3"
-	"github.com/labstack/echo/v4"
+)
+
+// Defines values for Enum1.
+const (
+	Enum1One   Enum1 = "One"
+	Enum1Three Enum1 = "Three"
+	Enum1Two   Enum1 = "Two"
+)
+
+// Defines values for Enum2.
+const (
+	Enum2Four  Enum2 = "Four"
+	Enum2Three Enum2 = "Three"
+	Enum2Two   Enum2 = "Two"
+)
+
+// Defines values for Enum3.
+const (
+	Enum3Bar      Enum3 = "Bar"
+	Enum3Enum1One Enum3 = "Enum1One"
+	Enum3Foo      Enum3 = "Foo"
+)
+
+// Defines values for Enum4.
+const (
+	Cat   Enum4 = "Cat"
+	Dog   Enum4 = "Dog"
+	Mouse Enum4 = "Mouse"
+)
+
+// Defines values for Enum5.
+const (
+	Enum5N5 Enum5 = 5
+	Enum5N6 Enum5 = 6
+	Enum5N7 Enum5 = 7
+)
+
+// Defines values for EnumUnion.
+const (
+	EnumUnionFour  EnumUnion = "Four"
+	EnumUnionOne   EnumUnion = "One"
+	EnumUnionThree EnumUnion = "Three"
+	EnumUnionTwo   EnumUnion = "Two"
+)
+
+// Defines values for EnumUnion2.
+const (
+	EnumUnion2One   EnumUnion2 = "One"
+	EnumUnion2Seven EnumUnion2 = "Seven"
+	EnumUnion2Three EnumUnion2 = "Three"
+	EnumUnion2Two   EnumUnion2 = "Two"
+)
+
+// Defines values for FunnyValues.
+const (
+	FunnyValuesAnd      FunnyValues = "&"
+	FunnyValuesAsterisk FunnyValues = "*"
+	FunnyValuesEmpty    FunnyValues = ""
+	FunnyValuesN5       FunnyValues = "5"
+)
+
+// Defines values for EnumParam1.
+const (
+	EnumParam1Both  EnumParam1 = "both"
+	EnumParam1False EnumParam1 = "false"
+	EnumParam1True  EnumParam1 = "true"
+)
+
+// Defines values for EnumParam2.
+const (
+	EnumParam2Both  EnumParam2 = "both"
+	EnumParam2False EnumParam2 = "false"
+	EnumParam2True  EnumParam2 = "true"
+)
+
+// Defines values for EnumParam3.
+const (
+	Alice EnumParam3 = "alice"
+	Bob   EnumParam3 = "bob"
+	Eve   EnumParam3 = "eve"
 )
 
 // Has additional properties of type int
@@ -60,6 +126,33 @@ type AdditionalPropertiesObject5 struct {
 	AdditionalProperties map[string]SchemaObject `json:"-"`
 }
 
+// Conflicts with Enum2, enum values need to be prefixed with type
+// name.
+type Enum1 string
+
+// Conflicts with Enum1, enum values need to be prefixed with type
+// name.
+type Enum2 string
+
+// Enum values conflict with Enums above, need to be prefixed
+// with type name.
+type Enum3 string
+
+// No conflicts here, should have unmodified enums
+type Enum4 string
+
+// Numerical enum
+type Enum5 int
+
+// EnumUnion defines model for EnumUnion.
+type EnumUnion string
+
+// EnumUnion2 defines model for EnumUnion2.
+type EnumUnion2 string
+
+// Edge cases for enum names
+type FunnyValues string
+
 // ObjectWithJsonField defines model for ObjectWithJsonField.
 type ObjectWithJsonField struct {
 	Name   string          `json:"name"`
@@ -67,6 +160,18 @@ type ObjectWithJsonField struct {
 	Value2 json.RawMessage `json:"value2,omitempty"`
 }
 
+// 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
+	NewName NewName `json:"ToNewName"`
+}
+
+// This schema should be renamed via x-go-name when generating
+type NewName struct {
+	Prop1 string `json:"prop1"`
+	Prop2 string `json:"prop2"`
+}
+
 // SchemaObject defines model for SchemaObject.
 type SchemaObject struct {
 	FirstName string `json:"firstName"`
@@ -78,13 +183,25 @@ type SchemaObject struct {
 	WriteOnlyRequiredProp *int    `json:"writeOnlyRequiredProp,omitempty"`
 }
 
-// ResponseObject defines model for ResponseObject.
-type ResponseObject struct {
+// EnumParam1 defines model for EnumParam1.
+type EnumParam1 string
+
+// EnumParam2 defines model for EnumParam2.
+type EnumParam2 string
+
+// EnumParam3 defines model for EnumParam3.
+type EnumParam3 string
+
+// a parameter
+type RenamedParameterObject string
+
+// RenamedResponseObject defines model for ResponseObject.
+type RenamedResponseObject struct {
 	Field SchemaObject `json:"Field"`
 }
 
-// RequestBody defines model for RequestBody.
-type RequestBody struct {
+// RenamedRequestBody defines model for RequestBody.
+type RenamedRequestBody struct {
 	Field SchemaObject `json:"Field"`
 }
 
@@ -102,7 +219,7 @@ type ParamsWithAddPropsParams struct {
 	// turned into a proper type for additionalProperties to work
 	P2 struct {
 		Inner ParamsWithAddPropsParams_P2_Inner `json:"inner"`
-	} `json:"p2"`
+	} `form:"p2" json:"p2"`
 }
 
 // ParamsWithAddPropsParams_P2_Inner defines parameters for ParamsWithAddProps.
@@ -123,7 +240,7 @@ type BodyWithAddPropsJSONBody_Inner struct {
 }
 
 // EnsureEverythingIsReferencedJSONRequestBody defines body for EnsureEverythingIsReferenced for application/json ContentType.
-type EnsureEverythingIsReferencedJSONRequestBody RequestBody
+type EnsureEverythingIsReferencedJSONRequestBody RenamedRequestBody
 
 // BodyWithAddPropsJSONRequestBody defines body for BodyWithAddProps for application/json ContentType.
 type BodyWithAddPropsJSONRequestBody BodyWithAddPropsJSONBody
@@ -723,745 +840,3 @@ func (a AdditionalPropertiesObject5) MarshalJSON() ([]byte, error) {
 	}
 	return json.Marshal(object)
 }
-
-// RequestEditorFn  is the function signature for the RequestEditor callback function
-type RequestEditorFn func(ctx context.Context, req *http.Request) error
-
-// Doer performs HTTP requests.
-//
-// The standard http.Client implements this interface.
-type HttpRequestDoer interface {
-	Do(req *http.Request) (*http.Response, error)
-}
-
-// Client which conforms to the OpenAPI3 specification for this service.
-type Client struct {
-	// The endpoint of the server conforming to this interface, with scheme,
-	// https://api.deepmap.com for example. This can contain a path relative
-	// to the server, such as https://api.deepmap.com/dev-test, and all the
-	// paths in the swagger spec will be appended to the server.
-	Server string
-
-	// Doer for performing requests, typically a *http.Client with any
-	// customized settings, such as certificate chains.
-	Client HttpRequestDoer
-
-	// A list of callbacks for modifying requests which are generated before sending over
-	// the network.
-	RequestEditors []RequestEditorFn
-}
-
-// ClientOption allows setting custom parameters during construction
-type ClientOption func(*Client) error
-
-// Creates a new Client, with reasonable defaults
-func NewClient(server string, opts ...ClientOption) (*Client, error) {
-	// create a client with sane default values
-	client := Client{
-		Server: server,
-	}
-	// mutate client and add all optional params
-	for _, o := range opts {
-		if err := o(&client); err != nil {
-			return nil, err
-		}
-	}
-	// ensure the server URL always has a trailing slash
-	if !strings.HasSuffix(client.Server, "/") {
-		client.Server += "/"
-	}
-	// create httpClient, if not already present
-	if client.Client == nil {
-		client.Client = &http.Client{}
-	}
-	return &client, nil
-}
-
-// WithHTTPClient allows overriding the default Doer, which is
-// automatically created using http.Client. This is useful for tests.
-func WithHTTPClient(doer HttpRequestDoer) ClientOption {
-	return func(c *Client) error {
-		c.Client = doer
-		return nil
-	}
-}
-
-// WithRequestEditorFn allows setting up a callback function, which will be
-// called right before sending the request. This can be used to mutate the request.
-func WithRequestEditorFn(fn RequestEditorFn) ClientOption {
-	return func(c *Client) error {
-		c.RequestEditors = append(c.RequestEditors, fn)
-		return nil
-	}
-}
-
-// The interface specification for the client above.
-type ClientInterface interface {
-	// EnsureEverythingIsReferenced request with any body
-	EnsureEverythingIsReferencedWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error)
-
-	EnsureEverythingIsReferenced(ctx context.Context, body EnsureEverythingIsReferencedJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error)
-
-	// ParamsWithAddProps request
-	ParamsWithAddProps(ctx context.Context, params *ParamsWithAddPropsParams, reqEditors ...RequestEditorFn) (*http.Response, error)
-
-	// BodyWithAddProps request with any body
-	BodyWithAddPropsWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error)
-
-	BodyWithAddProps(ctx context.Context, body BodyWithAddPropsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error)
-}
-
-func (c *Client) EnsureEverythingIsReferencedWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) {
-	req, err := NewEnsureEverythingIsReferencedRequestWithBody(c.Server, contentType, body)
-	if err != nil {
-		return nil, err
-	}
-	req = req.WithContext(ctx)
-	if err := c.applyEditors(ctx, req, reqEditors); err != nil {
-		return nil, err
-	}
-	return c.Client.Do(req)
-}
-
-func (c *Client) EnsureEverythingIsReferenced(ctx context.Context, body EnsureEverythingIsReferencedJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) {
-	req, err := NewEnsureEverythingIsReferencedRequest(c.Server, body)
-	if err != nil {
-		return nil, err
-	}
-	req = req.WithContext(ctx)
-	if err := c.applyEditors(ctx, req, reqEditors); err != nil {
-		return nil, err
-	}
-	return c.Client.Do(req)
-}
-
-func (c *Client) ParamsWithAddProps(ctx context.Context, params *ParamsWithAddPropsParams, reqEditors ...RequestEditorFn) (*http.Response, error) {
-	req, err := NewParamsWithAddPropsRequest(c.Server, params)
-	if err != nil {
-		return nil, err
-	}
-	req = req.WithContext(ctx)
-	if err := c.applyEditors(ctx, req, reqEditors); err != nil {
-		return nil, err
-	}
-	return c.Client.Do(req)
-}
-
-func (c *Client) BodyWithAddPropsWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) {
-	req, err := NewBodyWithAddPropsRequestWithBody(c.Server, contentType, body)
-	if err != nil {
-		return nil, err
-	}
-	req = req.WithContext(ctx)
-	if err := c.applyEditors(ctx, req, reqEditors); err != nil {
-		return nil, err
-	}
-	return c.Client.Do(req)
-}
-
-func (c *Client) BodyWithAddProps(ctx context.Context, body BodyWithAddPropsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) {
-	req, err := NewBodyWithAddPropsRequest(c.Server, body)
-	if err != nil {
-		return nil, err
-	}
-	req = req.WithContext(ctx)
-	if err := c.applyEditors(ctx, req, reqEditors); err != nil {
-		return nil, err
-	}
-	return c.Client.Do(req)
-}
-
-// NewEnsureEverythingIsReferencedRequest calls the generic EnsureEverythingIsReferenced builder with application/json body
-func NewEnsureEverythingIsReferencedRequest(server string, body EnsureEverythingIsReferencedJSONRequestBody) (*http.Request, error) {
-	var bodyReader io.Reader
-	buf, err := json.Marshal(body)
-	if err != nil {
-		return nil, err
-	}
-	bodyReader = bytes.NewReader(buf)
-	return NewEnsureEverythingIsReferencedRequestWithBody(server, "application/json", bodyReader)
-}
-
-// NewEnsureEverythingIsReferencedRequestWithBody generates requests for EnsureEverythingIsReferenced with any type of body
-func NewEnsureEverythingIsReferencedRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) {
-	var err error
-
-	serverURL, err := url.Parse(server)
-	if err != nil {
-		return nil, err
-	}
-
-	operationPath := fmt.Sprintf("/ensure-everything-is-referenced")
-	if operationPath[0] == '/' {
-		operationPath = "." + operationPath
-	}
-
-	queryURL, err := serverURL.Parse(operationPath)
-	if err != nil {
-		return nil, err
-	}
-
-	req, err := http.NewRequest("GET", queryURL.String(), body)
-	if err != nil {
-		return nil, err
-	}
-
-	req.Header.Add("Content-Type", contentType)
-
-	return req, nil
-}
-
-// NewParamsWithAddPropsRequest generates requests for ParamsWithAddProps
-func NewParamsWithAddPropsRequest(server string, params *ParamsWithAddPropsParams) (*http.Request, error) {
-	var err error
-
-	serverURL, err := url.Parse(server)
-	if err != nil {
-		return nil, err
-	}
-
-	operationPath := fmt.Sprintf("/params_with_add_props")
-	if operationPath[0] == '/' {
-		operationPath = "." + operationPath
-	}
-
-	queryURL, err := serverURL.Parse(operationPath)
-	if err != nil {
-		return nil, err
-	}
-
-	queryValues := queryURL.Query()
-
-	if queryFrag, err := runtime.StyleParamWithLocation("simple", true, "p1", runtime.ParamLocationQuery, params.P1); err != nil {
-		return nil, err
-	} else if parsed, err := url.ParseQuery(queryFrag); err != nil {
-		return nil, err
-	} else {
-		for k, v := range parsed {
-			for _, v2 := range v {
-				queryValues.Add(k, v2)
-			}
-		}
-	}
-
-	if queryFrag, err := runtime.StyleParamWithLocation("form", true, "p2", runtime.ParamLocationQuery, params.P2); err != nil {
-		return nil, err
-	} else if parsed, err := url.ParseQuery(queryFrag); err != nil {
-		return nil, err
-	} else {
-		for k, v := range parsed {
-			for _, v2 := range v {
-				queryValues.Add(k, v2)
-			}
-		}
-	}
-
-	queryURL.RawQuery = queryValues.Encode()
-
-	req, err := http.NewRequest("GET", queryURL.String(), nil)
-	if err != nil {
-		return nil, err
-	}
-
-	return req, nil
-}
-
-// NewBodyWithAddPropsRequest calls the generic BodyWithAddProps builder with application/json body
-func NewBodyWithAddPropsRequest(server string, body BodyWithAddPropsJSONRequestBody) (*http.Request, error) {
-	var bodyReader io.Reader
-	buf, err := json.Marshal(body)
-	if err != nil {
-		return nil, err
-	}
-	bodyReader = bytes.NewReader(buf)
-	return NewBodyWithAddPropsRequestWithBody(server, "application/json", bodyReader)
-}
-
-// NewBodyWithAddPropsRequestWithBody generates requests for BodyWithAddProps with any type of body
-func NewBodyWithAddPropsRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) {
-	var err error
-
-	serverURL, err := url.Parse(server)
-	if err != nil {
-		return nil, err
-	}
-
-	operationPath := fmt.Sprintf("/params_with_add_props")
-	if operationPath[0] == '/' {
-		operationPath = "." + operationPath
-	}
-
-	queryURL, err := serverURL.Parse(operationPath)
-	if err != nil {
-		return nil, err
-	}
-
-	req, err := http.NewRequest("POST", queryURL.String(), body)
-	if err != nil {
-		return nil, err
-	}
-
-	req.Header.Add("Content-Type", contentType)
-
-	return req, nil
-}
-
-func (c *Client) applyEditors(ctx context.Context, req *http.Request, additionalEditors []RequestEditorFn) error {
-	for _, r := range c.RequestEditors {
-		if err := r(ctx, req); err != nil {
-			return err
-		}
-	}
-	for _, r := range additionalEditors {
-		if err := r(ctx, req); err != nil {
-			return err
-		}
-	}
-	return nil
-}
-
-// ClientWithResponses builds on ClientInterface to offer response payloads
-type ClientWithResponses struct {
-	ClientInterface
-}
-
-// NewClientWithResponses creates a new ClientWithResponses, which wraps
-// Client with return type handling
-func NewClientWithResponses(server string, opts ...ClientOption) (*ClientWithResponses, error) {
-	client, err := NewClient(server, opts...)
-	if err != nil {
-		return nil, err
-	}
-	return &ClientWithResponses{client}, nil
-}
-
-// WithBaseURL overrides the baseURL.
-func WithBaseURL(baseURL string) ClientOption {
-	return func(c *Client) error {
-		newBaseURL, err := url.Parse(baseURL)
-		if err != nil {
-			return err
-		}
-		c.Server = newBaseURL.String()
-		return nil
-	}
-}
-
-// ClientWithResponsesInterface is the interface specification for the client with responses above.
-type ClientWithResponsesInterface interface {
-	// EnsureEverythingIsReferenced request with any body
-	EnsureEverythingIsReferencedWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*EnsureEverythingIsReferencedResponse, error)
-
-	EnsureEverythingIsReferencedWithResponse(ctx context.Context, body EnsureEverythingIsReferencedJSONRequestBody, reqEditors ...RequestEditorFn) (*EnsureEverythingIsReferencedResponse, error)
-
-	// ParamsWithAddProps request
-	ParamsWithAddPropsWithResponse(ctx context.Context, params *ParamsWithAddPropsParams, reqEditors ...RequestEditorFn) (*ParamsWithAddPropsResponse, error)
-
-	// BodyWithAddProps request with any body
-	BodyWithAddPropsWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*BodyWithAddPropsResponse, error)
-
-	BodyWithAddPropsWithResponse(ctx context.Context, body BodyWithAddPropsJSONRequestBody, reqEditors ...RequestEditorFn) (*BodyWithAddPropsResponse, error)
-}
-
-type EnsureEverythingIsReferencedResponse struct {
-	Body         []byte
-	HTTPResponse *http.Response
-	JSON200      *struct {
-		// Has additional properties with schema for dictionaries
-		Five *AdditionalPropertiesObject5 `json:"five,omitempty"`
-
-		// Has anonymous field which has additional properties
-		Four      *AdditionalPropertiesObject4 `json:"four,omitempty"`
-		JsonField *ObjectWithJsonField         `json:"jsonField,omitempty"`
-
-		// Has additional properties of type int
-		One *AdditionalPropertiesObject1 `json:"one,omitempty"`
-
-		// Allows any additional property
-		Three *AdditionalPropertiesObject3 `json:"three,omitempty"`
-
-		// Does not allow additional properties
-		Two *AdditionalPropertiesObject2 `json:"two,omitempty"`
-	}
-	JSONDefault *struct {
-		Field SchemaObject `json:"Field"`
-	}
-}
-
-// Status returns HTTPResponse.Status
-func (r EnsureEverythingIsReferencedResponse) Status() string {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.Status
-	}
-	return http.StatusText(0)
-}
-
-// StatusCode returns HTTPResponse.StatusCode
-func (r EnsureEverythingIsReferencedResponse) StatusCode() int {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.StatusCode
-	}
-	return 0
-}
-
-type ParamsWithAddPropsResponse struct {
-	Body         []byte
-	HTTPResponse *http.Response
-}
-
-// Status returns HTTPResponse.Status
-func (r ParamsWithAddPropsResponse) Status() string {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.Status
-	}
-	return http.StatusText(0)
-}
-
-// StatusCode returns HTTPResponse.StatusCode
-func (r ParamsWithAddPropsResponse) StatusCode() int {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.StatusCode
-	}
-	return 0
-}
-
-type BodyWithAddPropsResponse struct {
-	Body         []byte
-	HTTPResponse *http.Response
-}
-
-// Status returns HTTPResponse.Status
-func (r BodyWithAddPropsResponse) Status() string {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.Status
-	}
-	return http.StatusText(0)
-}
-
-// StatusCode returns HTTPResponse.StatusCode
-func (r BodyWithAddPropsResponse) StatusCode() int {
-	if r.HTTPResponse != nil {
-		return r.HTTPResponse.StatusCode
-	}
-	return 0
-}
-
-// EnsureEverythingIsReferencedWithBodyWithResponse request with arbitrary body returning *EnsureEverythingIsReferencedResponse
-func (c *ClientWithResponses) EnsureEverythingIsReferencedWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*EnsureEverythingIsReferencedResponse, error) {
-	rsp, err := c.EnsureEverythingIsReferencedWithBody(ctx, contentType, body, reqEditors...)
-	if err != nil {
-		return nil, err
-	}
-	return ParseEnsureEverythingIsReferencedResponse(rsp)
-}
-
-func (c *ClientWithResponses) EnsureEverythingIsReferencedWithResponse(ctx context.Context, body EnsureEverythingIsReferencedJSONRequestBody, reqEditors ...RequestEditorFn) (*EnsureEverythingIsReferencedResponse, error) {
-	rsp, err := c.EnsureEverythingIsReferenced(ctx, body, reqEditors...)
-	if err != nil {
-		return nil, err
-	}
-	return ParseEnsureEverythingIsReferencedResponse(rsp)
-}
-
-// ParamsWithAddPropsWithResponse request returning *ParamsWithAddPropsResponse
-func (c *ClientWithResponses) ParamsWithAddPropsWithResponse(ctx context.Context, params *ParamsWithAddPropsParams, reqEditors ...RequestEditorFn) (*ParamsWithAddPropsResponse, error) {
-	rsp, err := c.ParamsWithAddProps(ctx, params, reqEditors...)
-	if err != nil {
-		return nil, err
-	}
-	return ParseParamsWithAddPropsResponse(rsp)
-}
-
-// BodyWithAddPropsWithBodyWithResponse request with arbitrary body returning *BodyWithAddPropsResponse
-func (c *ClientWithResponses) BodyWithAddPropsWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*BodyWithAddPropsResponse, error) {
-	rsp, err := c.BodyWithAddPropsWithBody(ctx, contentType, body, reqEditors...)
-	if err != nil {
-		return nil, err
-	}
-	return ParseBodyWithAddPropsResponse(rsp)
-}
-
-func (c *ClientWithResponses) BodyWithAddPropsWithResponse(ctx context.Context, body BodyWithAddPropsJSONRequestBody, reqEditors ...RequestEditorFn) (*BodyWithAddPropsResponse, error) {
-	rsp, err := c.BodyWithAddProps(ctx, body, reqEditors...)
-	if err != nil {
-		return nil, err
-	}
-	return ParseBodyWithAddPropsResponse(rsp)
-}
-
-// ParseEnsureEverythingIsReferencedResponse parses an HTTP response from a EnsureEverythingIsReferencedWithResponse call
-func ParseEnsureEverythingIsReferencedResponse(rsp *http.Response) (*EnsureEverythingIsReferencedResponse, error) {
-	bodyBytes, err := ioutil.ReadAll(rsp.Body)
-	defer func() { _ = rsp.Body.Close() }()
-	if err != nil {
-		return nil, err
-	}
-
-	response := &EnsureEverythingIsReferencedResponse{
-		Body:         bodyBytes,
-		HTTPResponse: rsp,
-	}
-
-	switch {
-	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
-		var dest struct {
-			// Has additional properties with schema for dictionaries
-			Five *AdditionalPropertiesObject5 `json:"five,omitempty"`
-
-			// Has anonymous field which has additional properties
-			Four      *AdditionalPropertiesObject4 `json:"four,omitempty"`
-			JsonField *ObjectWithJsonField         `json:"jsonField,omitempty"`
-
-			// Has additional properties of type int
-			One *AdditionalPropertiesObject1 `json:"one,omitempty"`
-
-			// Allows any additional property
-			Three *AdditionalPropertiesObject3 `json:"three,omitempty"`
-
-			// Does not allow additional properties
-			Two *AdditionalPropertiesObject2 `json:"two,omitempty"`
-		}
-		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
-			return nil, err
-		}
-		response.JSON200 = &dest
-
-	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && true:
-		var dest struct {
-			Field SchemaObject `json:"Field"`
-		}
-		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
-			return nil, err
-		}
-		response.JSONDefault = &dest
-
-	case true:
-		// Content-type (text/plain) unsupported
-
-	}
-
-	return response, nil
-}
-
-// ParseParamsWithAddPropsResponse parses an HTTP response from a ParamsWithAddPropsWithResponse call
-func ParseParamsWithAddPropsResponse(rsp *http.Response) (*ParamsWithAddPropsResponse, error) {
-	bodyBytes, err := ioutil.ReadAll(rsp.Body)
-	defer func() { _ = rsp.Body.Close() }()
-	if err != nil {
-		return nil, err
-	}
-
-	response := &ParamsWithAddPropsResponse{
-		Body:         bodyBytes,
-		HTTPResponse: rsp,
-	}
-
-	return response, nil
-}
-
-// ParseBodyWithAddPropsResponse parses an HTTP response from a BodyWithAddPropsWithResponse call
-func ParseBodyWithAddPropsResponse(rsp *http.Response) (*BodyWithAddPropsResponse, error) {
-	bodyBytes, err := ioutil.ReadAll(rsp.Body)
-	defer func() { _ = rsp.Body.Close() }()
-	if err != nil {
-		return nil, err
-	}
-
-	response := &BodyWithAddPropsResponse{
-		Body:         bodyBytes,
-		HTTPResponse: rsp,
-	}
-
-	return response, nil
-}
-
-// ServerInterface represents all server handlers.
-type ServerInterface interface {
-
-	// (GET /ensure-everything-is-referenced)
-	EnsureEverythingIsReferenced(ctx echo.Context) error
-
-	// (GET /params_with_add_props)
-	ParamsWithAddProps(ctx echo.Context, params ParamsWithAddPropsParams) error
-
-	// (POST /params_with_add_props)
-	BodyWithAddProps(ctx echo.Context) error
-}
-
-// ServerInterfaceWrapper converts echo contexts to parameters.
-type ServerInterfaceWrapper struct {
-	Handler ServerInterface
-}
-
-// EnsureEverythingIsReferenced converts echo context to params.
-func (w *ServerInterfaceWrapper) EnsureEverythingIsReferenced(ctx echo.Context) error {
-	var err error
-
-	// Invoke the callback with all the unmarshalled arguments
-	err = w.Handler.EnsureEverythingIsReferenced(ctx)
-	return err
-}
-
-// ParamsWithAddProps converts echo context to params.
-func (w *ServerInterfaceWrapper) ParamsWithAddProps(ctx echo.Context) error {
-	var err error
-
-	// Parameter object where we will unmarshal all parameters from the context
-	var params ParamsWithAddPropsParams
-	// ------------- Required query parameter "p1" -------------
-
-	err = runtime.BindQueryParameter("simple", true, true, "p1", ctx.QueryParams(), &params.P1)
-	if err != nil {
-		return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter p1: %s", err))
-	}
-
-	// ------------- Required query parameter "p2" -------------
-
-	err = runtime.BindQueryParameter("form", true, true, "p2", ctx.QueryParams(), &params.P2)
-	if err != nil {
-		return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter p2: %s", err))
-	}
-
-	// Invoke the callback with all the unmarshalled arguments
-	err = w.Handler.ParamsWithAddProps(ctx, params)
-	return err
-}
-
-// BodyWithAddProps converts echo context to params.
-func (w *ServerInterfaceWrapper) BodyWithAddProps(ctx echo.Context) error {
-	var err error
-
-	// Invoke the callback with all the unmarshalled arguments
-	err = w.Handler.BodyWithAddProps(ctx)
-	return err
-}
-
-// This is a simple interface which specifies echo.Route addition functions which
-// are present on both echo.Echo and echo.Group, since we want to allow using
-// either of them for path registration
-type EchoRouter interface {
-	CONNECT(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
-	DELETE(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
-	GET(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
-	HEAD(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
-	OPTIONS(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
-	PATCH(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
-	POST(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
-	PUT(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
-	TRACE(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
-}
-
-// RegisterHandlers adds each server route to the EchoRouter.
-func RegisterHandlers(router EchoRouter, si ServerInterface) {
-	RegisterHandlersWithBaseURL(router, si, "")
-}
-
-// Registers handlers, and prepends BaseURL to the paths, so that the paths
-// can be served under a prefix.
-func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL string) {
-
-	wrapper := ServerInterfaceWrapper{
-		Handler: si,
-	}
-
-	router.GET(baseURL+"/ensure-everything-is-referenced", wrapper.EnsureEverythingIsReferenced)
-	router.GET(baseURL+"/params_with_add_props", wrapper.ParamsWithAddProps)
-	router.POST(baseURL+"/params_with_add_props", wrapper.BodyWithAddProps)
-
-}
-
-// Base64 encoded, gzipped, json marshaled Swagger object
-var swaggerSpec = []string{
-
-	"H4sIAAAAAAAC/9RXTW/cNhD9KwO2R9nrj/SyNxdNURdoEzgGeogNgxZHFl3uUCEpb4RA/70YStqVtdRm",
-	"bedSX6ylyPl4M/P49E3kdlVZQgpeLL8Jh19q9OFXqzTGhavNQsM/c0sBKfCjrCqjcxm0pcWjt8RrPi9x",
-	"JfmpcrZCF3orv2s0ih9+dliIpfhpsXW76A75xaf4/8P9I+ZBtG0Wg9EOlVh+7i3c8nLAr2FRGaknLkNT",
-	"oVgKH5ymB9HyH9vwlSU/JNP96H383/LJhEKfO11xjGIpLsDrVWUQhiTBbp31UbChC6U0H5Hm4yaLLqzT",
-	"mHji9ci/poAP6MSO+z+kh+1Z2CIEtgA+DJqCyCbQaZW2TXKFiawzYavOQQqS55hGExl7uM2GrQMi2R4U",
-	"zuZRKKTxOE38N4seyAaQxth1GoO35v2DUjufTy24eiezC07Ig6QmkVWzk9MLYn9Z2O9eFnbsRLLUrGzt",
-	"oeDRgnWp8xLKuR7drQ8Ruu+5/aHpH3a8iyt7DYq/7Jvuw5nr8Llf61BCZwQK60DpPG5yHeA7oXce/tGh",
-	"/NNb2pDqQShn4kmaGiODFdatZBBLEXk7m9l6dsDW9Nj1nlLoP4NqJ/ZCOx/+nkvAoVQfyDRXvUcuEG98",
-	"Dvd1qf1mBEF7GAIESQoGGxl4C6FEeLCwsgoN+NLWRkEpnxB0AC4aVJaZx2VwQ9Lz6lobE6nsntk6N7VC",
-	"Hh0kfqk9eKQAhbMryI3m52DBo3tCd3zD8A3uhwHZzdGadPJrpwOmsp9Q5Ghn52Rao+ghG0E9A+ycy9t4",
-	"s2oqLHs3OkfyuG088dflNccbdOBMxDX6AJ8iBNwZ6HxXptPjk+OT7r5CkpUWS3F+fHJ8ykQjQxnbYYHk",
-	"a4dH+ISuCaWmhyPtjxwW6JByjM3/gGGmB5BUrB/gV+2D7wouA2wHGHJJXMncoQyoQBOEUvsb8hXmsV36",
-	"UleuJlSxgtxXUfRcKrEU72OA7zfxXfqrbXTZSB42cxzyTEEuxvJxqsbOTk7eIMEK/YTf47F91NhmorC1",
-	"e72Jd2ziccxb++ykqI6bhd6QxGnsy9LhG2ycRxtr+3oLZ5E1J8TYq9VC1ibMd0rfDIuJLu9OLyrp5Mrf",
-	"8aVyJ5W64/r72RG5AB6z7gqKJzGg87HpJdxb1fSKoKeO9A2WmIiPMQou3IWKhBGlw8aBWH5OE/awY16C",
-	"RGcs+sWXGl0z3PFLUZ2KMcV1zLqdg30CZed+8qGJtNV9KYg2OyRaGqmpqD+2908HIiEqz3fBPd5QqB1F",
-	"sgmWr5i4s9P/rAFS0fLJtXX/ziNwtheBFym3xN0zbdaU4rpt29tnhEW1MW0mKusT3Rc1EfTcN243Zjep",
-	"id8q7TAPSUAy7tMb2gs8N3bqbKJnmW4nHete+R1/uBo+tAyjb59XSuLhY2ioUzvtlXa3cG3b/hcAAP//",
-	"Jit1A+wQAAA=",
-}
-
-// 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/internal/test/components/components.yaml b/internal/test/components/components.yaml
index 479159e..a11eb9f 100644
--- a/internal/test/components/components.yaml
+++ b/internal/test/components/components.yaml
@@ -18,6 +18,7 @@ paths:
         $ref: "#/components/requestBodies/RequestBody"
       responses:
         200:
+          description: placeholder
           content:
             application/json:
               schema:
@@ -65,6 +66,13 @@ paths:
                 additionalProperties:
                   type: string
             required: [inner]
+      responses:
+        default:
+          description: placeholder
+          content:
+            text/plain:
+              schema:
+                type: string
     post:
       operationId: BodyWithAddProps
       description: |
@@ -84,6 +92,13 @@ paths:
                     type: integer
               required: [name, inner]
               additionalProperties: true
+      responses:
+        default:
+          description: placeholder
+          content:
+            text/plain:
+              schema:
+                type: string
 components:
   schemas:
     SchemaObject:
@@ -169,8 +184,74 @@ components:
           type: string
           format: json
       required: [name, value1]
+    Enum1:
+      description: |
+        Conflicts with Enum2, enum values need to be prefixed with type
+        name.
+      type: string
+      enum: [One, Two, Three]
+    Enum2:
+      description: |
+        Conflicts with Enum1, enum values need to be prefixed with type
+        name.
+      type: string
+      enum: [ Two, Three, Four ]
+    Enum3:
+      description: |
+        Enum values conflict with Enums above, need to be prefixed
+        with type name.
+      type: string
+      enum: [ Enum1One, Foo, Bar ]
+    Enum4:
+      description: |
+        No conflicts here, should have unmodified enums
+      type: string
+      enum: [ Cat, Dog, Mouse ]
+    Enum5:
+      description: |
+        Numerical enum
+      type: integer
+      enum: [ 5, 6, 7 ]
+    EnumUnion:
+      description: |
+        Two enums of the same type combined with allOf.
+      allOf:
+        - $ref: "#/components/schemas/Enum1"
+        - $ref: "#/components/schemas/Enum2"
+    EnumUnion2:
+      description: |
+        Two enums of the same type combined with allOf.
+      allOf:
+      - $ref: "#/components/schemas/Enum1"
+      - enum: [ Seven ]
+    FunnyValues:
+      description: Edge cases for enum names
+      type: string
+      enum:
+        - '*'
+        - '5'
+        - '&'
+        - ''
+    RenameMe:
+      description: This schema should be renamed via x-go-name when generating
+      x-go-name: NewName
+      type: object
+      properties:
+        prop1:
+          type: string
+        prop2:
+          type: string
+      required: [prop1, prop2]
+    ReferenceToRenameMe:
+      description: When a Schema is renamed, $ref should refer to the new name
+      type: object
+      properties:
+        ToNewName:
+          $ref: "#/components/schemas/RenameMe"
+      required: [ToNewName]
   responses:
     ResponseObject:
+      x-go-name: RenamedResponseObject
       description: A simple response object
       content:
         application/json:
@@ -184,6 +265,7 @@ components:
             type: string
   parameters:
     ParameterObject:
+      x-go-name: RenamedParameterObject
       name: ParameterObject
       description: a parameter
       in: query
@@ -197,8 +279,30 @@ components:
         text/plain:
           schema:
             type: string
+    EnumParam1:
+      name: ParamEnum1
+      description: a parameter
+      in: query
+      schema:
+        type: string
+        enum: [on, off, both]
+    EnumParam2:
+      name: ParamEnum2
+      description: a parameter
+      in: query
+      schema:
+        type: string
+        enum: [ on, off, both ]
+    EnumParam3:
+      name: ParamEnum3
+      description: a parameter
+      in: query
+      schema:
+        type: string
+        enum: [ alice, eve, bob ]
   requestBodies:
     RequestBody:
+      x-go-name: RenamedRequestBody
       content:
         application/json:
           schema:
diff --git a/internal/test/components/config.yaml b/internal/test/components/config.yaml
new file mode 100644
index 0000000..bd3484a
--- /dev/null
+++ b/internal/test/components/config.yaml
@@ -0,0 +1,6 @@
+output:
+  components.gen.go
+package: components
+generate:
+  - types
+  - skip-prune
diff --git a/internal/test/components/doc.go b/internal/test/components/doc.go
index c8f457a..651d172 100644
--- a/internal/test/components/doc.go
+++ b/internal/test/components/doc.go
@@ -1,3 +1,3 @@
 package components
 
-//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen  --package=components -o components.gen.go components.yaml
+//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen --old-config-style  --config=config.yaml components.yaml
diff --git a/internal/test/externalref/externalref.cfg.yaml b/internal/test/externalref/externalref.cfg.yaml
index 82fe596..caab32e 100644
--- a/internal/test/externalref/externalref.cfg.yaml
+++ b/internal/test/externalref/externalref.cfg.yaml
@@ -1,10 +1,11 @@
-output:
-  externalref.gen.go
 package: externalref
 generate:
-  - types
-  - skip-prune
-  - spec
+  models: true
+  embedded-spec: true
 import-mapping:
   ./packageA/spec.yaml: github.com/deepmap/oapi-codegen/internal/test/externalref/packageA
   ./packageB/spec.yaml: github.com/deepmap/oapi-codegen/internal/test/externalref/packageB
+output: externalref.gen.go
+output-options:
+
+  skip-prune: true
diff --git a/internal/test/externalref/packageA/doc.go b/internal/test/externalref/packageA/doc.go
index 6d851c8..cce4462 100644
--- a/internal/test/externalref/packageA/doc.go
+++ b/internal/test/externalref/packageA/doc.go
@@ -1,3 +1,3 @@
 package packageA
 
-//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen -generate types,skip-prune,spec --package=packageA -o externalref.gen.go --import-mapping=../packageB/spec.yaml:github.com/deepmap/oapi-codegen/internal/test/externalref/packageB spec.yaml
+//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen --old-config-style -generate types,skip-prune,spec --package=packageA -o externalref.gen.go --import-mapping=../packageB/spec.yaml:github.com/deepmap/oapi-codegen/internal/test/externalref/packageB spec.yaml
diff --git a/internal/test/externalref/packageB/doc.go b/internal/test/externalref/packageB/doc.go
index 29c3185..c03650a 100644
--- a/internal/test/externalref/packageB/doc.go
+++ b/internal/test/externalref/packageB/doc.go
@@ -1,3 +1,3 @@
 package packageB
 
-//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen -generate types,skip-prune,spec --package=packageB -o externalref.gen.go spec.yaml
+//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen --old-config-style -generate types,skip-prune,spec --package=packageB -o externalref.gen.go spec.yaml
diff --git a/internal/test/issues/issue-312/doc.go b/internal/test/issues/issue-312/doc.go
index de380e3..bc971d5 100644
--- a/internal/test/issues/issue-312/doc.go
+++ b/internal/test/issues/issue-312/doc.go
@@ -1,3 +1,3 @@
 package issue_312
 
-//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen --package=issue_312 -o issue.gen.go spec.yaml
+//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen --old-config-style --package=issue_312 -o issue.gen.go spec.yaml
diff --git a/internal/test/issues/issue-312/issue.gen.go b/internal/test/issues/issue-312/issue.gen.go
index 4710f7c..480b03d 100644
--- a/internal/test/issues/issue-312/issue.gen.go
+++ b/internal/test/issues/issue-312/issue.gen.go
@@ -44,10 +44,10 @@ type PetNames struct {
 }
 
 // ValidatePetsJSONBody defines parameters for ValidatePets.
-type ValidatePetsJSONBody PetNames
+type ValidatePetsJSONBody = PetNames
 
 // ValidatePetsJSONRequestBody defines body for ValidatePets for application/json ContentType.
-type ValidatePetsJSONRequestBody ValidatePetsJSONBody
+type ValidatePetsJSONRequestBody = ValidatePetsJSONBody
 
 // RequestEditorFn  is the function signature for the RequestEditor callback function
 type RequestEditorFn func(ctx context.Context, req *http.Request) error
diff --git a/internal/test/issues/issue-312/spec.yaml b/internal/test/issues/issue-312/spec.yaml
index bcf273e..47be65f 100644
--- a/internal/test/issues/issue-312/spec.yaml
+++ b/internal/test/issues/issue-312/spec.yaml
@@ -1,4 +1,3 @@
-
 openapi: "3.0.0"
 info:
   version: 1.0.0
diff --git a/internal/test/issues/issue-52/doc.go b/internal/test/issues/issue-52/doc.go
index 1d25700..9dfe7fb 100644
--- a/internal/test/issues/issue-52/doc.go
+++ b/internal/test/issues/issue-52/doc.go
@@ -1,3 +1,3 @@
 package issue_52
 
-//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen  --package=issue_52 -o issue.gen.go spec.yaml
+//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen --old-config-style  --package=issue_52 -o issue.gen.go spec.yaml
diff --git a/internal/test/issues/issue-52/issue.gen.go b/internal/test/issues/issue-52/issue.gen.go
index f671f94..dd162db 100644
--- a/internal/test/issues/issue-52/issue.gen.go
+++ b/internal/test/issues/issue-52/issue.gen.go
@@ -21,7 +21,7 @@ import (
 )
 
 // ArrayValue defines model for ArrayValue.
-type ArrayValue []Value
+type ArrayValue = []Value
 
 // Document defines model for Document.
 type Document struct {
diff --git a/internal/test/issues/issue-52/issue_test.go b/internal/test/issues/issue-52/issue_test.go
index 0408fe6..960c266 100644
--- a/internal/test/issues/issue-52/issue_test.go
+++ b/internal/test/issues/issue-52/issue_test.go
@@ -17,13 +17,16 @@ func TestIssue(t *testing.T) {
 	swagger, err := openapi3.NewLoader().LoadFromData(spec)
 	require.NoError(t, err)
 
-	opts := codegen.Options{
-		GenerateClient:     true,
-		GenerateEchoServer: true,
-		GenerateTypes:      true,
-		EmbedSpec:          true,
+	opts := codegen.Configuration{
+		PackageName: "issue_52",
+		Generate: codegen.GenerateOptions{
+			EchoServer:   true,
+			Client:       true,
+			Models:       true,
+			EmbeddedSpec: true,
+		},
 	}
 
-	_, err = codegen.Generate(swagger, "issue_52", opts)
+	_, err = codegen.Generate(swagger, opts)
 	require.NoError(t, err)
 }
diff --git a/internal/test/issues/issue-579/gen.go b/internal/test/issues/issue-579/gen.go
new file mode 100644
index 0000000..8a79e37
--- /dev/null
+++ b/internal/test/issues/issue-579/gen.go
@@ -0,0 +1,3 @@
+package issue_579
+
+//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen --old-config-style --package=issue_579 --generate=types,skip-prune --alias-types -o issue.gen.go spec.yaml
diff --git a/internal/test/issues/issue-579/issue.gen.go b/internal/test/issues/issue-579/issue.gen.go
new file mode 100644
index 0000000..93cc60a
--- /dev/null
+++ b/internal/test/issues/issue-579/issue.gen.go
@@ -0,0 +1,17 @@
+// Package issue_579 provides primitives to interact with the openapi HTTP API.
+//
+// Code generated by github.com/deepmap/oapi-codegen version (devel) DO NOT EDIT.
+package issue_579
+
+import (
+	openapi_types "github.com/deepmap/oapi-codegen/pkg/types"
+)
+
+// AliasedDate defines model for AliasedDate.
+type AliasedDate = openapi_types.Date
+
+// Pet defines model for Pet.
+type Pet struct {
+	Born   *AliasedDate        `json:"born,omitempty"`
+	BornAt *openapi_types.Date `json:"born_at,omitempty"`
+}
diff --git a/internal/test/issues/issue-579/issue_test.go b/internal/test/issues/issue-579/issue_test.go
new file mode 100644
index 0000000..4c1b901
--- /dev/null
+++ b/internal/test/issues/issue-579/issue_test.go
@@ -0,0 +1,14 @@
+package issue_579
+
+import (
+	"encoding/json"
+	"testing"
+
+	"github.com/stretchr/testify/require"
+)
+
+func TestAliasedDate(t *testing.T) {
+	pet := Pet{}
+	err := json.Unmarshal([]byte(`{"born": "2022-05-19", "born_at": "2022-05-20"}`), &pet)
+	require.NoError(t, err)
+}
diff --git a/internal/test/issues/issue-579/spec.yaml b/internal/test/issues/issue-579/spec.yaml
new file mode 100644
index 0000000..2bf359c
--- /dev/null
+++ b/internal/test/issues/issue-579/spec.yaml
@@ -0,0 +1,27 @@
+openapi: "3.0.0"
+info:
+  version: 1.0.0
+  title: Issue 579 test
+paths:
+  /placeholder:
+    get:
+      responses:
+        200:
+          description: placeholder
+          content:
+            text/plain:
+              schema:
+                type: string
+components:
+  schemas:
+    Pet:
+      type: object
+      properties:
+        born:
+          $ref: "#/components/schemas/AliasedDate"
+        born_at:
+          type: string
+          format: date
+    AliasedDate:
+      type: string
+      format: date
diff --git a/internal/test/issues/issue-grab_import_names/doc.go b/internal/test/issues/issue-grab_import_names/doc.go
index 2f8ae67..7bf6678 100644
--- a/internal/test/issues/issue-grab_import_names/doc.go
+++ b/internal/test/issues/issue-grab_import_names/doc.go
@@ -1,3 +1,3 @@
 package grab_import_names
 
-//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen  --package=grab_import_names -o issue.gen.go spec.yaml
+//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen --old-config-style  --package=grab_import_names -o issue.gen.go spec.yaml
diff --git a/internal/test/issues/issue-grab_import_names/issue_test.go b/internal/test/issues/issue-grab_import_names/issue_test.go
index 705ae82..1f90f83 100644
--- a/internal/test/issues/issue-grab_import_names/issue_test.go
+++ b/internal/test/issues/issue-grab_import_names/issue_test.go
@@ -3,23 +3,27 @@ package grab_import_names
 import (
 	"testing"
 
-	"github.com/deepmap/oapi-codegen/pkg/codegen"
 	"github.com/getkin/kin-openapi/openapi3"
 	"github.com/stretchr/testify/require"
+
+	"github.com/deepmap/oapi-codegen/pkg/codegen"
 )
 
 func TestLineComments(t *testing.T) {
 	swagger, err := openapi3.NewLoader().LoadFromFile("spec.yaml")
 	require.NoError(t, err)
 
-	opts := codegen.Options{
-		GenerateClient:     true,
-		GenerateEchoServer: true,
-		GenerateTypes:      true,
-		EmbedSpec:          true,
+	opts := codegen.Configuration{
+		PackageName: "grab_import_names",
+		Generate: codegen.GenerateOptions{
+			EchoServer:   true,
+			Client:       true,
+			Models:       true,
+			EmbeddedSpec: true,
+		},
 	}
 
-	code, err := codegen.Generate(swagger, "grab_import_names", opts)
+	code, err := codegen.Generate(swagger, opts)
 	require.NoError(t, err)
 	require.NotContains(t, code, `"openapi_types"`)
 }
diff --git a/internal/test/issues/issue-illegal_enum_names/doc.go b/internal/test/issues/issue-illegal_enum_names/doc.go
index b348a97..7d1b2f1 100644
--- a/internal/test/issues/issue-illegal_enum_names/doc.go
+++ b/internal/test/issues/issue-illegal_enum_names/doc.go
@@ -1,3 +1,3 @@
 package illegal_enum_names
 
-//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen  --package=illegal_enum_names -o issue.gen.go spec.yaml
+//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen --old-config-style  --package=illegal_enum_names -o issue.gen.go spec.yaml
diff --git a/internal/test/issues/issue-illegal_enum_names/issue.gen.go b/internal/test/issues/issue-illegal_enum_names/issue.gen.go
index a117067..cf58178 100644
--- a/internal/test/issues/issue-illegal_enum_names/issue.gen.go
+++ b/internal/test/issues/issue-illegal_enum_names/issue.gen.go
@@ -22,23 +22,15 @@ import (
 
 // Defines values for Bar.
 const (
-	BarBar Bar = "Bar"
-
-	BarFoo Bar = "Foo"
-
-	BarFoo1 Bar = " Foo"
-
-	BarFoo2 Bar = " Foo "
-
-	BarFoo3 Bar = "_Foo_"
-
-	BarFooBar Bar = "Foo Bar"
-
+	BarBar     Bar = "Bar"
+	BarFoo     Bar = "Foo"
+	BarFoo1    Bar = " Foo"
+	BarFoo2    Bar = " Foo "
+	BarFoo3    Bar = "_Foo_"
+	BarFooBar  Bar = "Foo Bar"
 	BarFooBar1 Bar = "Foo-Bar"
-
-	BarN1 Bar = "1"
-
-	BarN1Foo Bar = "1Foo"
+	BarN1      Bar = "1"
+	BarN1Foo   Bar = "1Foo"
 )
 
 // Bar defines model for Bar.
diff --git a/internal/test/issues/issue-illegal_enum_names/issue_test.go b/internal/test/issues/issue-illegal_enum_names/issue_test.go
index 1032cb0..8c40e09 100644
--- a/internal/test/issues/issue-illegal_enum_names/issue_test.go
+++ b/internal/test/issues/issue-illegal_enum_names/issue_test.go
@@ -6,23 +6,27 @@ import (
 	"go/token"
 	"testing"
 
-	"github.com/deepmap/oapi-codegen/pkg/codegen"
 	"github.com/getkin/kin-openapi/openapi3"
 	"github.com/stretchr/testify/require"
+
+	"github.com/deepmap/oapi-codegen/pkg/codegen"
 )
 
 func TestIllegalEnumNames(t *testing.T) {
 	swagger, err := openapi3.NewLoader().LoadFromFile("spec.yaml")
 	require.NoError(t, err)
 
-	opts := codegen.Options{
-		GenerateClient:     true,
-		GenerateEchoServer: true,
-		GenerateTypes:      true,
-		EmbedSpec:          true,
+	opts := codegen.Configuration{
+		PackageName: "illegal_enum_names",
+		Generate: codegen.GenerateOptions{
+			EchoServer:   true,
+			Client:       true,
+			Models:       true,
+			EmbeddedSpec: true,
+		},
 	}
 
-	code, err := codegen.Generate(swagger, "illegal_enum_names", opts)
+	code, err := codegen.Generate(swagger, opts)
 	require.NoError(t, err)
 
 	f, err := parser.ParseFile(token.NewFileSet(), "", code, parser.AllErrors)
diff --git a/internal/test/issues/issue-illegal_enum_names/spec.yaml b/internal/test/issues/issue-illegal_enum_names/spec.yaml
index b9e9852..cf3fc78 100644
--- a/internal/test/issues/issue-illegal_enum_names/spec.yaml
+++ b/internal/test/issues/issue-illegal_enum_names/spec.yaml
@@ -21,13 +21,13 @@ components:
   schemas:
     Bar:
       type: string
-      enum: 
+      enum:
         - Foo
         - Bar
         - Foo Bar
         - Foo-Bar
         - 1Foo
-        - Bar
+        - Bar  # A swagger validator would catch this duplicate value
         - ' Foo'
         - ' Foo '
         - _Foo_
diff --git a/internal/test/parameters/doc.go b/internal/test/parameters/doc.go
index e4b88fb..736ce1e 100644
--- a/internal/test/parameters/doc.go
+++ b/internal/test/parameters/doc.go
@@ -1,3 +1,3 @@
 package parameters
 
-//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen --package=parameters -o parameters.gen.go parameters.yaml
+//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen --old-config-style --package=parameters -o parameters.gen.go parameters.yaml
diff --git a/internal/test/parameters/parameters.gen.go b/internal/test/parameters/parameters.gen.go
index 537d26f..6091ea6 100644
--- a/internal/test/parameters/parameters.gen.go
+++ b/internal/test/parameters/parameters.gen.go
@@ -37,28 +37,28 @@ type Object struct {
 // GetCookieParams defines parameters for GetCookie.
 type GetCookieParams struct {
 	// primitive
-	P *int32 `json:"p,omitempty"`
+	P *int32 `form:"p,omitempty" json:"p,omitempty"`
 
 	// primitive
-	Ep *int32 `json:"ep,omitempty"`
+	Ep *int32 `form:"ep,omitempty" json:"ep,omitempty"`
 
 	// exploded array
-	Ea *[]int32 `json:"ea,omitempty"`
+	Ea *[]int32 `form:"ea,omitempty" json:"ea,omitempty"`
 
 	// array
-	A *[]int32 `json:"a,omitempty"`
+	A *[]int32 `form:"a,omitempty" json:"a,omitempty"`
 
 	// exploded object
-	Eo *Object `json:"eo,omitempty"`
+	Eo *Object `form:"eo,omitempty" json:"eo,omitempty"`
 
 	// object
-	O *Object `json:"o,omitempty"`
+	O *Object `form:"o,omitempty" json:"o,omitempty"`
 
 	// complex object
-	Co *ComplexObject `json:"co,omitempty"`
+	Co *ComplexObject `form:"co,omitempty" json:"co,omitempty"`
 
 	// name starting with number
-	N1s *string `json:"1s,omitempty"`
+	N1s *string `form:"1s,omitempty" json:"1s,omitempty"`
 }
 
 // GetHeaderParams defines parameters for GetHeader.
@@ -97,31 +97,31 @@ type GetDeepObjectParams struct {
 // GetQueryFormParams defines parameters for GetQueryForm.
 type GetQueryFormParams struct {
 	// exploded array
-	Ea *[]int32 `json:"ea,omitempty"`
+	Ea *[]int32 `form:"ea,omitempty" json:"ea,omitempty"`
 
 	// array
-	A *[]int32 `json:"a,omitempty"`
+	A *[]int32 `form:"a,omitempty" json:"a,omitempty"`
 
 	// exploded object
-	Eo *Object `json:"eo,omitempty"`
+	Eo *Object `form:"eo,omitempty" json:"eo,omitempty"`
 
 	// object
-	O *Object `json:"o,omitempty"`
+	O *Object `form:"o,omitempty" json:"o,omitempty"`
 
 	// exploded primitive
-	Ep *int32 `json:"ep,omitempty"`
+	Ep *int32 `form:"ep,omitempty" json:"ep,omitempty"`
 
 	// primitive
-	P *int32 `json:"p,omitempty"`
+	P *int32 `form:"p,omitempty" json:"p,omitempty"`
 
 	// primitive string
-	Ps *string `json:"ps,omitempty"`
+	Ps *string `form:"ps,omitempty" json:"ps,omitempty"`
 
 	// complex object
-	Co *ComplexObject `json:"co,omitempty"`
+	Co *ComplexObject `form:"co,omitempty" json:"co,omitempty"`
 
 	// name starting with number
-	N1s *string `json:"1s,omitempty"`
+	N1s *string `form:"1s,omitempty" json:"1s,omitempty"`
 }
 
 // RequestEditorFn  is the function signature for the RequestEditor callback function
@@ -3250,25 +3250,25 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL
 // Base64 encoded, gzipped, json marshaled Swagger object
 var swaggerSpec = []string{
 
-	"H4sIAAAAAAAC/9xa34+bOBD+V9DcPZ1ISNo33qrer5Wu295lpZ5U7YMXJsE9wK7t7O0q4n8/2UAAQwgk",
-	"YTe9ty7MzDfzefzVHrKDgCWcpZgqCf4OBErOUonmjxVNeIx/FY/0k4ClClOl/6nwSXk8JjTVf8kgwoSY",
-	"588cwQepBE03kGWZCyHKQFCuKEvBh3eONHGdEsthD18xUKBN8zgG/T3TVk8f85f+DrhgHIWieXI3YQ2N",
-	"pgo3KCBz4Ua+C5M8qeLlA2MxklS/rIL9KHANPvzgVfV7Bbj3scpH4LctFRiC/6V0djV0hXPfCNvMcU2F",
-	"VLckwQ5iXBAs7nphoRortxbq3nBK0zXTzjENsFic1ADBh5s7HV1RpcPDHUrlrFA8ogAXHlHIfBmW88V8",
-	"oQ0Zx5RwCj68nS/mS3CBExWZ/L1ivfP6vB0ngiSZfrNBU64uluh11asBv6F6X3cwoQRJUKGQ4H9p9A/h",
-	"PKaBcfa+SmZ1Ud/yNBujYAN8kza4JQ0GGepcKrHF7N5t9vibxeIQ3t7OszZCZjC9gLF/KPazYSxaNDQ3",
-	"BBc0oYo+akN84jELEfw1iSUWhQVlmLI0cGtUrZlIiMo3wds34Lb2ROYOQtT0HADEsxELlNAhQpDnobCk",
-	"AUsVJnIQ/v5JjtaRTyuNPr6nS2NPCys3zCBeWCOhYVJmQ7cR+yg4DXGq7d6sJMgNKg47KwgYtEnQ7xyp",
-	"iFA03Tj/UhU56TZ5MFLZGWUpG0TY0t1Ul3Qbx0YpIiQhij6l+D23OFcpojJMke7fs081l0k1owd69kvR",
-	"5i+iIu1E3mnr7iReTFMOZPXKytLOKt9m3WRNITSHMvju9KZdSBGoLOgE9bFjLmerwnr2mapodltaj1ak",
-	"mDxgXCyyaURvNzfS81Pv8e4P262tWF1tNuRkdpmN4IJUz+bcayqES5736pyVJ+KxpB06GF+CtSG7ZHJ+",
-	"bllXVx3np+nXQ1BdPP5HfbWvv9lZI4g72lrnMPfavZUQJeiT1Vo07N94H1pOp2w8Gk7eU3l10xG276lR",
-	"jJ2uVUcoG9dMk5HTkioaDiDnAkL1PXdUW6fGsXaGSl17V3Ei5V0k2HYTDRmVfarMewdlIwatrzIG+7ZF",
-	"8fwzIq+moIdKrlkduemGiLz/6mJgqzrDPPTJHWKd+qtGCaucDx2mTSq/MpH01f7n3uhI6YMuuVb1F5uU",
-	"VXVrVxh5ybWyerGkhl12bc6mn6JZiJcA3Jd6bB5jVzvN0Lin2ssBOoXGHcDpH8m98ljASva0KaQVZNQQ",
-	"8ixtz7/UNY9JA268q5bb9c4J8hJhMtYa385G0HY9k4LJGLIP4MfPTKsOvyueFUzP3PAvs6sux6uYFkzG",
-	"0v4DxHB+6p9LLGZOYmJA80xJQ/F/ymeqonxW7O2WA6houU14QVlOfEPRDJtfP+R5b0UMPkRKcd/zip8+",
-	"KJRqrs/5CeFzQiG7z/4LAAD//734if8YIwAA",
+	"H4sIAAAAAAAC/9xa34+jNhD+V9C0TxUJyd0bb6frr5V6e9dmpat02gcvTIKvgH22s91VxP9e2UAAQwgk",
+	"YTfXtwRm5pv5GH+xh+wgYAlnKaZKgr8DgZKzVKL5sqIJj/Gv4pK+ErBUYar0R4VPyuMxoan+JoMIE2Ku",
+	"P3MEH6QSNN1AlmUuhCgDQbmiLAUf3jnSxHVKLIc9fMVAgTbN4xj090xbPX3Mb/o74IJxFIrmyd2ENTSa",
+	"KtyggMyFG/kuTPKkipsPjMVIUn2zCvajwDX48INX1e8V4N7HKh+B37ZUYAj+l9LZ1dAVzn0jbDPHNRVS",
+	"3ZIEO4hxQbC464aFaqzcWqh7wylN10w7xzTA4uGkBgg+3Nzp6IoqHR7uUCpnheIRBbjwiELmj2E5X8wX",
+	"2pBxTAmn4MPb+WK+BBc4UZHJ3yued16ft+NEkCTTdzZoytXFEv1c9dOA31C9rzuYUIIkqFBI8L80+odw",
+	"HtPAOHtfJbO6qO/xNBujYAN8kza4JQ0GGepcKrHF7N5t9vibxeIQ3t7OsxZCZjC9gLF/KPazYSxaNDQX",
+	"BBc0oYo+akN84jELEfw1iSUWhQVlmLI0cGtUrZlIiMoXwds34LbWROYOQtT0HADEsxELlNAhQpDnobCk",
+	"AUsVJnIQ/v5KjtaRTyuNPr6nS2NPCysXzCBeWCOhYVJmQ7cR+yg4DXGq5d6sJMgNKg47KwgYtEnQ9xyp",
+	"iFA03Tj/UhU56TZ5MFLZGWUpG0TY0m2rS4hrso3VqQoTIQlR9CnM77nFuQoTlWGKMv+efaq5TKo1PdCz",
+	"X4rl8SLq007knbbuTuLFtOhAVq+sSO2s8uXZTdYUAnUog+9Op9qFFIHKgk5QLTvmcrYqrGefqYpmt6X1",
+	"iylZTB4wLprDNLC3mxvJ+ql3O/mH7dZWuq72HLITvMwCckGqZ7PPNhXCJfeXdc7KHfhY0g5txC/B2pDV",
+	"NTk/t6yrq47z0/TrIaguOv+jvtrX3+ysEcQdba1zmHvt3kqIEvTJai0a9i+8Dy2nUxYeDSfvqby66Qjb",
+	"99Qoxk7XqiOUjWumychpSRUNB5BzAaH6njuqrVPjWDtDpa69qziR8i4SbLuJhozmPlXmvYO5EYPdVxm7",
+	"fduieP4ZkVdT10Ml16yOnJBDRN5/5DGwVZ1hHvrkDrFOC1WjhFXOl96EmxJ+ZSLp4+zPvdERygYdqi3W",
+	"LjbRq/jSrjDyUG1l9WJJDTtc25xNP+2zEC8BuC/12PzHrnaa4XZPtZcDdAptPIDTPzp85TGElexp01Ir",
+	"yMhh6Rm/Cfkbxeb2asBJedVyu975Ql4iTMZa4x3fCNquZ8IwGUP2xv34XmvV4XfFM4bpmRv+BnnV5XgV",
+	"U4bJWNq/8BjOT/31jMXMSUwMaJ4paSh+Uz5TFeWzaW+3HEBFy23Cg81y4pONZtj8SyPPeyti8CFSivue",
+	"V/xFQ6FUc30+SAifEwrZffZfAAAA///Yf5GUwCMAAA==",
 }
 
 // GetSwagger returns the content of the embedded swagger specification file
diff --git a/internal/test/parameters/parameters.yaml b/internal/test/parameters/parameters.yaml
index bd90e5f..19a8f9d 100644
--- a/internal/test/parameters/parameters.yaml
+++ b/internal/test/parameters/parameters.yaml
@@ -23,7 +23,6 @@ paths:
           $ref: "#/components/responses/SimpleResponse"
   /simpleNoExplodeArray/{param}:
     get:
-      summary:
       operationId: getSimpleNoExplodeArray
       parameters:
       - name: param
@@ -41,7 +40,6 @@ paths:
           $ref: "#/components/responses/SimpleResponse"
   /simpleExplodeArray/{param*}:
     get:
-      summary:
       operationId: getSimpleExplodeArray
       parameters:
         - name: param
@@ -59,7 +57,6 @@ paths:
           $ref: "#/components/responses/SimpleResponse"
   /simpleNoExplodeObject/{param}:
     get:
-      summary:
       operationId: getSimpleNoExplodeObject
       parameters:
         - name: param
@@ -74,7 +71,6 @@ paths:
           $ref: "#/components/responses/SimpleResponse"
   /simpleExplodeObject/{param*}:
     get:
-      summary:
       operationId: getSimpleExplodeObject
       parameters:
         - name: param
@@ -89,7 +85,6 @@ paths:
           $ref: "#/components/responses/SimpleResponse"
   /labelNoExplodeArray/{.param}:
     get:
-      summary:
       operationId: getLabelNoExplodeArray
       parameters:
         - name: param
@@ -107,7 +102,6 @@ paths:
           $ref: "#/components/responses/SimpleResponse"
   /labelExplodeArray/{.param*}:
     get:
-      summary:
       operationId: getLabelExplodeArray
       parameters:
         - name: param
@@ -125,7 +119,6 @@ paths:
           $ref: "#/components/responses/SimpleResponse"
   /labelNoExplodeObject/{.param}:
     get:
-      summary:
       operationId: getLabelNoExplodeObject
       parameters:
         - name: param
@@ -140,7 +133,6 @@ paths:
           $ref: "#/components/responses/SimpleResponse"
   /labelExplodeObject/{.param*}:
     get:
-      summary:
       operationId: getLabelExplodeObject
       parameters:
         - name: param
@@ -155,7 +147,6 @@ paths:
           $ref: "#/components/responses/SimpleResponse"
   /matrixNoExplodeArray/{.id}:
     get:
-      summary:
       operationId: getMatrixNoExplodeArray
       parameters:
         - name: id
@@ -173,7 +164,6 @@ paths:
           $ref: "#/components/responses/SimpleResponse"
   /matrixExplodeArray/{.id*}:
     get:
-      summary:
       operationId: getMatrixExplodeArray
       parameters:
         - name: id
@@ -191,7 +181,6 @@ paths:
           $ref: "#/components/responses/SimpleResponse"
   /matrixNoExplodeObject/{.id}:
     get:
-      summary:
       operationId: getMatrixNoExplodeObject
       parameters:
         - name: id
@@ -206,7 +195,6 @@ paths:
           $ref: "#/components/responses/SimpleResponse"
   /matrixExplodeObject/{.id*}:
     get:
-      summary:
       operationId: getMatrixExplodeObject
       parameters:
         - name: id
@@ -352,6 +340,9 @@ paths:
           explode: true
           schema:
             $ref: "#/components/schemas/ComplexObject"
+      responses:
+        default:
+         $ref: "#/components/responses/SimpleResponse"
   /header:
     get:
       operationId: getHeader
@@ -420,6 +411,10 @@ paths:
           required: false
           schema:
             type: string
+      responses:
+        default:
+          $ref: "#/components/responses/SimpleResponse"
+
   /cookie:
     get:
       operationId: getCookie
@@ -488,6 +483,10 @@ paths:
           required: false
           schema:
             type: string
+      responses:
+        default:
+          $ref: "#/components/responses/SimpleResponse"
+
 components:
   schemas:
     Object:
diff --git a/internal/test/schemas/doc.go b/internal/test/schemas/doc.go
index 81a0936..a794f06 100644
--- a/internal/test/schemas/doc.go
+++ b/internal/test/schemas/doc.go
@@ -1,3 +1,3 @@
 package schemas
 
-//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen --package=schemas -o schemas.gen.go schemas.yaml
+//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen --old-config-style --package=schemas -o schemas.gen.go schemas.yaml
diff --git a/internal/test/schemas/schemas.gen.go b/internal/test/schemas/schemas.gen.go
index 84ff520..e65ba86 100644
--- a/internal/test/schemas/schemas.gen.go
+++ b/internal/test/schemas/schemas.gen.go
@@ -31,27 +31,26 @@ const (
 
 // Defines values for EnumInObjInArrayVal.
 const (
-	EnumInObjInArrayValFirst EnumInObjInArrayVal = "first"
-
-	EnumInObjInArrayValSecond EnumInObjInArrayVal = "second"
+	First  EnumInObjInArrayVal = "first"
+	Second EnumInObjInArrayVal = "second"
 )
 
 // This schema name starts with a number
-type N5StartsWithNumber map[string]interface{}
+type N5StartsWithNumber = map[string]interface{}
 
 // AnyType1 defines model for AnyType1.
-type AnyType1 interface{}
+type AnyType1 = interface{}
 
 // AnyType2 represents any type.
 //
 // This should be an interface{}
-type AnyType2 interface{}
+type AnyType2 = interface{}
 
 // CustomStringType defines model for CustomStringType.
-type CustomStringType string
+type CustomStringType = string
 
 // EnumInObjInArray defines model for EnumInObjInArray.
-type EnumInObjInArray []struct {
+type EnumInObjInArray = []struct {
 	Val *EnumInObjInArrayVal `json:"val,omitempty"`
 }
 
@@ -59,7 +58,7 @@ type EnumInObjInArray []struct {
 type EnumInObjInArrayVal string
 
 // GenericObject defines model for GenericObject.
-type GenericObject map[string]interface{}
+type GenericObject = map[string]interface{}
 
 // NullableProperties defines model for NullableProperties.
 type NullableProperties struct {
@@ -70,24 +69,24 @@ type NullableProperties struct {
 }
 
 // StringInPath defines model for StringInPath.
-type StringInPath string
+type StringInPath = string
 
 // Issue185JSONBody defines parameters for Issue185.
-type Issue185JSONBody NullableProperties
+type Issue185JSONBody = NullableProperties
 
 // Issue9JSONBody defines parameters for Issue9.
-type Issue9JSONBody interface{}
+type Issue9JSONBody = interface{}
 
 // Issue9Params defines parameters for Issue9.
 type Issue9Params struct {
-	Foo string `json:"foo"`
+	Foo string `form:"foo" json:"foo"`
 }
 
 // Issue185JSONRequestBody defines body for Issue185 for application/json ContentType.
-type Issue185JSONRequestBody Issue185JSONBody
+type Issue185JSONRequestBody = Issue185JSONBody
 
 // Issue9JSONRequestBody defines body for Issue9 for application/json ContentType.
-type Issue9JSONRequestBody Issue9JSONBody
+type Issue9JSONRequestBody = Issue9JSONBody
 
 // RequestEditorFn  is the function signature for the RequestEditor callback function
 type RequestEditorFn func(ctx context.Context, req *http.Request) error
diff --git a/internal/test/server/doc.go b/internal/test/server/doc.go
index 421ca4d..f3c8230 100644
--- a/internal/test/server/doc.go
+++ b/internal/test/server/doc.go
@@ -1,4 +1,4 @@
 package server
 
-//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen --generate=types,chi-server --package=server -o server.gen.go ../test-schema.yaml
+//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen --old-config-style --generate=types,chi-server --package=server -o server.gen.go ../test-schema.yaml
 //go:generate go run github.com/matryer/moq -out server_moq.gen.go . ServerInterface
diff --git a/internal/test/server/server.gen.go b/internal/test/server/server.gen.go
index aa88169..ee11ddd 100644
--- a/internal/test/server/server.gen.go
+++ b/internal/test/server/server.gen.go
@@ -75,10 +75,10 @@ type SomeObject struct {
 }
 
 // Argument defines model for argument.
-type Argument string
+type Argument = string
 
 // ResponseWithReference defines model for ResponseWithReference.
-type ResponseWithReference SomeObject
+type ResponseWithReference = SomeObject
 
 // SimpleResponse defines model for SimpleResponse.
 type SimpleResponse struct {
@@ -88,10 +88,10 @@ type SimpleResponse struct {
 // GetWithArgsParams defines parameters for GetWithArgs.
 type GetWithArgsParams struct {
 	// An optional query argument
-	OptionalArgument *int64 `json:"optional_argument,omitempty"`
+	OptionalArgument *int64 `form:"optional_argument,omitempty" json:"optional_argument,omitempty"`
 
 	// An optional query argument
-	RequiredArgument int64 `json:"required_argument"`
+	RequiredArgument int64 `form:"required_argument" json:"required_argument"`
 
 	// An optional query argument
 	HeaderArgument *int32 `json:"header_argument,omitempty"`
@@ -101,15 +101,15 @@ type GetWithArgsParams struct {
 type GetWithContentTypeParamsContentType string
 
 // CreateResourceJSONBody defines parameters for CreateResource.
-type CreateResourceJSONBody EveryTypeRequired
+type CreateResourceJSONBody = EveryTypeRequired
 
 // CreateResource2JSONBody defines parameters for CreateResource2.
-type CreateResource2JSONBody Resource
+type CreateResource2JSONBody = Resource
 
 // CreateResource2Params defines parameters for CreateResource2.
 type CreateResource2Params struct {
 	// Some query argument
-	InlineQueryArgument *int `json:"inline_query_argument,omitempty"`
+	InlineQueryArgument *int `form:"inline_query_argument,omitempty" json:"inline_query_argument,omitempty"`
 }
 
 // UpdateResource3JSONBody defines parameters for UpdateResource3.
@@ -119,10 +119,10 @@ type UpdateResource3JSONBody struct {
 }
 
 // CreateResourceJSONRequestBody defines body for CreateResource for application/json ContentType.
-type CreateResourceJSONRequestBody CreateResourceJSONBody
+type CreateResourceJSONRequestBody = CreateResourceJSONBody
 
 // CreateResource2JSONRequestBody defines body for CreateResource2 for application/json ContentType.
-type CreateResource2JSONRequestBody CreateResource2JSONBody
+type CreateResource2JSONRequestBody = CreateResource2JSONBody
 
 // UpdateResource3JSONRequestBody defines body for UpdateResource3 for application/json ContentType.
 type UpdateResource3JSONRequestBody UpdateResource3JSONBody
@@ -276,7 +276,7 @@ func (siw *ServerInterfaceWrapper) GetWithReferences(w http.ResponseWriter, r *h
 	// ------------- Path parameter "global_argument" -------------
 	var globalArgument int64
 
-	err = runtime.BindStyledParameter("simple", false, "global_argument", chi.URLParam(r, "global_argument"), &globalArgument)
+	err = runtime.BindStyledParameterWithLocation("simple", false, "global_argument", runtime.ParamLocationPath, chi.URLParam(r, "global_argument"), &globalArgument)
 	if err != nil {
 		siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "global_argument", Err: err})
 		return
@@ -285,7 +285,7 @@ func (siw *ServerInterfaceWrapper) GetWithReferences(w http.ResponseWriter, r *h
 	// ------------- Path parameter "argument" -------------
 	var argument Argument
 
-	err = runtime.BindStyledParameter("simple", false, "argument", chi.URLParam(r, "argument"), &argument)
+	err = runtime.BindStyledParameterWithLocation("simple", false, "argument", runtime.ParamLocationPath, chi.URLParam(r, "argument"), &argument)
 	if err != nil {
 		siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "argument", Err: err})
 		return
@@ -311,7 +311,7 @@ func (siw *ServerInterfaceWrapper) GetWithContentType(w http.ResponseWriter, r *
 	// ------------- Path parameter "content_type" -------------
 	var contentType GetWithContentTypeParamsContentType
 
-	err = runtime.BindStyledParameter("simple", false, "content_type", chi.URLParam(r, "content_type"), &contentType)
+	err = runtime.BindStyledParameterWithLocation("simple", false, "content_type", runtime.ParamLocationPath, chi.URLParam(r, "content_type"), &contentType)
 	if err != nil {
 		siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "content_type", Err: err})
 		return
@@ -352,7 +352,7 @@ func (siw *ServerInterfaceWrapper) CreateResource(w http.ResponseWriter, r *http
 	// ------------- Path parameter "argument" -------------
 	var argument Argument
 
-	err = runtime.BindStyledParameter("simple", false, "argument", chi.URLParam(r, "argument"), &argument)
+	err = runtime.BindStyledParameterWithLocation("simple", false, "argument", runtime.ParamLocationPath, chi.URLParam(r, "argument"), &argument)
 	if err != nil {
 		siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "argument", Err: err})
 		return
@@ -378,7 +378,7 @@ func (siw *ServerInterfaceWrapper) CreateResource2(w http.ResponseWriter, r *htt
 	// ------------- Path parameter "inline_argument" -------------
 	var inlineArgument int
 
-	err = runtime.BindStyledParameter("simple", false, "inline_argument", chi.URLParam(r, "inline_argument"), &inlineArgument)
+	err = runtime.BindStyledParameterWithLocation("simple", false, "inline_argument", runtime.ParamLocationPath, chi.URLParam(r, "inline_argument"), &inlineArgument)
 	if err != nil {
 		siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "inline_argument", Err: err})
 		return
@@ -418,7 +418,7 @@ func (siw *ServerInterfaceWrapper) UpdateResource3(w http.ResponseWriter, r *htt
 	// ------------- Path parameter "fallthrough" -------------
 	var pFallthrough int
 
-	err = runtime.BindStyledParameter("simple", false, "fallthrough", chi.URLParam(r, "fallthrough"), &pFallthrough)
+	err = runtime.BindStyledParameterWithLocation("simple", false, "fallthrough", runtime.ParamLocationPath, chi.URLParam(r, "fallthrough"), &pFallthrough)
 	if err != nil {
 		siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "fallthrough", Err: err})
 		return
diff --git a/internal/test/server/server_moq.gen.go b/internal/test/server/server_moq.gen.go
index b4dde63..aa22e65 100644
--- a/internal/test/server/server_moq.gen.go
+++ b/internal/test/server/server_moq.gen.go
@@ -18,7 +18,7 @@ var _ ServerInterface = &ServerInterfaceMock{}
 //
 // 		// make and configure a mocked ServerInterface
 // 		mockedServerInterface := &ServerInterfaceMock{
-// 			CreateResourceFunc: func(w http.ResponseWriter, r *http.Request, argument Argument)  {
+// 			CreateResourceFunc: func(w http.ResponseWriter, r *http.Request, argument string)  {
 // 				panic("mock out the CreateResource method")
 // 			},
 // 			CreateResource2Func: func(w http.ResponseWriter, r *http.Request, inlineArgument int, params CreateResource2Params)  {
@@ -42,7 +42,7 @@ var _ ServerInterface = &ServerInterfaceMock{}
 // 			GetWithContentTypeFunc: func(w http.ResponseWriter, r *http.Request, contentType GetWithContentTypeParamsContentType)  {
 // 				panic("mock out the GetWithContentType method")
 // 			},
-// 			GetWithReferencesFunc: func(w http.ResponseWriter, r *http.Request, globalArgument int64, argument Argument)  {
+// 			GetWithReferencesFunc: func(w http.ResponseWriter, r *http.Request, globalArgument int64, argument string)  {
 // 				panic("mock out the GetWithReferences method")
 // 			},
 // 			UpdateResource3Func: func(w http.ResponseWriter, r *http.Request, pFallthrough int)  {
@@ -56,7 +56,7 @@ var _ ServerInterface = &ServerInterfaceMock{}
 // 	}
 type ServerInterfaceMock struct {
 	// CreateResourceFunc mocks the CreateResource method.
-	CreateResourceFunc func(w http.ResponseWriter, r *http.Request, argument Argument)
+	CreateResourceFunc func(w http.ResponseWriter, r *http.Request, argument string)
 
 	// CreateResource2Func mocks the CreateResource2 method.
 	CreateResource2Func func(w http.ResponseWriter, r *http.Request, inlineArgument int, params CreateResource2Params)
@@ -80,7 +80,7 @@ type ServerInterfaceMock struct {
 	GetWithContentTypeFunc func(w http.ResponseWriter, r *http.Request, contentType GetWithContentTypeParamsContentType)
 
 	// GetWithReferencesFunc mocks the GetWithReferences method.
-	GetWithReferencesFunc func(w http.ResponseWriter, r *http.Request, globalArgument int64, argument Argument)
+	GetWithReferencesFunc func(w http.ResponseWriter, r *http.Request, globalArgument int64, argument string)
 
 	// UpdateResource3Func mocks the UpdateResource3 method.
 	UpdateResource3Func func(w http.ResponseWriter, r *http.Request, pFallthrough int)
@@ -94,7 +94,7 @@ type ServerInterfaceMock struct {
 			// R is the r argument value.
 			R *http.Request
 			// Argument is the argument argument value.
-			Argument Argument
+			Argument string
 		}
 		// CreateResource2 holds details about calls to the CreateResource2 method.
 		CreateResource2 []struct {
@@ -162,7 +162,7 @@ type ServerInterfaceMock struct {
 			// GlobalArgument is the globalArgument argument value.
 			GlobalArgument int64
 			// Argument is the argument argument value.
-			Argument Argument
+			Argument string
 		}
 		// UpdateResource3 holds details about calls to the UpdateResource3 method.
 		UpdateResource3 []struct {
@@ -187,14 +187,14 @@ type ServerInterfaceMock struct {
 }
 
 // CreateResource calls CreateResourceFunc.
-func (mock *ServerInterfaceMock) CreateResource(w http.ResponseWriter, r *http.Request, argument Argument) {
+func (mock *ServerInterfaceMock) CreateResource(w http.ResponseWriter, r *http.Request, argument string) {
 	if mock.CreateResourceFunc == nil {
 		panic("ServerInterfaceMock.CreateResourceFunc: method is nil but ServerInterface.CreateResource was just called")
 	}
 	callInfo := struct {
 		W        http.ResponseWriter
 		R        *http.Request
-		Argument Argument
+		Argument string
 	}{
 		W:        w,
 		R:        r,
@@ -212,12 +212,12 @@ func (mock *ServerInterfaceMock) CreateResource(w http.ResponseWriter, r *http.R
 func (mock *ServerInterfaceMock) CreateResourceCalls() []struct {
 	W        http.ResponseWriter
 	R        *http.Request
-	Argument Argument
+	Argument string
 } {
 	var calls []struct {
 		W        http.ResponseWriter
 		R        *http.Request
-		Argument Argument
+		Argument string
 	}
 	mock.lockCreateResource.RLock()
 	calls = mock.calls.CreateResource
@@ -487,7 +487,7 @@ func (mock *ServerInterfaceMock) GetWithContentTypeCalls() []struct {
 }
 
 // GetWithReferences calls GetWithReferencesFunc.
-func (mock *ServerInterfaceMock) GetWithReferences(w http.ResponseWriter, r *http.Request, globalArgument int64, argument Argument) {
+func (mock *ServerInterfaceMock) GetWithReferences(w http.ResponseWriter, r *http.Request, globalArgument int64, argument string) {
 	if mock.GetWithReferencesFunc == nil {
 		panic("ServerInterfaceMock.GetWithReferencesFunc: method is nil but ServerInterface.GetWithReferences was just called")
 	}
@@ -495,7 +495,7 @@ func (mock *ServerInterfaceMock) GetWithReferences(w http.ResponseWriter, r *htt
 		W              http.ResponseWriter
 		R              *http.Request
 		GlobalArgument int64
-		Argument       Argument
+		Argument       string
 	}{
 		W:              w,
 		R:              r,
@@ -515,13 +515,13 @@ func (mock *ServerInterfaceMock) GetWithReferencesCalls() []struct {
 	W              http.ResponseWriter
 	R              *http.Request
 	GlobalArgument int64
-	Argument       Argument
+	Argument       string
 } {
 	var calls []struct {
 		W              http.ResponseWriter
 		R              *http.Request
 		GlobalArgument int64
-		Argument       Argument
+		Argument       string
 	}
 	mock.lockGetWithReferences.RLock()
 	calls = mock.calls.GetWithReferences
diff --git a/pkg/chi-middleware/oapi_validate_test.go b/pkg/chi-middleware/oapi_validate_test.go
index b67d5f3..8df1a2e 100644
--- a/pkg/chi-middleware/oapi_validate_test.go
+++ b/pkg/chi-middleware/oapi_validate_test.go
@@ -2,95 +2,24 @@ package middleware
 
 import (
 	"context"
+	_ "embed"
 	"errors"
 	"net/http"
 	"net/http/httptest"
 	"net/url"
 	"testing"
 
-	"github.com/deepmap/oapi-codegen/pkg/testutil"
 	"github.com/getkin/kin-openapi/openapi3"
 	"github.com/getkin/kin-openapi/openapi3filter"
 	"github.com/go-chi/chi/v5"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
+
+	"github.com/deepmap/oapi-codegen/pkg/testutil"
 )
 
-var testSchema = `openapi: "3.0.0"
-info:
-  version: 1.0.0
-  title: TestServer
-servers:
-  - url: http://deepmap.ai/
-paths:
-  /resource:
-    get:
-      operationId: getResource
-      parameters:
-        - name: id
-          in: query
-          schema:
-            type: integer
-            minimum: 10
-            maximum: 100
-      responses:
-        '200':
-            description: success
-            content:
-              application/json:
-                schema:
-                  properties:
-                    name:
-                      type: string
-                    id:
-                      type: integer
-    post:
-      operationId: createResource
-      responses:
-        '204':
-          description: No content
-      requestBody:
-        required: true
-        content:
-          application/json:
-            schema:
-              properties:
-                name:
-                  type: string
-  /protected_resource:
-    get:
-      operationId: getProtectedResource
-      security:
-        - BearerAuth:
-          - someScope
-      responses:
-        '204':
-          description: no content
-  /protected_resource2:
-    get:
-      operationId: getProtectedResource
-      security:
-        - BearerAuth:
-          - otherScope
-      responses:
-        '204':
-          description: no content
-  /protected_resource_401:
-    get:
-      operationId: getProtectedResource
-      security:
-        - BearerAuth:
-          - unauthorized
-      responses:
-        '401':
-          description: no content
-components:
-  securitySchemes:
-    BearerAuth:
-      type: http
-      scheme: bearer
-      bearerFormat: JWT
-`
+//go:embed test_spec.yaml
+var testSchema []byte
 
 func doGet(t *testing.T, mux *chi.Mux, rawURL string) *httptest.ResponseRecorder {
 	u, err := url.Parse(rawURL)
@@ -113,7 +42,7 @@ func doPost(t *testing.T, mux *chi.Mux, rawURL string, jsonBody interface{}) *ht
 }
 
 func TestOapiRequestValidator(t *testing.T) {
-	swagger, err := openapi3.NewLoader().LoadFromData([]byte(testSchema))
+	swagger, err := openapi3.NewLoader().LoadFromData(testSchema)
 	require.NoError(t, err, "Error initializing swagger")
 
 	r := chi.NewRouter()
@@ -134,6 +63,9 @@ func TestOapiRequestValidatorWithOptions(t *testing.T) {
 	// Set up an authenticator to check authenticated function. It will allow
 	// access to "someScope", but disallow others.
 	options := Options{
+		ErrorHandler: func(w http.ResponseWriter, message string, statusCode int) {
+			http.Error(w, "test: "+message, statusCode)
+		},
 		Options: openapi3filter.Options{
 			AuthenticationFunc: func(c context.Context, input *openapi3filter.AuthenticationInput) error {
 
@@ -188,6 +120,7 @@ func TestOapiRequestValidatorWithOptions(t *testing.T) {
 	{
 		rec := doGet(t, r, "http://deepmap.ai/protected_resource_401")
 		assert.Equal(t, http.StatusUnauthorized, rec.Code)
+		assert.Equal(t, "test: Security requirements failed\n", rec.Body.String())
 		assert.False(t, called, "Handler should not have been called")
 		called = false
 	}
diff --git a/pkg/chi-middleware/test_spec.yaml b/pkg/chi-middleware/test_spec.yaml
new file mode 100644
index 0000000..f2ce175
--- /dev/null
+++ b/pkg/chi-middleware/test_spec.yaml
@@ -0,0 +1,74 @@
+openapi: "3.0.0"
+info:
+  version: 1.0.0
+  title: TestServer
+servers:
+  - url: http://deepmap.ai/
+paths:
+  /resource:
+    get:
+      operationId: getResource
+      parameters:
+        - name: id
+          in: query
+          schema:
+            type: integer
+            minimum: 10
+            maximum: 100
+      responses:
+        '200':
+          description: success
+          content:
+            application/json:
+              schema:
+                properties:
+                  name:
+                    type: string
+                  id:
+                    type: integer
+    post:
+      operationId: createResource
+      responses:
+        '204':
+          description: No content
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              properties:
+                name:
+                  type: string
+  /protected_resource:
+    get:
+      operationId: getProtectedResource
+      security:
+        - BearerAuth:
+            - someScope
+      responses:
+        '204':
+          description: no content
+  /protected_resource2:
+    get:
+      operationId: getProtectedResource
+      security:
+        - BearerAuth:
+            - otherScope
+      responses:
+        '204':
+          description: no content
+  /protected_resource_401:
+    get:
+      operationId: getProtectedResource
+      security:
+        - BearerAuth:
+            - unauthorized
+      responses:
+        '401':
+          description: no content
+components:
+  securitySchemes:
+    BearerAuth:
+      type: http
+      scheme: bearer
+      bearerFormat: JWT
diff --git a/pkg/codegen/codegen_test.go b/pkg/codegen/codegen_test.go
index 2e02d0b..8d333a6 100644
--- a/pkg/codegen/codegen_test.go
+++ b/pkg/codegen/codegen_test.go
@@ -2,27 +2,32 @@ package codegen
 
 import (
 	"bytes"
+	_ "embed"
 	"go/format"
 	"io/ioutil"
 	"net/http"
 	"testing"
 
-	examplePetstoreClient "github.com/deepmap/oapi-codegen/examples/petstore-expanded"
-	examplePetstore "github.com/deepmap/oapi-codegen/examples/petstore-expanded/echo/api"
 	"github.com/getkin/kin-openapi/openapi3"
 	"github.com/golangci/lint-1"
 	"github.com/stretchr/testify/assert"
+
+	examplePetstoreClient "github.com/deepmap/oapi-codegen/examples/petstore-expanded"
+	examplePetstore "github.com/deepmap/oapi-codegen/examples/petstore-expanded/echo/api"
 )
 
 func TestExamplePetStoreCodeGeneration(t *testing.T) {
 
 	// Input vars for code generation:
 	packageName := "api"
-	opts := Options{
-		GenerateClient:     true,
-		GenerateEchoServer: true,
-		GenerateTypes:      true,
-		EmbedSpec:          true,
+	opts := Configuration{
+		PackageName: packageName,
+		Generate: GenerateOptions{
+			EchoServer:   true,
+			Client:       true,
+			Models:       true,
+			EmbeddedSpec: true,
+		},
 	}
 
 	// Get a spec from the example PetStore definition:
@@ -30,7 +35,7 @@ func TestExamplePetStoreCodeGeneration(t *testing.T) {
 	assert.NoError(t, err)
 
 	// Run our code generation:
-	code, err := Generate(swagger, packageName, opts)
+	code, err := Generate(swagger, opts)
 	assert.NoError(t, err)
 	assert.NotEmpty(t, code)
 
@@ -65,9 +70,14 @@ func TestExamplePetStoreCodeGenerationWithUserTemplates(t *testing.T) {
 
 	// Input vars for code generation:
 	packageName := "api"
-	opts := Options{
-		GenerateTypes: true,
-		UserTemplates: userTemplates,
+	opts := Configuration{
+		PackageName: packageName,
+		Generate: GenerateOptions{
+			Models: true,
+		},
+		OutputOptions: OutputOptions{
+			UserTemplates: userTemplates,
+		},
 	}
 
 	// Get a spec from the example PetStore definition:
@@ -75,7 +85,7 @@ func TestExamplePetStoreCodeGenerationWithUserTemplates(t *testing.T) {
 	assert.NoError(t, err)
 
 	// Run our code generation:
-	code, err := Generate(swagger, packageName, opts)
+	code, err := Generate(swagger, opts)
 	assert.NoError(t, err)
 	assert.NotEmpty(t, code)
 
@@ -114,11 +124,14 @@ func TestExampleOpenAPICodeGeneration(t *testing.T) {
 
 	// Input vars for code generation:
 	packageName := "testswagger"
-	opts := Options{
-		GenerateClient:     true,
-		GenerateEchoServer: true,
-		GenerateTypes:      true,
-		EmbedSpec:          true,
+	opts := Configuration{
+		PackageName: packageName,
+		Generate: GenerateOptions{
+			EchoServer:   true,
+			Client:       true,
+			Models:       true,
+			EmbeddedSpec: true,
+		},
 	}
 
 	// Get a spec from the test definition in this file:
@@ -126,7 +139,7 @@ func TestExampleOpenAPICodeGeneration(t *testing.T) {
 	assert.NoError(t, err)
 
 	// Run our code generation:
-	code, err := Generate(swagger, packageName, opts)
+	code, err := Generate(swagger, opts)
 	assert.NoError(t, err)
 	assert.NotEmpty(t, code)
 
@@ -160,7 +173,7 @@ type GetTestByNameResponse struct {
 
 	// Check the client method signatures:
 	assert.Contains(t, code, "type GetTestByNameParams struct {")
-	assert.Contains(t, code, "Top *int `json:\"$top,omitempty\"`")
+	assert.Contains(t, code, "Top *int `form:\"$top,omitempty\" json:\"$top,omitempty\"`")
 	assert.Contains(t, code, "func (c *Client) GetTestByName(ctx context.Context, name string, params *GetTestByNameParams, reqEditors ...RequestEditorFn) (*http.Response, error) {")
 	assert.Contains(t, code, "func (c *ClientWithResponses) GetTestByNameWithResponse(ctx context.Context, name string, params *GetTestByNameParams, reqEditors ...RequestEditorFn) (*GetTestByNameResponse, error) {")
 	assert.Contains(t, code, "DeadSince *time.Time    `json:\"dead_since,omitempty\" tag1:\"value1\" tag2:\"value2\"`")
@@ -172,143 +185,5 @@ type GetTestByNameResponse struct {
 	assert.Len(t, problems, 0)
 }
 
-const testOpenAPIDefinition = `
-openapi: 3.0.1
-
-info:
-  title: OpenAPI-CodeGen Test
-  description: 'This is a test OpenAPI Spec'
-  version: 1.0.0
-
-servers:
-- url: https://test.oapi-codegen.com/v2
-- url: http://test.oapi-codegen.com/v2
-
-paths:
-  /test/{name}:
-    get:
-      tags:
-      - test
-      summary: Get test
-      operationId: getTestByName
-      parameters:
-      - name: name
-        in: path
-        required: true
-        schema:
-          type: string
-      - name: $top
-        in: query
-        required: false
-        schema:
-          type: integer
-      responses:
-        200:
-          description: Success
-          content:
-            application/xml:
-              schema:
-                type: array
-                items:
-                  $ref: '#/components/schemas/Test'
-            application/json:
-              schema:
-                type: array
-                items:
-                  $ref: '#/components/schemas/Test'
-        422:
-          description: InvalidArray
-          content:
-            application/xml:
-              schema:
-                type: array
-            application/json:
-              schema:
-                type: array
-        default:
-          description: Error
-          content:
-            application/json:
-              schema:
-                $ref: '#/components/schemas/Error'
-  /cat:
-    get:
-      tags:
-      - cat
-      summary: Get cat status
-      operationId: getCatStatus
-      responses:
-        200:
-          description: Success
-          content:
-            application/json:
-              schema:
-                oneOf:
-                - $ref: '#/components/schemas/CatAlive'
-                - $ref: '#/components/schemas/CatDead'
-            application/xml:
-              schema:
-                anyOf:
-                - $ref: '#/components/schemas/CatAlive'
-                - $ref: '#/components/schemas/CatDead'
-            application/yaml:
-              schema:
-                allOf:
-                - $ref: '#/components/schemas/CatAlive'
-                - $ref: '#/components/schemas/CatDead'
-        default:
-          description: Error
-          content:
-            application/json:
-              schema:
-                $ref: '#/components/schemas/Error'
-
-components:
-  schemas:
-
-    Test:
-      properties:
-        name:
-          type: string
-        cases:
-          type: array
-          items:
-            $ref: '#/components/schemas/TestCase'
-
-    TestCase:
-      properties:
-        name:
-          type: string
-        command:
-          type: string
-
-    Error:
-      properties:
-        code:
-          type: integer
-          format: int32
-        message:
-          type: string
-
-    CatAlive:
-      properties:
-        name:
-          type: string
-        alive_since:
-          type: string
-          format: date-time
-
-    CatDead:
-      properties:
-        name:
-          type: string
-        dead_since:
-          type: string
-          format: date-time
-          x-oapi-codegen-extra-tags:
-            tag1: value1
-            tag2: value2
-        cause:
-          type: string
-          enum: [car, dog, oldage]
-`
+//go:embed test_spec.yaml
+var testOpenAPIDefinition string
diff --git a/pkg/codegen/filter_test.go b/pkg/codegen/filter_test.go
index 1342493..d3a5aec 100644
--- a/pkg/codegen/filter_test.go
+++ b/pkg/codegen/filter_test.go
@@ -10,12 +10,17 @@ import (
 func TestFilterOperationsByTag(t *testing.T) {
 	packageName := "testswagger"
 	t.Run("include tags", func(t *testing.T) {
-		opts := Options{
-			GenerateClient:     true,
-			GenerateEchoServer: true,
-			GenerateTypes:      true,
-			EmbedSpec:          true,
-			IncludeTags:        []string{"hippo", "giraffe", "cat"},
+		opts := Configuration{
+			PackageName: packageName,
+			Generate: GenerateOptions{
+				EchoServer:   true,
+				Client:       true,
+				Models:       true,
+				EmbeddedSpec: true,
+			},
+			OutputOptions: OutputOptions{
+				IncludeTags: []string{"hippo", "giraffe", "cat"},
+			},
 		}
 
 		// Get a spec from the test definition in this file:
@@ -23,7 +28,7 @@ func TestFilterOperationsByTag(t *testing.T) {
 		assert.NoError(t, err)
 
 		// Run our code generation:
-		code, err := Generate(swagger, packageName, opts)
+		code, err := Generate(swagger, opts)
 		assert.NoError(t, err)
 		assert.NotEmpty(t, code)
 		assert.NotContains(t, code, `"/test/:name"`)
@@ -31,12 +36,17 @@ func TestFilterOperationsByTag(t *testing.T) {
 	})
 
 	t.Run("exclude tags", func(t *testing.T) {
-		opts := Options{
-			GenerateClient:     true,
-			GenerateEchoServer: true,
-			GenerateTypes:      true,
-			EmbedSpec:          true,
-			ExcludeTags:        []string{"hippo", "giraffe", "cat"},
+		opts := Configuration{
+			PackageName: packageName,
+			Generate: GenerateOptions{
+				EchoServer:   true,
+				Client:       true,
+				Models:       true,
+				EmbeddedSpec: true,
+			},
+			OutputOptions: OutputOptions{
+				ExcludeTags: []string{"hippo", "giraffe", "cat"},
+			},
 		}
 
 		// Get a spec from the test definition in this file:
@@ -44,7 +54,7 @@ func TestFilterOperationsByTag(t *testing.T) {
 		assert.NoError(t, err)
 
 		// Run our code generation:
-		code, err := Generate(swagger, packageName, opts)
+		code, err := Generate(swagger, opts)
 		assert.NoError(t, err)
 		assert.NotEmpty(t, code)
 		assert.Contains(t, code, `"/test/:name"`)
diff --git a/pkg/codegen/prune_test.go b/pkg/codegen/prune_test.go
index 86437c2..e0cc16c 100644
--- a/pkg/codegen/prune_test.go
+++ b/pkg/codegen/prune_test.go
@@ -18,8 +18,10 @@ func TestFindReferences(t *testing.T) {
 	t.Run("only cat", func(t *testing.T) {
 		swagger, err := openapi3.NewLoader().LoadFromData([]byte(pruneSpecTestFixture))
 		assert.NoError(t, err)
-		opts := Options{
-			IncludeTags: []string{"cat"},
+		opts := Configuration{
+			OutputOptions: OutputOptions{
+				IncludeTags: []string{"cat"},
+			},
 		}
 
 		filterOperationsByTag(swagger, opts)
@@ -31,8 +33,10 @@ func TestFindReferences(t *testing.T) {
 		swagger, err := openapi3.NewLoader().LoadFromData([]byte(pruneSpecTestFixture))
 		assert.NoError(t, err)
 
-		opts := Options{
-			IncludeTags: []string{"dog"},
+		opts := Configuration{
+			OutputOptions: OutputOptions{
+				IncludeTags: []string{"dog"},
+			},
 		}
 
 		filterOperationsByTag(swagger, opts)
@@ -47,8 +51,10 @@ func TestFilterOnlyCat(t *testing.T) {
 	swagger, err := openapi3.NewLoader().LoadFromData([]byte(pruneSpecTestFixture))
 	assert.NoError(t, err)
 
-	opts := Options{
-		IncludeTags: []string{"cat"},
+	opts := Configuration{
+		OutputOptions: OutputOptions{
+			IncludeTags: []string{"cat"},
+		},
 	}
 
 	refs := findComponentRefs(swagger)
@@ -75,8 +81,10 @@ func TestFilterOnlyDog(t *testing.T) {
 	swagger, err := openapi3.NewLoader().LoadFromData([]byte(pruneSpecTestFixture))
 	assert.NoError(t, err)
 
-	opts := Options{
-		IncludeTags: []string{"dog"},
+	opts := Configuration{
+		OutputOptions: OutputOptions{
+			IncludeTags: []string{"dog"},
+		},
 	}
 
 	refs := findComponentRefs(swagger)
diff --git a/pkg/codegen/test_spec.yaml b/pkg/codegen/test_spec.yaml
new file mode 100644
index 0000000..b6903e5
--- /dev/null
+++ b/pkg/codegen/test_spec.yaml
@@ -0,0 +1,138 @@
+openapi: 3.0.1
+
+info:
+  title: OpenAPI-CodeGen Test
+  description: 'This is a test OpenAPI Spec'
+  version: 1.0.0
+
+servers:
+  - url: https://test.oapi-codegen.com/v2
+  - url: http://test.oapi-codegen.com/v2
+
+paths:
+  /test/{name}:
+    get:
+      tags:
+        - test
+      summary: Get test
+      operationId: getTestByName
+      parameters:
+        - name: name
+          in: path
+          required: true
+          schema:
+            type: string
+        - name: $top
+          in: query
+          required: false
+          schema:
+            type: integer
+      responses:
+        200:
+          description: Success
+          content:
+            application/xml:
+              schema:
+                type: array
+                items:
+                  $ref: '#/components/schemas/Test'
+            application/json:
+              schema:
+                type: array
+                items:
+                  $ref: '#/components/schemas/Test'
+        422:
+          description: InvalidArray
+          content:
+            application/xml:
+              schema:
+                type: array
+            application/json:
+              schema:
+                type: array
+        default:
+          description: Error
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
+  /cat:
+    get:
+      tags:
+        - cat
+      summary: Get cat status
+      operationId: getCatStatus
+      responses:
+        200:
+          description: Success
+          content:
+            application/json:
+              schema:
+                oneOf:
+                  - $ref: '#/components/schemas/CatAlive'
+                  - $ref: '#/components/schemas/CatDead'
+            application/xml:
+              schema:
+                anyOf:
+                  - $ref: '#/components/schemas/CatAlive'
+                  - $ref: '#/components/schemas/CatDead'
+            application/yaml:
+              schema:
+                allOf:
+                  - $ref: '#/components/schemas/CatAlive'
+                  - $ref: '#/components/schemas/CatDead'
+        default:
+          description: Error
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
+
+components:
+  schemas:
+
+    Test:
+      properties:
+        name:
+          type: string
+        cases:
+          type: array
+          items:
+            $ref: '#/components/schemas/TestCase'
+
+    TestCase:
+      properties:
+        name:
+          type: string
+        command:
+          type: string
+
+    Error:
+      properties:
+        code:
+          type: integer
+          format: int32
+        message:
+          type: string
+
+    CatAlive:
+      properties:
+        name:
+          type: string
+        alive_since:
+          type: string
+          format: date-time
+
+    CatDead:
+      properties:
+        name:
+          type: string
+        dead_since:
+          type: string
+          format: date-time
+          x-oapi-codegen-extra-tags:
+            tag1: value1
+            tag2: value2
+        cause:
+          type: string
+          enum: [car, dog, oldage]
diff --git a/pkg/gin-middleware/oapi_validate_test.go b/pkg/gin-middleware/oapi_validate_test.go
index de194ce..7373071 100644
--- a/pkg/gin-middleware/oapi_validate_test.go
+++ b/pkg/gin-middleware/oapi_validate_test.go
@@ -16,95 +16,24 @@ package middleware
 
 import (
 	"context"
+	_ "embed"
 	"errors"
 	"net/http"
 	"net/http/httptest"
 	"net/url"
 	"testing"
 
-	"github.com/deepmap/oapi-codegen/pkg/testutil"
 	"github.com/getkin/kin-openapi/openapi3"
 	"github.com/getkin/kin-openapi/openapi3filter"
 	"github.com/gin-gonic/gin"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
+
+	"github.com/deepmap/oapi-codegen/pkg/testutil"
 )
 
-var testSchema = `openapi: "3.0.0"
-info:
-  version: 1.0.0
-  title: TestServer
-servers:
-  - url: http://deepmap.ai/
-paths:
-  /resource:
-    get:
-      operationId: getResource
-      parameters:
-        - name: id
-          in: query
-          schema:
-            type: integer
-            minimum: 10
-            maximum: 100
-      responses:
-        '200':
-            description: success
-            content:
-              application/json:
-                schema:
-                  properties:
-                    name:
-                      type: string
-                    id:
-                      type: integer
-    post:
-      operationId: createResource
-      responses:
-        '204':
-          description: No content
-      requestBody:
-        required: true
-        content:
-          application/json:
-            schema:
-              properties:
-                name:
-                  type: string
-  /protected_resource:
-    get:
-      operationId: getProtectedResource
-      security:
-        - BearerAuth:
-          - someScope
-      responses:
-        '204':
-          description: no content
-  /protected_resource2:
-    get:
-      operationId: getProtectedResource
-      security:
-        - BearerAuth:
-          - otherScope
-      responses:
-        '204':
-          description: no content
-  /protected_resource_401:
-    get:
-      operationId: getProtectedResource
-      security:
-        - BearerAuth:
-          - unauthorized
-      responses:
-        '401':
-          description: no content
-components:
-  securitySchemes:
-    BearerAuth:
-      type: http
-      scheme: bearer
-      bearerFormat: JWT
-`
+//go:embed test_spec.yaml
+var testSchema []byte
 
 func doGet(t *testing.T, handler http.Handler, rawURL string) *httptest.ResponseRecorder {
 	u, err := url.Parse(rawURL)
@@ -127,7 +56,7 @@ func doPost(t *testing.T, handler http.Handler, rawURL string, jsonBody interfac
 }
 
 func TestOapiRequestValidator(t *testing.T) {
-	swagger, err := openapi3.NewLoader().LoadFromData([]byte(testSchema))
+	swagger, err := openapi3.NewLoader().LoadFromData(testSchema)
 	require.NoError(t, err, "Error initializing swagger")
 
 	// Create a new echo router
@@ -136,6 +65,9 @@ func TestOapiRequestValidator(t *testing.T) {
 	// Set up an authenticator to check authenticated function. It will allow
 	// access to "someScope", but disallow others.
 	options := Options{
+		ErrorHandler: func(c *gin.Context, message string, statusCode int) {
+			c.String(statusCode, "test: "+message)
+		},
 		Options: openapi3filter.Options{
 			AuthenticationFunc: func(c context.Context, input *openapi3filter.AuthenticationInput) error {
 				// The gin context should be propagated into here.
@@ -265,6 +197,7 @@ func TestOapiRequestValidator(t *testing.T) {
 	{
 		rec := doGet(t, g, "http://deepmap.ai/protected_resource_401")
 		assert.Equal(t, http.StatusBadRequest, rec.Code)
+		assert.Equal(t, "test: error in openapi3filter.SecurityRequirementsError: Security requirements failed", rec.Body.String())
 		assert.False(t, called, "Handler should not have been called")
 		called = false
 	}
diff --git a/pkg/gin-middleware/test_spec.yaml b/pkg/gin-middleware/test_spec.yaml
new file mode 100644
index 0000000..f2ce175
--- /dev/null
+++ b/pkg/gin-middleware/test_spec.yaml
@@ -0,0 +1,74 @@
+openapi: "3.0.0"
+info:
+  version: 1.0.0
+  title: TestServer
+servers:
+  - url: http://deepmap.ai/
+paths:
+  /resource:
+    get:
+      operationId: getResource
+      parameters:
+        - name: id
+          in: query
+          schema:
+            type: integer
+            minimum: 10
+            maximum: 100
+      responses:
+        '200':
+          description: success
+          content:
+            application/json:
+              schema:
+                properties:
+                  name:
+                    type: string
+                  id:
+                    type: integer
+    post:
+      operationId: createResource
+      responses:
+        '204':
+          description: No content
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              properties:
+                name:
+                  type: string
+  /protected_resource:
+    get:
+      operationId: getProtectedResource
+      security:
+        - BearerAuth:
+            - someScope
+      responses:
+        '204':
+          description: no content
+  /protected_resource2:
+    get:
+      operationId: getProtectedResource
+      security:
+        - BearerAuth:
+            - otherScope
+      responses:
+        '204':
+          description: no content
+  /protected_resource_401:
+    get:
+      operationId: getProtectedResource
+      security:
+        - BearerAuth:
+            - unauthorized
+      responses:
+        '401':
+          description: no content
+components:
+  securitySchemes:
+    BearerAuth:
+      type: http
+      scheme: bearer
+      bearerFormat: JWT
diff --git a/pkg/middleware/oapi_validate_test.go b/pkg/middleware/oapi_validate_test.go
index 726f6f9..5c3989b 100644
--- a/pkg/middleware/oapi_validate_test.go
+++ b/pkg/middleware/oapi_validate_test.go
@@ -16,96 +16,25 @@ package middleware
 
 import (
 	"context"
+	_ "embed"
 	"errors"
 	"net/http"
 	"net/http/httptest"
 	"net/url"
 	"testing"
 
-	"github.com/deepmap/oapi-codegen/pkg/testutil"
 	"github.com/getkin/kin-openapi/openapi3"
 	"github.com/getkin/kin-openapi/openapi3filter"
 	"github.com/labstack/echo/v4"
 	echomiddleware "github.com/labstack/echo/v4/middleware"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
+
+	"github.com/deepmap/oapi-codegen/pkg/testutil"
 )
 
-var testSchema = `openapi: "3.0.0"
-info:
-  version: 1.0.0
-  title: TestServer
-servers:
-  - url: http://deepmap.ai
-paths:
-  /resource:
-    get:
-      operationId: getResource
-      parameters:
-        - name: id
-          in: query
-          schema:
-            type: integer
-            minimum: 10
-            maximum: 100
-      responses:
-        '200':
-            description: success
-            content:
-              application/json:
-                schema:
-                  properties:
-                    name:
-                      type: string
-                    id:
-                      type: integer
-    post:
-      operationId: createResource
-      responses:
-        '204':
-          description: No content
-      requestBody:
-        required: true
-        content:
-          application/json:
-            schema:
-              properties:
-                name:
-                  type: string
-  /protected_resource:
-    get:
-      operationId: getProtectedResource
-      security:
-        - BearerAuth:
-          - someScope
-      responses:
-        '204':
-          description: no content
-  /protected_resource2:
-    get:
-      operationId: getProtectedResource
-      security:
-        - BearerAuth:
-          - otherScope
-      responses:
-        '204':
-          description: no content
-  /protected_resource_401:
-    get:
-      operationId: getProtectedResource
-      security:
-        - BearerAuth:
-          - unauthorized
-      responses:
-        '401':
-          description: no content
-components:
-  securitySchemes:
-    BearerAuth:
-      type: http
-      scheme: bearer
-      bearerFormat: JWT
-`
+//go:embed test_spec.yaml
+var testSchema []byte
 
 func doGet(t *testing.T, e *echo.Echo, rawURL string) *httptest.ResponseRecorder {
 	u, err := url.Parse(rawURL)
@@ -128,7 +57,7 @@ func doPost(t *testing.T, e *echo.Echo, rawURL string, jsonBody interface{}) *ht
 }
 
 func TestOapiRequestValidator(t *testing.T) {
-	swagger, err := openapi3.NewLoader().LoadFromData([]byte(testSchema))
+	swagger, err := openapi3.NewLoader().LoadFromData(testSchema)
 	require.NoError(t, err, "Error initializing swagger")
 
 	// Create a new echo router
@@ -137,6 +66,9 @@ func TestOapiRequestValidator(t *testing.T) {
 	// Set up an authenticator to check authenticated function. It will allow
 	// access to "someScope", but disallow others.
 	options := Options{
+		ErrorHandler: func(c echo.Context, err *echo.HTTPError) error {
+			return c.String(err.Code, "test: "+err.Error())
+		},
 		Options: openapi3filter.Options{
 			AuthenticationFunc: func(c context.Context, input *openapi3filter.AuthenticationInput) error {
 				// The echo context should be propagated into here.
@@ -268,6 +200,7 @@ func TestOapiRequestValidator(t *testing.T) {
 	{
 		rec := doGet(t, e, "http://deepmap.ai/protected_resource_401")
 		assert.Equal(t, http.StatusUnauthorized, rec.Code)
+		assert.Equal(t, "test: code=401, message=Unauthorized", rec.Body.String())
 		assert.False(t, called, "Handler should not have been called")
 		called = false
 	}
diff --git a/pkg/middleware/test_spec.yaml b/pkg/middleware/test_spec.yaml
new file mode 100644
index 0000000..a279068
--- /dev/null
+++ b/pkg/middleware/test_spec.yaml
@@ -0,0 +1,74 @@
+openapi: "3.0.0"
+info:
+  version: 1.0.0
+  title: TestServer
+servers:
+  - url: http://deepmap.ai
+paths:
+  /resource:
+    get:
+      operationId: getResource
+      parameters:
+        - name: id
+          in: query
+          schema:
+            type: integer
+            minimum: 10
+            maximum: 100
+      responses:
+        '200':
+          description: success
+          content:
+            application/json:
+              schema:
+                properties:
+                  name:
+                    type: string
+                  id:
+                    type: integer
+    post:
+      operationId: createResource
+      responses:
+        '204':
+          description: No content
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              properties:
+                name:
+                  type: string
+  /protected_resource:
+    get:
+      operationId: getProtectedResource
+      security:
+        - BearerAuth:
+            - someScope
+      responses:
+        '204':
+          description: no content
+  /protected_resource2:
+    get:
+      operationId: getProtectedResource
+      security:
+        - BearerAuth:
+            - otherScope
+      responses:
+        '204':
+          description: no content
+  /protected_resource_401:
+    get:
+      operationId: getProtectedResource
+      security:
+        - BearerAuth:
+            - unauthorized
+      responses:
+        '401':
+          description: no content
+components:
+  securitySchemes:
+    BearerAuth:
+      type: http
+      scheme: bearer
+      bearerFormat: JWT
diff --git a/pkg/runtime/bindparam_test.go b/pkg/runtime/bindparam_test.go
index 54269d1..c745e18 100644
--- a/pkg/runtime/bindparam_test.go
+++ b/pkg/runtime/bindparam_test.go
@@ -335,6 +335,30 @@ func TestBindQueryParameter(t *testing.T) {
 		assert.NoError(t, err)
 		assert.Equal(t, expected, birthday)
 	})
+
+	t.Run("optional", func(t *testing.T) {
+		queryParams := url.Values{
+			"time":   {"2020-12-09T16:09:53+00:00"},
+			"number": {"100"},
+		}
+		// An optional time will be a pointer to a time in a parameter object
+		var optionalTime *time.Time
+		err := BindQueryParameter("form", true, false, "notfound", queryParams, &optionalTime)
+		require.NoError(t, err)
+		assert.Nil(t, optionalTime)
+
+		var optionalNumber *int
+		err = BindQueryParameter("form", true, false, "notfound", queryParams, &optionalNumber)
+		require.NoError(t, err)
+		assert.Nil(t, optionalNumber)
+
+		// If we require values, we require errors when they're not present.
+		err = BindQueryParameter("form", true, true, "notfound", queryParams, &optionalTime)
+		assert.Error(t, err)
+		err = BindQueryParameter("form", true, true, "notfound", queryParams, &optionalNumber)
+		assert.Error(t, err)
+
+	})
 }
 
 func TestBindParameterViaAlias(t *testing.T) {
@@ -423,28 +447,36 @@ func TestBindParamsToExplodedObject(t *testing.T) {
 	}
 
 	var dstTime time.Time
-	err := bindParamsToExplodedObject("time", values, &dstTime)
+	fieldsPresent, err := bindParamsToExplodedObject("time", values, &dstTime)
 	assert.NoError(t, err)
+	assert.True(t, fieldsPresent)
 	assert.EqualValues(t, now, dstTime)
 
 	type AliasedTime time.Time
 	var aDstTime AliasedTime
-	err = bindParamsToExplodedObject("time", values, &aDstTime)
+	fieldsPresent, err = bindParamsToExplodedObject("time", values, &aDstTime)
 	assert.NoError(t, err)
+	assert.True(t, fieldsPresent)
 	assert.EqualValues(t, now, aDstTime)
 
 	expectedDate := MockBinder{Time: time.Date(2020, 11, 6, 0, 0, 0, 0, time.UTC)}
 
 	var dstDate MockBinder
-	err = bindParamsToExplodedObject("date", values, &dstDate)
+	fieldsPresent, err = bindParamsToExplodedObject("date", values, &dstDate)
+	assert.NoError(t, err)
+	assert.True(t, fieldsPresent)
 	assert.EqualValues(t, expectedDate, dstDate)
 
 	var eDstDate EmbeddedMockBinder
-	err = bindParamsToExplodedObject("date", values, &eDstDate)
+	fieldsPresent, err = bindParamsToExplodedObject("date", values, &eDstDate)
+	assert.NoError(t, err)
+	assert.True(t, fieldsPresent)
 	assert.EqualValues(t, expectedDate, dstDate)
 
 	var nTDstDate AnotherMockBinder
-	err = bindParamsToExplodedObject("date", values, &nTDstDate)
+	fieldsPresent, err = bindParamsToExplodedObject("date", values, &nTDstDate)
+	assert.NoError(t, err)
+	assert.True(t, fieldsPresent)
 	assert.EqualValues(t, expectedDate, nTDstDate)
 
 	type ObjectWithOptional struct {
@@ -452,8 +484,9 @@ func TestBindParamsToExplodedObject(t *testing.T) {
 	}
 
 	var optDstTime ObjectWithOptional
-	err = bindParamsToExplodedObject("explodedObject", values, &optDstTime)
+	fieldsPresent, err = bindParamsToExplodedObject("explodedObject", values, &optDstTime)
 	assert.NoError(t, err)
+	assert.True(t, fieldsPresent)
 	assert.EqualValues(t, &now, optDstTime.Time)
 }
 
diff --git a/pkg/runtime/styleparam_test.go b/pkg/runtime/styleparam_test.go
index 9dddc4b..7561f3c 100644
--- a/pkg/runtime/styleparam_test.go
+++ b/pkg/runtime/styleparam_test.go
@@ -490,6 +490,31 @@ func TestStyleParam(t *testing.T) {
 	assert.NoError(t, err)
 	assert.EqualValues(t, "7", result)
 
+	type UintType uint
+	result, err = StyleParamWithLocation("simple", false, "foo", ParamLocationQuery, UintType(9))
+	assert.NoError(t, err)
+	assert.EqualValues(t, "9", result)
+
+	type Uint8Type uint8
+	result, err = StyleParamWithLocation("simple", false, "foo", ParamLocationQuery, Uint8Type(9))
+	assert.NoError(t, err)
+	assert.EqualValues(t, "9", result)
+
+	type Uint16Type uint16
+	result, err = StyleParamWithLocation("simple", false, "foo", ParamLocationQuery, Uint16Type(9))
+	assert.NoError(t, err)
+	assert.EqualValues(t, "9", result)
+
+	type Uint32Type uint32
+	result, err = StyleParamWithLocation("simple", false, "foo", ParamLocationQuery, Uint32Type(9))
+	assert.NoError(t, err)
+	assert.EqualValues(t, "9", result)
+
+	type Uint64Type uint64
+	result, err = StyleParamWithLocation("simple", false, "foo", ParamLocationQuery, Uint64Type(9))
+	assert.NoError(t, err)
+	assert.EqualValues(t, "9", result)
+
 	type FloatType64 float64
 	result, err = StyleParamWithLocation("simple", false, "foo", ParamLocationQuery, FloatType64(7.5))
 	assert.NoError(t, err)
diff --git a/pkg/types/uuid_test.go b/pkg/types/uuid_test.go
index 4ac689a..bb62040 100644
--- a/pkg/types/uuid_test.go
+++ b/pkg/types/uuid_test.go
@@ -25,7 +25,7 @@ func TestUUID_MarshalJSON_Pass(t *testing.T) {
 	b := struct {
 		UUIDField UUID `json:"uuid"`
 	}{
-		UUIDField: UUID(testUUID),
+		UUIDField: testUUID,
 	}
 	jsonBytes, err := json.Marshal(b)
 	assert.NoError(t, err)
@@ -42,7 +42,7 @@ func TestUUID_UnmarshalJSON_Fail(t *testing.T) {
 }
 
 func TestUUID_UnmarshalJSON_Pass(t *testing.T) {
-	testUUID := UUID(uuid.MustParse("9cb14230-b640-11ec-b909-0242ac120002"))
+	testUUID := uuid.MustParse("9cb14230-b640-11ec-b909-0242ac120002")
 	jsonStr := `{"uuid":"9cb14230-b640-11ec-b909-0242ac120002"}`
 	b := struct {
 		UUIDField UUID `json:"uuid"`