Repository: deepmap/oapi-codegen
Pull request #1088 (2023-06-06 14:26:58)

Feat: Add Iris Server Support
I think most of the work has been done.Is there any reviewer?

Ns2Kracy: @deepmap-marcinr  if there is anyone available on the team who can review it?
jamietanna: Thanks for your first contribution and it's a big one 🙌  We'll try and review soon - there's a few things we'll want to get in following the v1.13.0 release for a patch bump, then can look at this one
Ns2Kracy: > 

Looking forward for it!
Ns2Kracy: > Cool.
> 
> I left some comments.

I'll finish it
Ns2Kracy: I think all the work is basically done! Great!
deepmap-marcinr: Thank you for doing this.

I need to find a way of abstracting away all these different routers behind a stable interface for template code. It'd be nice if oapi-codegen could simply import someone else's code generator as a module.

Patch:
diff --git a/README.md b/README.md
index c315349..fc43d7b 100644
--- a/README.md
+++ b/README.md
@@ -16,9 +16,9 @@ you can focus on implementing the business logic for your service.
 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. [Chi](https://github.com/go-chi/chi), [Gin](https://github.com/gin-gonic/gin),
-[gorilla/mux](https://github.com/gorilla/mux), and [Fiber](https://github.com/gofiber/fiber)
-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
+[gorilla/mux](https://github.com/gorilla/mux), [Fiber](https://github.com/gofiber/fiber), and
+[Iris](https://github.com/kataras/iris) 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
@@ -300,6 +300,56 @@ Alternatively, [Gorilla](https://github.com/gorilla/mux) is also 100% compatible
 
 </summary></details>
 
+<details><summary><code>Iris</code></summary>
+
+Code generated using `-generate iris`.
+
+The usage of `iris` is out of scope of this doc, but once you have an
+gin instance, we generate a utility function to help you associate your handlers
+with this autogenerated code. For the pet store, it looks like this:
+
+```go
+// RegisterHandlersWithOptions creates http.Handler with additional options
+func RegisterHandlersWithOptions(router *iris.Application, si ServerInterface, options IrisServerOptions) {
+
+	wrapper := ServerInterfaceWrapper{
+		Handler: si,
+	}
+
+	router.Get(options.BaseURL+"/pets", wrapper.FindPets)
+	router.Post(options.BaseURL+"/pets", wrapper.AddPet)
+	router.Delete(options.BaseURL+"/pets/:id", wrapper.DeletePet)
+	router.Get(options.BaseURL+"/pets/:id", wrapper.FindPetByID)
+
+	router.Build()
+}
+```
+
+```go
+import (
+	"github.com/kataras/iris/v12"
+	"github.com/deepmap/oapi-codegen/examples/petstore-expanded/iris/api"
+	middleware "github.com/deepmap/oapi-codegen/pkg/iris-middleware"
+)
+```
+
+The wrapper functions referenced above contain generated code which pulls
+parameters off the `Iris` request context, and unmarshals them into Go objects.
+
+You would register the generated handlers as follows:
+
+```go
+func SetupHandler() {
+    var myApi PetStoreImpl
+
+    i := iris.Default()
+	  i.Use(middleware.OapiRequestValidator(swagger))
+    api.RegisterHandlers(r, &myApi)
+}
+```
+
+</summary></details>
+
 #### Strict server generation
 
 oapi-codegen also supports generating RPC inspired strict server, that will parse request bodies and encode responses.
@@ -745,6 +795,8 @@ you can specify any combination of those.
   that produced by the `types` target.
 - `fiber`: generate the Fiber server boilerplate. This code is dependent
   on that produced by the `types` target.
+- `iris`: generate the Iris server boilerplate. This code is dependent
+  on that produced by the `types` target.
 - `client`: generate the client boilerplate. It, too, requires the types to be
   present in its package.
 - `spec`: embed the OpenAPI spec into the generated code as a gzipped blob.
diff --git a/cmd/oapi-codegen/oapi-codegen.go b/cmd/oapi-codegen/oapi-codegen.go
index 0e9ea89..3976f3a 100644
--- a/cmd/oapi-codegen/oapi-codegen.go
+++ b/cmd/oapi-codegen/oapi-codegen.go
@@ -91,7 +91,7 @@ func main() {
 	// All flags below are deprecated, and will be removed in a future release. Please do not
 	// update their behavior.
 	flag.StringVar(&flagGenerate, "generate", "types,client,server,spec",
-		`Comma-separated list of code to generate; valid options: "types", "client", "chi-server", "server", "gin", "gorilla", "spec", "skip-fmt", "skip-prune", "fiber".`)
+		`Comma-separated list of code to generate; valid options: "types", "client", "chi-server", "server", "gin", "gorilla", "spec", "skip-fmt", "skip-prune", "fiber", "iris".`)
 	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.")
@@ -443,6 +443,8 @@ func generationTargets(cfg *codegen.Configuration, targets []string) error {
 	opts := codegen.GenerateOptions{} // Blank to start with.
 	for _, opt := range targets {
 		switch opt {
+		case "iris", "iris-server":
+			opts.IrisServer = true
 		case "chi-server", "chi":
 			opts.ChiServer = true
 		case "fiber-server", "fiber":
diff --git a/examples/petstore-expanded/iris/api/petstore-server.gen.go b/examples/petstore-expanded/iris/api/petstore-server.gen.go
new file mode 100644
index 0000000..415e984
--- /dev/null
+++ b/examples/petstore-expanded/iris/api/petstore-server.gen.go
@@ -0,0 +1,247 @@
+// Package api provides primitives to interact with the openapi HTTP API.
+//
+// Code generated by github.com/deepmap/oapi-codegen version (devel) DO NOT EDIT.
+package api
+
+import (
+	"bytes"
+	"compress/gzip"
+	"encoding/base64"
+	"fmt"
+	"net/http"
+	"net/url"
+	"path"
+	"strings"
+
+	"github.com/deepmap/oapi-codegen/pkg/runtime"
+	"github.com/getkin/kin-openapi/openapi3"
+	"github.com/kataras/iris/v12"
+)
+
+// ServerInterface represents all server handlers.
+type ServerInterface interface {
+	// Returns all pets
+	// (GET /pets)
+	FindPets(ctx iris.Context, params FindPetsParams)
+	// Creates a new pet
+	// (POST /pets)
+	AddPet(ctx iris.Context)
+	// Deletes a pet by ID
+	// (DELETE /pets/{id})
+	DeletePet(ctx iris.Context, id int64)
+	// Returns a pet by ID
+	// (GET /pets/{id})
+	FindPetByID(ctx iris.Context, id int64)
+}
+
+// ServerInterfaceWrapper converts echo contexts to parameters.
+type ServerInterfaceWrapper struct {
+	Handler ServerInterface
+}
+
+type MiddlewareFunc iris.Handler
+
+// FindPets converts iris context to params.
+func (w *ServerInterfaceWrapper) FindPets(ctx iris.Context) {
+
+	var err error
+
+	// Parameter object where we will unmarshal all parameters from the context
+	var params FindPetsParams
+	// ------------- Optional query parameter "tags" -------------
+
+	err = runtime.BindQueryParameter("form", true, false, "tags", ctx.Request().URL.Query(), &params.Tags)
+	if err != nil {
+		ctx.StatusCode(http.StatusBadRequest)
+		ctx.Writef("Invalid format for parameter tags: %s", err)
+		return
+	}
+
+	// ------------- Optional query parameter "limit" -------------
+
+	err = runtime.BindQueryParameter("form", true, false, "limit", ctx.Request().URL.Query(), &params.Limit)
+	if err != nil {
+		ctx.StatusCode(http.StatusBadRequest)
+		ctx.Writef("Invalid format for parameter limit: %s", err)
+		return
+	}
+
+	// Invoke the callback with all the unmarshaled arguments
+	w.Handler.FindPets(ctx, params)
+}
+
+// AddPet converts iris context to params.
+func (w *ServerInterfaceWrapper) AddPet(ctx iris.Context) {
+
+	// Invoke the callback with all the unmarshaled arguments
+	w.Handler.AddPet(ctx)
+}
+
+// DeletePet converts iris context to params.
+func (w *ServerInterfaceWrapper) DeletePet(ctx iris.Context) {
+
+	var err error
+
+	// ------------- Path parameter "id" -------------
+	var id int64
+
+	err = runtime.BindStyledParameterWithLocation("simple", false, "id", runtime.ParamLocationPath, ctx.Params().Get("id"), &id)
+	if err != nil {
+		ctx.StatusCode(http.StatusBadRequest)
+		ctx.Writef("Invalid format for parameter id: %s", err)
+		return
+	}
+
+	// Invoke the callback with all the unmarshaled arguments
+	w.Handler.DeletePet(ctx, id)
+}
+
+// FindPetByID converts iris context to params.
+func (w *ServerInterfaceWrapper) FindPetByID(ctx iris.Context) {
+
+	var err error
+
+	// ------------- Path parameter "id" -------------
+	var id int64
+
+	err = runtime.BindStyledParameterWithLocation("simple", false, "id", runtime.ParamLocationPath, ctx.Params().Get("id"), &id)
+	if err != nil {
+		ctx.StatusCode(http.StatusBadRequest)
+		ctx.Writef("Invalid format for parameter id: %s", err)
+		return
+	}
+
+	// Invoke the callback with all the unmarshaled arguments
+	w.Handler.FindPetByID(ctx, id)
+}
+
+// IrisServerOption is the option for iris server
+type IrisServerOptions struct {
+	BaseURL     string
+	Middlewares []MiddlewareFunc
+}
+
+// RegisterHandlers creates http.Handler with routing matching OpenAPI spec.
+func RegisterHandlers(router *iris.Application, si ServerInterface) {
+	RegisterHandlersWithOptions(router, si, IrisServerOptions{})
+}
+
+// RegisterHandlersWithOptions creates http.Handler with additional options
+func RegisterHandlersWithOptions(router *iris.Application, si ServerInterface, options IrisServerOptions) {
+
+	wrapper := ServerInterfaceWrapper{
+		Handler: si,
+	}
+
+	router.Get(options.BaseURL+"/pets", wrapper.FindPets)
+	router.Post(options.BaseURL+"/pets", wrapper.AddPet)
+	router.Delete(options.BaseURL+"/pets/:id", wrapper.DeletePet)
+	router.Get(options.BaseURL+"/pets/:id", wrapper.FindPetByID)
+
+	router.Build()
+}
+
+// Base64 encoded, gzipped, json marshaled Swagger object
+var swaggerSpec = []string{
+
+	"H4sIAAAAAAAC/+RXW48budH9KwV+32OnNbEXedBTvB4vICBrT+LdvKznoYZdkmrBSw9Z1FgY6L8HRbZu",
+	"I3k2QYIgQV506WY1T51zqlj9bGz0YwwUJJv5s8l2TR7rzw8pxaQ/xhRHSsJUL9s4kH4PlG3iUTgGM2+L",
+	"od7rzDImj2LmhoO8fWM6I9uR2l9aUTK7znjKGVfffND+9iE0S+KwMrtdZxI9Fk40mPkvZtpwv/x+15mP",
+	"9HRHcok7oL+y3Uf0BHEJsiYYSS437Izg6jLup+34etwLoHV3hTdhQ+c+Lc38l2fz/4mWZm7+b3YUYjap",
+	"MJty2XUvk+HhEtLPgR8LAQ/nuE7F+MN3V8R4gZQHc7+73+llDsvYJA+CtuImj+zM3ODIQuj/mJ9wtaLU",
+	"czTdRLH53K7Bu7sF/EToTWdK0qC1yJjns9lJ0K57kcU7yOhHRzVa1ihQMmVAzSZLTASYAQPQ17ZMIgzk",
+	"Y8iSUAiWhFISZeBQOfg0UtAnve1vII9keckW61adcWwpZDqaw7wb0a4J3vQ3F5ifnp56rLf7mFazKTbP",
+	"/rR4/+Hj5w+/e9Pf9GvxrjqGks+flp8pbdjS1cRndc1M5WBxp6zdTXmazmwo5cbK7/ub/kYfHUcKOLKZ",
+	"m7f1UmdGlHX1xEwZ0h+rZrFzXv9CUlLIgM5VKmGZoq8U5W0W8o1r/V8yJVgry9ZSziDxS/iIHjINYGMY",
+	"2FOQ4oGy9PAjkqWAGYT8GBNkXLEIZ8g4MoUOAllI6xhsyZDJnyxgAfQkPbyjQBgABVYJNzwgYFkV6gAt",
+	"MNriuIb28L4kfGApCeLAEVxM5DuIKWAioBUJkKMJXSDbgS0pl6wl4chKyT3cFs7gGaSkkXMHY3EbDph0",
+	"L0pRk+5AOFgeShDYYOKS4deSJfawCLBGC2sFgTkTjA6FEAa2UrzSsWhFpbngwCNny2EFGESzOebueFUc",
+	"HjIf15hIEu5J1PXgo6MsTMB+pDSwMvVX3qBvCaHjx4IeBkZlJmGGR81tQ44FQgwgMUlMSgkvKQyH3Xu4",
+	"S0iZgihMCuyPAEoKCJvoiowosKFAARVwI1c/PJakz1iE45OXlCbWl2jZcT7bpO6gH91RXws5DuhIhR06",
+	"5dFSQtHE9LuHzyWPFAZWlh2qeYboYurUgZmsqJtrltUqmnUHG1qzLQ5BW1saigfHD5RiDz/G9MBAhbOP",
+	"w6kMersa26HlwNh/CV/CZxqqEiXDktR8Lj7EVAMoHh2TiqTie9Da8FgfOJHP2XVA5axamuTgivpQ3dnD",
+	"3RozOdcKY6Q0hVeaq7wksMRi+aE0wnG/j647jd+Qm6TjDaWE3fnWWifAQ3coxMAP6x5+FhjJOQpCWU+O",
+	"MeZCWkn7IupBqcB9FWjR7bncP2mfVmWyq0AOtgglWJDEWerBtGFB6uGHki0BSe0GQ+FDFWinyJYcJa5w",
+	"mn/3AV7dUrCaxxafMYDHlaZMblKrhz+XFuqjU92aelSad45QukPzASxWi6StnOzZ0p7MMTWZQzWqWVRg",
+	"4NAdoUyFGzjzHnBWDJalDKxQc0YosvfZJGTb6Yy0ul8Pd6fCVOYmjGMi4eJPOlczTelO/K2tt/+iZ5wO",
+	"DfW8Wwxmbn7gMOj5Uo+NpARQynUKOT8sBFfa92HJTijBw9boMGDm5rFQ2h5Pel1numlorHOJkK9n0OUU",
+	"1S5gSrjV/1m29djT8aQOOOcIPH5lr228+AdKOtEkysVJhZXqWfYNTI49yxmo3xxHd/c6AuVRW0tF/+bm",
+	"Zj/3UGjz2ji6aXKY/ZoV4vO1tF8b5tok94KI3cUANJLAHkwbj5ZYnPxDeF6D0cb6KxuXQF9Hba3ag9ua",
+	"zuTiPabtlQFCsY0xXxk13idCqTNboCddux/G6lyjZ3DDrkt0nnMuPtFwYdZ3g3rVtOmUsnwfh+2/jIX9",
+	"ZH1Jwx2JegyHQb8OsM3plCyp0O6f9MxvWuW/xxoXgtf7dR6dPfOwaxZxJFdewNp1jc0cVq6+tcADapuN",
+	"zTWLW8hFc7rikdsa3Wzyakdb3GoPGZu2E5apf+gAfWwfPFwo/a1ecv1t6rKXfHeZtQJpKIb/JCFvD2JU",
+	"FbawuFV4r79QnCt20HFx+63j5/ttvff367Ukset/m1z/s2X8QtGmfl1CabOX6fyteP9S3p+82err6e5+",
+	"97cAAAD//ykDnxlaEgAA",
+}
+
+// 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: %w", err)
+	}
+	zr, err := gzip.NewReader(bytes.NewReader(zipped))
+	if err != nil {
+		return nil, fmt.Errorf("error decompressing spec: %w", err)
+	}
+	var buf bytes.Buffer
+	_, err = buf.ReadFrom(zr)
+	if err != nil {
+		return nil, fmt.Errorf("error decompressing spec: %w", 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) {
+	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) {
+	resolvePath := PathToRawSpec("")
+
+	loader := openapi3.NewLoader()
+	loader.IsExternalRefsAllowed = true
+	loader.ReadFromURIFunc = func(loader *openapi3.Loader, url *url.URL) ([]byte, error) {
+		pathToFile := url.String()
+		pathToFile = path.Clean(pathToFile)
+		getSpec, ok := resolvePath[pathToFile]
+		if !ok {
+			err1 := fmt.Errorf("path not found: %s", pathToFile)
+			return nil, err1
+		}
+		return getSpec()
+	}
+	var specData []byte
+	specData, err = rawSpec()
+	if err != nil {
+		return
+	}
+	swagger, err = loader.LoadFromData(specData)
+	if err != nil {
+		return
+	}
+	return
+}
diff --git a/examples/petstore-expanded/iris/api/petstore-types.gen.go b/examples/petstore-expanded/iris/api/petstore-types.gen.go
new file mode 100644
index 0000000..16bbe20
--- /dev/null
+++ b/examples/petstore-expanded/iris/api/petstore-types.gen.go
@@ -0,0 +1,46 @@
+// Package api provides primitives to interact with the openapi HTTP API.
+//
+// Code generated by github.com/deepmap/oapi-codegen version (devel) DO NOT EDIT.
+package api
+
+// Error defines model for Error.
+type Error struct {
+	// Code Error code
+	Code int32 `json:"code"`
+
+	// Message Error message
+	Message string `json:"message"`
+}
+
+// NewPet defines model for NewPet.
+type NewPet struct {
+	// Name Name of the pet
+	Name string `json:"name"`
+
+	// Tag Type of the pet
+	Tag *string `json:"tag,omitempty"`
+}
+
+// Pet defines model for Pet.
+type Pet struct {
+	// Id Unique id of the pet
+	Id int64 `json:"id"`
+
+	// Name Name of the pet
+	Name string `json:"name"`
+
+	// Tag Type of the pet
+	Tag *string `json:"tag,omitempty"`
+}
+
+// FindPetsParams defines parameters for FindPets.
+type FindPetsParams struct {
+	// Tags tags to filter by
+	Tags *[]string `form:"tags,omitempty" json:"tags,omitempty"`
+
+	// Limit maximum number of results to return
+	Limit *int32 `form:"limit,omitempty" json:"limit,omitempty"`
+}
+
+// AddPetJSONRequestBody defines body for AddPet for application/json ContentType.
+type AddPetJSONRequestBody = NewPet
diff --git a/examples/petstore-expanded/iris/api/petstore.go b/examples/petstore-expanded/iris/api/petstore.go
new file mode 100644
index 0000000..f64b351
--- /dev/null
+++ b/examples/petstore-expanded/iris/api/petstore.go
@@ -0,0 +1,132 @@
+// Copyright 2019 DeepMap, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// 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 (
+	"fmt"
+	"net/http"
+	"sync"
+
+	"github.com/kataras/iris/v12"
+)
+
+type PetStore struct {
+	Pets   map[int64]Pet
+	NextId int64
+	Lock   sync.Mutex
+}
+
+func NewPetStore() *PetStore {
+	return &PetStore{
+		Pets:   make(map[int64]Pet),
+		NextId: 1000,
+	}
+}
+
+// This function wraps sending of an error in the Error format, and
+// handling the failure to marshal that.
+func sendPetStoreError(c iris.Context, code int, message string) {
+	petErr := Error{
+		Code:    int32(code),
+		Message: message,
+	}
+	c.StopWithJSON(code, petErr)
+}
+
+// FindPets implements all the handlers in the ServerInterface
+func (p *PetStore) FindPets(c iris.Context, params FindPetsParams) {
+	p.Lock.Lock()
+	defer p.Lock.Unlock()
+
+	var result []Pet
+
+	for _, pet := range p.Pets {
+		if params.Tags != nil {
+			// If we have tags,  filter pets by tag
+			for _, t := range *params.Tags {
+				if pet.Tag != nil && (*pet.Tag == t) {
+					result = append(result, pet)
+				}
+			}
+		} else {
+			// Add all pets if we're not filtering
+			result = append(result, pet)
+		}
+
+		if params.Limit != nil {
+			l := int(*params.Limit)
+			if len(result) >= l {
+				// We're at the limit
+				break
+			}
+		}
+	}
+	c.StopWithJSON(http.StatusOK, result)
+}
+
+func (p *PetStore) AddPet(c iris.Context) {
+	// We expect a NewPet object in the request body.
+	var newPet NewPet
+	err := c.ReadJSON(&newPet)
+	if err != nil {
+		sendPetStoreError(c, http.StatusBadRequest, "Invalid format for NewPet")
+		return
+	}
+	// We now have a pet, let's add it to our "database".
+
+	// We're always asynchronous, so lock unsafe operations below
+	p.Lock.Lock()
+	defer p.Lock.Unlock()
+
+	// We handle pets, not NewPets, which have an additional ID field
+	var pet Pet
+	pet.Name = newPet.Name
+	pet.Tag = newPet.Tag
+	pet.Id = p.NextId
+	p.NextId = p.NextId + 1
+
+	// Insert into map
+	p.Pets[pet.Id] = pet
+
+	// Now, we have to return the NewPet
+	c.StopWithJSON(http.StatusCreated, pet)
+}
+
+func (p *PetStore) FindPetByID(c iris.Context, petId int64) {
+	p.Lock.Lock()
+	defer p.Lock.Unlock()
+
+	pet, found := p.Pets[petId]
+	if !found {
+		sendPetStoreError(c, http.StatusNotFound, fmt.Sprintf("Could not find pet with ID %d", petId))
+		return
+	}
+	c.StopWithJSON(http.StatusOK, pet)
+}
+
+func (p *PetStore) DeletePet(c iris.Context, id int64) {
+	p.Lock.Lock()
+	defer p.Lock.Unlock()
+
+	_, found := p.Pets[id]
+	if !found {
+		sendPetStoreError(c, http.StatusNotFound, fmt.Sprintf("Could not find pet with ID %d", id))
+	}
+	delete(p.Pets, id)
+	c.StatusCode(http.StatusNoContent)
+}
diff --git a/examples/petstore-expanded/iris/api/server.cfg.yaml b/examples/petstore-expanded/iris/api/server.cfg.yaml
new file mode 100644
index 0000000..cb44eb3
--- /dev/null
+++ b/examples/petstore-expanded/iris/api/server.cfg.yaml
@@ -0,0 +1,5 @@
+package: api
+generate:
+  iris-server: true
+  embedded-spec: true
+output: petstore-server.gen.go
diff --git a/examples/petstore-expanded/iris/api/types.cfg.yaml b/examples/petstore-expanded/iris/api/types.cfg.yaml
new file mode 100644
index 0000000..9ac30e1
--- /dev/null
+++ b/examples/petstore-expanded/iris/api/types.cfg.yaml
@@ -0,0 +1,4 @@
+package: api
+generate:
+  models: true
+output: petstore-types.gen.go
diff --git a/examples/petstore-expanded/iris/petstore.go b/examples/petstore-expanded/iris/petstore.go
new file mode 100644
index 0000000..3f45e61
--- /dev/null
+++ b/examples/petstore-expanded/iris/petstore.go
@@ -0,0 +1,50 @@
+// This is an example of implementing the Pet Store from the OpenAPI documentation
+// found at:
+// https://github.com/OAI/OpenAPI-Specification/blob/master/examples/v3.0/petstore.yaml
+//
+// The code under api/petstore/ has been generated from that specification.
+package main
+
+import (
+	"flag"
+	"fmt"
+	"log"
+	"os"
+
+	"github.com/deepmap/oapi-codegen/examples/petstore-expanded/iris/api"
+	middleware "github.com/deepmap/oapi-codegen/pkg/iris-middleware"
+	"github.com/kataras/iris/v12"
+)
+
+func NewIrisPetServer(petStore *api.PetStore, port int) *iris.Application {
+	swagger, err := api.GetSwagger()
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "Error loading swagger spec\n: %s", err)
+		os.Exit(1)
+	}
+
+	// Clear out the servers array in the swagger spec, that skips validating
+	// that server names match. We don't know how this thing will be run.
+	swagger.Servers = nil
+
+	i := iris.Default()
+
+	// Use our validation middleware to check all requests against the
+	// OpenAPI schema.
+	i.Use(middleware.OapiRequestValidator(swagger))
+
+	api.RegisterHandlers(i, petStore)
+
+	return i
+}
+
+func main() {
+	port := flag.Int("port", 8080, "Port for test HTTP server")
+	flag.Parse()
+	// Create an instance of our handler which satisfies the generated interface
+	petStore := api.NewPetStore()
+	s := NewIrisPetServer(petStore, *port)
+
+	// And we serve HTTP until the world ends.
+	log.Fatal(s.Listen(fmt.Sprintf("localhost:%d", *port)))
+}
diff --git a/go.mod b/go.mod
index 7e84bf7..8850580 100644
--- a/go.mod
+++ b/go.mod
@@ -10,6 +10,7 @@ require (
 	github.com/gofiber/fiber/v2 v2.48.0
 	github.com/google/uuid v1.3.0
 	github.com/gorilla/mux v1.8.0
+	github.com/kataras/iris/v12 v12.2.5
 	github.com/labstack/echo/v4 v4.11.1
 	github.com/lestrrat-go/jwx v1.2.26
 	github.com/matryer/moq v0.3.2
@@ -21,26 +22,42 @@ require (
 )
 
 require (
+	github.com/BurntSushi/toml v1.3.2 // indirect
+	github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect
+	github.com/CloudyKit/jet/v6 v6.2.0 // indirect
+	github.com/Joker/jade v1.1.3 // indirect
+	github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06 // indirect
 	github.com/andybalholm/brotli v1.0.5 // indirect
-	github.com/bytedance/sonic v1.10.0-rc3 // indirect
-	github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
-	github.com/chenzhuoyu/iasm v0.9.0 // indirect
+	github.com/aymerick/douceur v0.2.0 // indirect
+	github.com/bytedance/sonic v1.9.1 // indirect
+	github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
+	github.com/fatih/structs v1.1.0 // indirect
+	github.com/flosch/pongo2/v4 v4.0.2 // indirect
 	github.com/gabriel-vasile/mimetype v1.4.2 // indirect
 	github.com/gin-contrib/sse v0.1.0 // indirect
-	github.com/go-openapi/jsonpointer v0.20.0 // indirect
-	github.com/go-openapi/swag v0.22.4 // indirect
+	github.com/go-openapi/jsonpointer v0.19.5 // indirect
+	github.com/go-openapi/swag v0.19.5 // indirect
 	github.com/go-playground/locales v0.14.1 // indirect
 	github.com/go-playground/universal-translator v0.18.1 // indirect
-	github.com/go-playground/validator/v10 v10.14.1 // indirect
+	github.com/go-playground/validator/v10 v10.14.0 // indirect
 	github.com/goccy/go-json v0.10.2 // indirect
 	github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
-	github.com/invopop/yaml v0.2.0 // indirect
+	github.com/golang/snappy v0.0.4 // indirect
+	github.com/gomarkdown/markdown v0.0.0-20230716120725-531d2d74bc12 // indirect
+	github.com/gorilla/css v1.0.0 // indirect
+	github.com/invopop/yaml v0.1.0 // indirect
+	github.com/iris-contrib/schema v0.0.6 // indirect
 	github.com/josharian/intern v1.0.0 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
+	github.com/kataras/blocks v0.0.7 // indirect
+	github.com/kataras/golog v0.1.9 // indirect
+	github.com/kataras/pio v0.0.12 // indirect
+	github.com/kataras/sitemap v0.0.6 // indirect
+	github.com/kataras/tunnel v0.0.4 // indirect
 	github.com/klauspost/compress v1.16.7 // indirect
-	github.com/klauspost/cpuid/v2 v2.2.5 // indirect
+	github.com/klauspost/cpuid/v2 v2.2.4 // indirect
 	github.com/labstack/gommon v0.4.0 // indirect
 	github.com/leodido/go-urn v1.2.4 // indirect
 	github.com/lestrrat-go/backoff/v2 v2.0.8 // indirect
@@ -48,33 +65,42 @@ require (
 	github.com/lestrrat-go/httpcc v1.0.1 // indirect
 	github.com/lestrrat-go/iter v1.0.2 // indirect
 	github.com/lestrrat-go/option v1.0.1 // indirect
+	github.com/mailgun/raymond/v2 v2.0.48 // indirect
 	github.com/mailru/easyjson v0.7.7 // indirect
 	github.com/mattn/go-colorable v0.1.13 // indirect
 	github.com/mattn/go-isatty v0.0.19 // indirect
-	github.com/mattn/go-runewidth v0.0.15 // indirect
+	github.com/mattn/go-runewidth v0.0.14 // indirect
+	github.com/microcosm-cc/bluemonday v1.0.25 // indirect
 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 	github.com/modern-go/reflect2 v1.0.2 // indirect
 	github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
-	github.com/pelletier/go-toml/v2 v2.0.9 // indirect
-	github.com/perimeterx/marshmallow v1.1.5 // indirect
+	github.com/pelletier/go-toml/v2 v2.0.8 // indirect
+	github.com/perimeterx/marshmallow v1.1.4 // indirect
 	github.com/pkg/errors v0.9.1 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
-	github.com/rivo/uniseg v0.4.4 // indirect
-	github.com/rogpeppe/go-internal v1.11.0 // indirect
+	github.com/rivo/uniseg v0.2.0 // indirect
+	github.com/russross/blackfriday/v2 v2.1.0 // indirect
+	github.com/schollz/closestmatch v2.1.0+incompatible // indirect
+	github.com/sirupsen/logrus v1.8.1 // indirect
 	github.com/stretchr/objx v0.5.0 // indirect
+	github.com/tdewolff/minify/v2 v2.12.8 // indirect
+	github.com/tdewolff/parse/v2 v2.6.7 // indirect
 	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
 	github.com/ugorji/go/codec v1.2.11 // indirect
 	github.com/valyala/bytebufferpool v1.0.0 // indirect
 	github.com/valyala/fasthttp v1.48.0 // indirect
 	github.com/valyala/fasttemplate v1.2.2 // indirect
 	github.com/valyala/tcplisten v1.0.0 // indirect
-	golang.org/x/arch v0.4.0 // indirect
+	github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
+	github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
+	github.com/yosssi/ace v0.0.5 // indirect
+	golang.org/x/arch v0.3.0 // indirect
 	golang.org/x/crypto v0.12.0 // indirect
 	golang.org/x/mod v0.12.0 // indirect
 	golang.org/x/net v0.14.0 // indirect
 	golang.org/x/sys v0.11.0 // indirect
 	golang.org/x/time v0.3.0 // indirect
-	golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f // indirect
 	google.golang.org/protobuf v1.31.0 // indirect
+	gopkg.in/ini.v1 v1.67.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 )
diff --git a/go.sum b/go.sum
index 9ae5252..5ad7659 100644
--- a/go.sum
+++ b/go.sum
@@ -1,25 +1,41 @@
+github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
+github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
+github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=
+github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=
+github.com/CloudyKit/jet/v6 v6.2.0 h1:EpcZ6SR9n28BUGtNJSvlBqf90IpjeFr36Tizxhn/oME=
+github.com/CloudyKit/jet/v6 v6.2.0/go.mod h1:d3ypHeIRNo2+XyqnGA8s+aphtcVpjP5hPwP/Lzo7Ro4=
+github.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc=
+github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=
+github.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk=
+github.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=
 github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
+github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06 h1:KkH3I3sJuOLP3TjA/dfr4NAY8bghDwnXiU7cTKxQqo0=
+github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06/go.mod h1:7erjKLwalezA0k99cWs5L11HWOAPNjdUZ6RxH1BXbbM=
+github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
 github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
 github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
 github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
 github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
+github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
+github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
 github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
 github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
-github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
-github.com/bytedance/sonic v1.10.0-rc3 h1:uNSnscRapXTwUgTyOF0GVljYD08p9X/Lbr9MweSV3V0=
-github.com/bytedance/sonic v1.10.0-rc3/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
+github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
+github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
 github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
+github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
 github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
-github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0=
-github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA=
-github.com/chenzhuoyu/iasm v0.9.0 h1:9fhXjVzq5hUy2gkhhgHl95zG2cEAhw9OSGs8toWWAwo=
-github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs=
 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
+github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
+github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
+github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
+github.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw=
+github.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8=
 github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
 github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
 github.com/getkin/kin-openapi v0.118.0 h1:z43njxPmJ7TaPpMSCQb7PN0dEYno4tyBPQcrFdHoLuM=
@@ -30,21 +46,20 @@ github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
 github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
 github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk=
 github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
+github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
 github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
-github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ=
-github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA=
+github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
 github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
-github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU=
-github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
 github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
 github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
 github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
 github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
 github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
-github.com/go-playground/validator/v10 v10.14.1 h1:9c50NUPC30zyuKprjL3vNZ0m5oG+jU0zvx4AqHGnv4k=
-github.com/go-playground/validator/v10 v10.14.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
+github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
+github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
 github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
 github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
+github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
 github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
 github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
 github.com/gofiber/fiber/v2 v2.48.0 h1:cRVMCb9aUJDsyHxGFLwz/sGzDggdailZZyptU9F9cU0=
@@ -52,32 +67,53 @@ github.com/gofiber/fiber/v2 v2.48.0/go.mod h1:xqJgfqrc23FJuqGOW6DVgi3HyZEm2Mn9pR
 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.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
+github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/gomarkdown/markdown v0.0.0-20230716120725-531d2d74bc12 h1:uK3X/2mt4tbSGoHvbLBHUny7CKiuwUip3MArtukol4E=
+github.com/gomarkdown/markdown v0.0.0-20230716120725-531d2d74bc12/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=
 github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
 github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
 github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
+github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
 github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
 github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
+github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
+github.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=
+github.com/invopop/yaml v0.1.0 h1:YW3WGUoJEXYfzWBjn00zIlrw7brGVD0fUKRYDPAPhrc=
 github.com/invopop/yaml v0.1.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q=
-github.com/invopop/yaml v0.2.0 h1:7zky/qH+O0DwAyoobXUqvVBwgBFRxKoQ/3FjcVpjTMY=
-github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q=
+github.com/iris-contrib/httpexpect/v2 v2.15.1 h1:G2/TW0EZ5UhNNdljNDBBQDfdfumLlV6ljRqdTk3cAmc=
+github.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw=
+github.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA=
 github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
 github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
 github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
 github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
 github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
+github.com/kataras/blocks v0.0.7 h1:cF3RDY/vxnSRezc7vLFlQFTYXG/yAr1o7WImJuZbzC4=
+github.com/kataras/blocks v0.0.7/go.mod h1:UJIU97CluDo0f+zEjbnbkeMRlvYORtmc1304EeyXf4I=
+github.com/kataras/golog v0.1.9 h1:vLvSDpP7kihFGKFAvBSofYo7qZNULYSHOH2D7rPTKJk=
+github.com/kataras/golog v0.1.9/go.mod h1:jlpk/bOaYCyqDqH18pgDHdaJab72yBE6i0O3s30hpWY=
+github.com/kataras/iris/v12 v12.2.5 h1:R5UzUW4MIByBM6tKMG3UqJ7hL1JCEE+dkqQ8L72f6PU=
+github.com/kataras/iris/v12 v12.2.5/go.mod h1:bf3oblPF8tQmRgyPCzPZr0mLazvEDFgImdaGZYuN4hw=
+github.com/kataras/pio v0.0.12 h1:o52SfVYauS3J5X08fNjlGS5arXHjW/ItLkyLcKjoH6w=
+github.com/kataras/pio v0.0.12/go.mod h1:ODK/8XBhhQ5WqrAhKy+9lTPS7sBf6O3KcLhc9klfRcY=
+github.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY=
+github.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4=
+github.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA=
+github.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw=
 github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I=
 github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
 github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
-github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=
-github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
-github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
+github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
+github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
-github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
-github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 github.com/labstack/echo/v4 v4.11.1 h1:dEpLU2FLg4UVmvCGPuk/APjlH6GDpbEPti61srUUUs4=
 github.com/labstack/echo/v4 v4.11.1/go.mod h1:YuYRTSM3CHs2ybfrL8Px48bO6BAnYIN4l8wSTMP6BDQ=
 github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8=
@@ -97,6 +133,8 @@ github.com/lestrrat-go/jwx v1.2.26/go.mod h1:MaiCdGbn3/cckbOFSCluJlJMmp9dmZm5hDu
 github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
 github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU=
 github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
+github.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw=
+github.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18=
 github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
@@ -110,8 +148,11 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k
 github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
 github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
 github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
-github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
-github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
+github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/microcosm-cc/bluemonday v1.0.25 h1:4NEwSfiJ+Wva0VxN5B8OwMicaJvD8r9tlJWm9rtloEg=
+github.com/microcosm-cc/bluemonday v1.0.25/go.mod h1:ZIOjCQp1OrzBBPIJmfX4qDYFuhU02nx4bn030ixfHLE=
+github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -119,25 +160,32 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
 github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
-github.com/pelletier/go-toml/v2 v2.0.9 h1:uH2qQXheeefCCkuBBSLi7jCiSmj3VRh2+Goq2N7Xxu0=
-github.com/pelletier/go-toml/v2 v2.0.9/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
+github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
+github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
+github.com/perimeterx/marshmallow v1.1.4 h1:pZLDH9RjlLGGorbXhcaQLhfuV0pFMNfPO55FuFkxqLw=
 github.com/perimeterx/marshmallow v1.1.4/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
-github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s=
-github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
 github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
-github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
-github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
-github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
-github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
+github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
+github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=
+github.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk=
+github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=
+github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
+github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
+github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
 github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
 github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
 github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
@@ -145,8 +193,15 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
 github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
 github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
 github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
 github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/tdewolff/minify/v2 v2.12.8 h1:Q2BqOTmlMjoutkuD/OPCnJUpIqrzT3nRPkw+q+KpXS0=
+github.com/tdewolff/minify/v2 v2.12.8/go.mod h1:YRgk7CC21LZnbuke2fmYnCTq+zhCgpb0yJACOTUNJ1E=
+github.com/tdewolff/parse/v2 v2.6.7 h1:WrFllrqmzAcrKHzoYgMupqgUBIfBVOb0yscFzDf8bBg=
+github.com/tdewolff/parse/v2 v2.6.7/go.mod h1:XHDhaU6IBgsryfdnpzUXBlT6leW/l25yrFBTEb4eIyM=
+github.com/tdewolff/test v1.0.9 h1:SswqJCmeN4B+9gEAi/5uqT0qpi1y2/2O47V/1hhGZT0=
+github.com/tdewolff/test v1.0.9/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
 github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
 github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
 github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
@@ -162,10 +217,23 @@ github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQ
 github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
 github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
 github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
+github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU=
+github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
+github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
+github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
+github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
+github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=
+github.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA=
+github.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0=
+github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=
+github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=
+github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
 golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
-golang.org/x/arch v0.4.0 h1:A8WCeEWhLwPBKNbFi5Wv5UTCBx5zzubnXDlMOFAzFMc=
-golang.org/x/arch v0.4.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
+golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
+golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
@@ -175,30 +243,38 @@ golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98y
 golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug=
 golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
 golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
 golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
+golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 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-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
 golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
 golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
 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/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+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-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -212,6 +288,7 @@ golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
 golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
@@ -222,6 +299,7 @@ golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
 golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
 golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss=
@@ -229,14 +307,17 @@ golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f h1:GGU+dLjvlC3qDwqYgL6UgRmHXhOOgns0bZu2Ty5mm6U=
-golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
 google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
 google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U=
+gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
+gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
 gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
@@ -245,5 +326,5 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C
 gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
+moul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs=
 rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
diff --git a/pkg/codegen/codegen.go b/pkg/codegen/codegen.go
index b0a60a9..a6d2670 100644
--- a/pkg/codegen/codegen.go
+++ b/pkg/codegen/codegen.go
@@ -174,6 +174,14 @@ func Generate(spec *openapi3.T, opts Configuration) (string, error) {
 		MergeImports(xGoTypeImports, imprts)
 	}
 
+	var irisServerOut string
+	if opts.Generate.IrisServer {
+		irisServerOut, err = GenerateIrisServer(t, ops)
+		if err != nil {
+			return "", fmt.Errorf("error generating Go handlers for Paths: %w", err)
+		}
+	}
+
 	var echoServerOut string
 	if opts.Generate.EchoServer {
 		echoServerOut, err = GenerateEchoServer(t, ops)
@@ -293,6 +301,14 @@ func Generate(spec *openapi3.T, opts Configuration) (string, error) {
 		}
 	}
 
+	if opts.Generate.IrisServer {
+		_, err = w.WriteString(irisServerOut)
+		if err != nil {
+			return "", fmt.Errorf("error writing server path handlers: %w", err)
+		}
+
+	}
+
 	if opts.Generate.EchoServer {
 		_, err = w.WriteString(echoServerOut)
 		if err != nil {
diff --git a/pkg/codegen/configuration.go b/pkg/codegen/configuration.go
index d266617..5fd7f57 100644
--- a/pkg/codegen/configuration.go
+++ b/pkg/codegen/configuration.go
@@ -22,6 +22,7 @@ type Configuration struct {
 
 // GenerateOptions specifies which supported output formats to generate.
 type GenerateOptions struct {
+	IrisServer    bool `yaml:"iris-server,omitempty"`    // IrisServer specifies whether to generate iris server boilerplate
 	ChiServer     bool `yaml:"chi-server,omitempty"`     // ChiServer specifies whether to generate chi server boilerplate
 	FiberServer   bool `yaml:"fiber-server,omitempty"`   // FiberServer specifies whether to generate fiber server boilerplate
 	EchoServer    bool `yaml:"echo-server,omitempty"`    // EchoServer specifies whether to generate echo server boilerplate
@@ -116,6 +117,9 @@ func (o Configuration) Validate() error {
 
 	// Only one server type should be specified at a time.
 	nServers := 0
+	if o.Generate.IrisServer {
+		nServers++
+	}
 	if o.Generate.ChiServer {
 		nServers++
 	}
diff --git a/pkg/codegen/operations.go b/pkg/codegen/operations.go
index 29127b3..1ad8541 100644
--- a/pkg/codegen/operations.go
+++ b/pkg/codegen/operations.go
@@ -904,7 +904,6 @@ func GenerateTypesForOperations(t *template.Template, ops []OperationDefinition)
 	}
 	if _, err := w.WriteString(addTypes); err != nil {
 		return "", fmt.Errorf("error writing boilerplate to buffer: %w", err)
-
 	}
 
 	// Generate boiler plate for all additional types.
@@ -933,6 +932,12 @@ func GenerateTypesForOperations(t *template.Template, ops []OperationDefinition)
 	return buf.String(), nil
 }
 
+// GenerateIrisServer generates all the go code for the ServerInterface as well as
+// all the wrapper functions around our handlers.
+func GenerateIrisServer(t *template.Template, operations []OperationDefinition) (string, error) {
+	return GenerateTemplates([]string{"iris/iris-interface.tmpl", "iris/iris-middleware.tmpl", "iris/iris-handler.tmpl"}, t, operations)
+}
+
 // GenerateChiServer generates all the go code for the ServerInterface as well as
 // all the wrapper functions around our handlers.
 func GenerateChiServer(t *template.Template, operations []OperationDefinition) (string, error) {
@@ -979,6 +984,9 @@ func GenerateStrictServer(t *template.Template, operations []OperationDefinition
 	if opts.Generate.FiberServer {
 		templates = append(templates, "strict/strict-fiber-interface.tmpl", "strict/strict-fiber.tmpl")
 	}
+	if opts.Generate.IrisServer {
+		templates = append(templates, "strict/strict-iris-interface.tmpl", "strict/strict-iris.tmpl")
+	}
 
 	return GenerateTemplates(templates, t, operations)
 }
diff --git a/pkg/codegen/template_helpers.go b/pkg/codegen/template_helpers.go
index 3f1c1f2..ffd3e77 100644
--- a/pkg/codegen/template_helpers.go
+++ b/pkg/codegen/template_helpers.go
@@ -283,6 +283,7 @@ var TemplateFunctions = template.FuncMap{
 	"genParamTypes":              genParamTypes,
 	"genParamNames":              genParamNames,
 	"genParamFmtString":          ReplacePathParamsWithStr,
+	"swaggerUriToIrisUri":        SwaggerUriToIrisUri,
 	"swaggerUriToEchoUri":        SwaggerUriToEchoUri,
 	"swaggerUriToFiberUri":       SwaggerUriToFiberUri,
 	"swaggerUriToChiUri":         SwaggerUriToChiUri,
diff --git a/pkg/codegen/templates/imports.tmpl b/pkg/codegen/templates/imports.tmpl
index f69a492..1f9ee11 100644
--- a/pkg/codegen/templates/imports.tmpl
+++ b/pkg/codegen/templates/imports.tmpl
@@ -28,6 +28,8 @@ import (
 	"github.com/labstack/echo/v4"
 	"github.com/gin-gonic/gin"
 	"github.com/gofiber/fiber/v2"
+	"github.com/kataras/iris/v12"
+	"github.com/kataras/iris/v12/core/router"
 	"github.com/gorilla/mux"
 	{{- range .ExternalImports}}
 	{{ . }}
diff --git a/pkg/codegen/templates/iris/iris-handler.tmpl b/pkg/codegen/templates/iris/iris-handler.tmpl
new file mode 100644
index 0000000..3f1228f
--- /dev/null
+++ b/pkg/codegen/templates/iris/iris-handler.tmpl
@@ -0,0 +1,23 @@
+// IrisServerOption is the option for iris server
+type IrisServerOptions struct {
+    BaseURL string
+    Middlewares []MiddlewareFunc
+}
+
+// RegisterHandlers creates http.Handler with routing matching OpenAPI spec.
+func RegisterHandlers(router *iris.Application, si ServerInterface) {
+    RegisterHandlersWithOptions(router, si, IrisServerOptions{})
+}
+
+
+// RegisterHandlersWithOptions creates http.Handler with additional options
+func RegisterHandlersWithOptions(router *iris.Application, si ServerInterface, options IrisServerOptions) {
+{{if .}}
+    wrapper := ServerInterfaceWrapper{
+        Handler: si,
+    }
+{{end}}
+{{range .}}router.{{.Method | lower | title}}(options.BaseURL + "{{.Path | swaggerUriToIrisUri}}", wrapper.{{.OperationId}})
+{{end}}
+    router.Build()
+}
diff --git a/pkg/codegen/templates/iris/iris-interface.tmpl b/pkg/codegen/templates/iris/iris-interface.tmpl
new file mode 100644
index 0000000..fb53a12
--- /dev/null
+++ b/pkg/codegen/templates/iris/iris-interface.tmpl
@@ -0,0 +1,7 @@
+// ServerInterface represents all server handlers.
+type ServerInterface interface {
+{{range .}}{{.SummaryAsComment }}
+// ({{.Method}} {{.Path}})
+{{.OperationId}}(ctx iris.Context{{genParamArgs .PathParams}}{{if .RequiresParamObject}}, params {{.OperationId}}Params{{end}})
+{{end}}
+}
diff --git a/pkg/codegen/templates/iris/iris-middleware.tmpl b/pkg/codegen/templates/iris/iris-middleware.tmpl
new file mode 100644
index 0000000..6f0b949
--- /dev/null
+++ b/pkg/codegen/templates/iris/iris-middleware.tmpl
@@ -0,0 +1,161 @@
+// ServerInterfaceWrapper converts echo contexts to parameters.
+type ServerInterfaceWrapper struct {
+    Handler ServerInterface
+}
+
+type MiddlewareFunc iris.Handler
+
+{{range .}}{{$opid := .OperationId}}// {{$opid}} converts iris context to params.
+func (w *ServerInterfaceWrapper) {{.OperationId}} (ctx iris.Context) {
+{{if or .RequiresParamObject (gt (len .PathParams) 0) }}
+    var err error
+{{end}}
+
+{{range .PathParams}}// ------------- Path parameter "{{.ParamName}}" -------------
+    var {{$varName := .GoVariableName}}{{$varName}} {{.TypeDef}}
+{{if .IsPassThrough}}
+    {{$varName}} = ctx.URLParam("{{.ParamName}}")
+{{end}}
+{{if .IsJson}}
+    err = json.Unmarshal([]byte(ctx.URLParam("{{.ParamName}}")), &{{$varName}})
+    if err != nil {
+    	ctx.StatusCode(http.StatusBadRequest)
+        ctx.WriteString("Error unmarshaling parameter '{{.ParamName}}' as JSON")
+        return
+    }
+{{end}}
+{{if .IsStyled}}
+    err = runtime.BindStyledParameterWithLocation("{{.Style}}",{{.Explode}}, "{{.ParamName}}", runtime.ParamLocationPath, ctx.Params().Get("{{.ParamName}}"), &{{$varName}})
+    if err != nil {
+        ctx.StatusCode(http.StatusBadRequest)
+        ctx.Writef("Invalid format for parameter {{.ParamName}}: %s", err)
+        return
+    }
+{{end}}
+{{end}}
+
+{{range .SecurityDefinitions}}
+    ctx.Set({{.ProviderName | sanitizeGoIdentity | ucFirst}}Scopes, {{toStringArray .Scopes}})
+{{end}}
+
+{{if .RequiresParamObject}}
+    // Parameter object where we will unmarshal all parameters from the context
+    var params {{.OperationId}}Params
+{{range $paramIdx, $param := .QueryParams}}
+    {{- if (or (or .Required .IsPassThrough) (or .IsJson .IsStyled)) -}}
+      // ------------- {{if .Required}}Required{{else}}Optional{{end}} query parameter "{{.ParamName}}" -------------
+    {{ end }}
+    {{if .IsStyled}}
+    err = runtime.BindQueryParameter("{{.Style}}", {{.Explode}}, {{.Required}}, "{{.ParamName}}", ctx.Request().URL.Query(), &params.{{.GoName}})
+    if err != nil {
+        ctx.StatusCode(http.StatusBadRequest)
+        ctx.Writef("Invalid format for parameter {{.ParamName}}: %s", err)
+        return
+    }
+    {{else}}
+    if paramValue := ctx.QueryParam("{{.ParamName}}"); paramValue != "" {
+    {{if .IsPassThrough}}
+    params.{{.GoName}} = {{if not .Required}}&{{end}}paramValue
+    {{end}}
+    {{if .IsJson}}
+    var value {{.TypeDef}}
+    err = json.Unmarshal([]byte(paramValue), &value)
+    if err != nil {
+        ctx.StatusCode(http.StatusBadRequest)
+        ctx.WriteString("Error unmarshaling parameter '{{.ParamName}}' as JSON")
+        return
+    }
+    params.{{.GoName}} = {{if not .Required}}&{{end}}value
+    {{end}}
+    }{{if .Required}} else {
+        ctx.StatusCode(http.StatusBadRequest)
+        ctx.WriteString("Query argument {{.ParamName}} is required, but not found")
+        return
+    }{{end}}
+    {{end}}
+{{end}}
+
+{{if .HeaderParams}}
+    headers := ctx.Request().Header
+{{range .HeaderParams}}// ------------- {{if .Required}}Required{{else}}Optional{{end}} header parameter "{{.ParamName}}" -------------
+    if valueList, found := headers[http.CanonicalHeaderKey("{{.ParamName}}")]; found {
+        var {{.GoName}} {{.TypeDef}}
+        n := len(valueList)
+        if n != 1 {
+            ctx.StatusCode(http.StatusBadRequest)
+            ctx.Writef("Expected one value for {{.ParamName}}, got %d", n)
+            return
+        }
+{{if .IsPassThrough}}
+        params.{{.GoName}} = {{if not .Required}}&{{end}}valueList[0]
+{{end}}
+{{if .IsJson}}
+        err = json.Unmarshal([]byte(valueList[0]), &{{.GoName}})
+        if err != nil {
+            ctx.StatusCode(http.StatusBadRequest)
+            ctx.WriteString("Error unmarshaling parameter '{{.ParamName}}' as JSON")
+            return
+        }
+{{end}}
+{{if .IsStyled}}
+        err = runtime.BindStyledParameterWithLocation("{{.Style}}",{{.Explode}}, "{{.ParamName}}", runtime.ParamLocationHeader, valueList[0], &{{.GoName}})
+        if err != nil {
+            ctx.StatusCode(http.StatusBadRequest)
+            ctx.Writef("Invalid format for parameter {{.ParamName}}: %s", err)
+            return
+        }
+{{end}}
+        params.{{.GoName}} = {{if not .Required}}&{{end}}{{.GoName}}
+        } {{if .Required}}else {
+            ctx.StatusCode(http.StatusBadRequest)
+            ctx.WriteString("Header {{.ParamName}} is required, but not found")
+            return
+        }{{end}}
+{{end}}
+{{end}}
+
+{{range .CookieParams}}
+    if cookie, err := ctx.Cookie("{{.ParamName}}"); err == nil {
+    {{if .IsPassThrough}}
+    params.{{.GoName}} = {{if not .Required}}&{{end}}cookie.Value
+    {{end}}
+    {{if .IsJson}}
+    var value {{.TypeDef}}
+    var decoded string
+    decoded, err := url.QueryUnescape(cookie.Value)
+    if err != nil {
+        ctx.StatusCode(http.StatusBadRequest)
+        ctx.WriteString("Error unescaping cookie parameter '{{.ParamName}}'")
+        return
+    }
+    err = json.Unmarshal([]byte(decoded), &value)
+    if err != nil {
+        ctx.StatusCode(http.StatusBadRequest)
+        ctx.WriteString("Error unmarshaling parameter '{{.ParamName}}' as JSON")
+        return
+    }
+    params.{{.GoName}} = {{if not .Required}}&{{end}}value
+    {{end}}
+    {{if .IsStyled}}
+    var value {{.TypeDef}}
+    err = runtime.BindStyledParameterWithLocation("simple",{{.Explode}}, "{{.ParamName}}", runtime.ParamLocationCookie, cookie.Value, &value)
+    if err != nil {
+        ctx.StatusCode(http.StatusBadRequest)
+        ctx.Writef("Invalid format for parameter {{.ParamName}}: %s", err)
+        return
+    }
+    params.{{.GoName}} = {{if not .Required}}&{{end}}value
+    {{end}}
+    }{{if .Required}} else {
+        ctx.StatusCode(http.StatusBadRequest)
+        ctx.WriteString("Cookie {{.ParamName}} is required, but not found")
+        return
+    }{{end}}
+
+{{end}}{{/* .CookieParams */}}
+
+{{end}}{{/* .RequiresParamObject */}}
+    // Invoke the callback with all the unmarshaled arguments
+    w.Handler.{{.OperationId}}(ctx{{genParamNames .PathParams}}{{if .RequiresParamObject}}, params{{end}})
+}
+{{end}}
diff --git a/pkg/codegen/templates/strict/strict-iris-interface.tmpl b/pkg/codegen/templates/strict/strict-iris-interface.tmpl
new file mode 100644
index 0000000..afddbad
--- /dev/null
+++ b/pkg/codegen/templates/strict/strict-iris-interface.tmpl
@@ -0,0 +1,140 @@
+{{range .}}
+    {{$opid := .OperationId -}}
+    type {{$opid | ucFirst}}RequestObject struct {
+        {{range .PathParams -}}
+            {{.GoName | ucFirst}} {{.TypeDef}} {{.JsonTag}}
+        {{end -}}
+        {{if .RequiresParamObject -}}
+            Params {{$opid}}Params
+        {{end -}}
+        {{if .HasMaskedRequestContentTypes -}}
+            ContentType string
+        {{end -}}
+        {{$multipleBodies := gt (len .Bodies) 1 -}}
+        {{range .Bodies -}}
+            {{if $multipleBodies}}{{.NameTag}}{{end}}Body {{if eq .NameTag "Multipart"}}*multipart.Reader{{else if ne .NameTag ""}}*{{$opid}}{{.NameTag}}RequestBody{{else}}io.Reader{{end}}
+        {{end -}}
+    }
+
+    type {{$opid | ucFirst}}ResponseObject interface {
+        Visit{{$opid}}Response(ctx iris.Context) error
+    }
+
+    {{range .Responses}}
+        {{$statusCode := .StatusCode -}}
+        {{$hasHeaders := ne 0 (len .Headers) -}}
+        {{$fixedStatusCode := .HasFixedStatusCode -}}
+        {{$isRef := .IsRef -}}
+        {{$ref := .Ref  | ucFirstWithPkgName -}}
+        {{$headers := .Headers -}}
+
+        {{if (and $hasHeaders (not $isRef)) -}}
+            type {{$opid}}{{$statusCode}}ResponseHeaders struct {
+                {{range .Headers -}}
+                    {{.GoName}} {{.Schema.TypeDecl}}
+                {{end -}}
+            }
+        {{end}}
+
+        {{range .Contents}}
+            {{$receiverTypeName := printf "%s%s%s%s" $opid $statusCode .NameTagOrContentType "Response"}}
+            {{if eq .NameTag "Text" -}}
+                type {{$receiverTypeName}} string
+            {{else if and $fixedStatusCode $isRef -}}
+                type {{$receiverTypeName}} struct{ {{$ref}}{{.NameTagOrContentType}}Response }
+            {{else if and (not $hasHeaders) ($fixedStatusCode) (.IsSupported) -}}
+                type {{$receiverTypeName}} {{if eq .NameTag "Multipart"}}func(writer *multipart.Writer)error{{else if .IsSupported}}{{if .Schema.IsRef}}={{end}} {{.Schema.TypeDecl}}{{else}}io.Reader{{end}}
+            {{else -}}
+                type {{$receiverTypeName}} struct {
+                    Body {{if eq .NameTag "Multipart"}}func(writer *multipart.Writer)error{{else if .IsSupported}}{{.Schema.TypeDecl}}{{else}}io.Reader{{end}}
+                    {{if $hasHeaders -}}
+                        Headers {{if $isRef}}{{$ref}}{{else}}{{$opid}}{{$statusCode}}{{end}}ResponseHeaders
+                    {{end -}}
+
+                    {{if not $fixedStatusCode -}}
+                        StatusCode int
+                    {{end -}}
+
+                    {{if not .HasFixedContentType -}}
+                        ContentType string
+                    {{end -}}
+
+                    {{if not .IsSupported -}}
+                        ContentLength int64
+                    {{end -}}
+                }
+            {{end}}
+
+            func (response {{$receiverTypeName}}) Visit{{$opid}}Response(ctx iris.Context) error {
+                {{range $headers -}}
+                    ctx.ResponseWriter().Header().Set("{{.Name}}", fmt.Sprint(response.Headers.{{.GoName}}))
+                {{end -}}
+                {{if eq .NameTag "Multipart" -}}
+                    writer := multipart.NewWriter(ctx.ResponseWriter())
+                {{end -}}
+                ctx.ResponseWriter().Header().Set("Content-Type", {{if eq .NameTag "Multipart"}}writer.FormDataContentType(){{else if .HasFixedContentType }}"{{.ContentType}}"{{else}}response.ContentType{{end}})
+                {{if not .IsSupported -}}
+                    if response.ContentLength != 0 {
+                        ctx.ResponseWriter().Header().Set("Content-Length", fmt.Sprint(response.ContentLength))
+                    }
+                {{end -}}
+                ctx.StatusCode({{if $fixedStatusCode}}{{$statusCode}}{{else}}response.StatusCode{{end}})
+                {{$hasBodyVar := or ($hasHeaders) (not $fixedStatusCode) (not .IsSupported)}}
+                {{if .IsJSON -}}
+                    {{$hasUnionElements := ne 0 (len .Schema.UnionElements)}}
+                    return ctx.JSON(&{{if $hasBodyVar}}response.Body{{else}}response{{end}}{{if $hasUnionElements}}.union{{end}})
+                {{else if eq .NameTag "Text" -}}
+                    _, err := ctx.WriteString(string({{if $hasBodyVar}}response.Body{{else}}response{{end}}))
+                    return err
+                {{else if eq .NameTag "Formdata" -}}
+                    if form, err := runtime.MarshalForm({{if $hasBodyVar}}response.Body{{else}}response{{end}}, nil); err != nil {
+                        return err
+                    } else {
+                        _, err := ctx.WriteString(form.Encode())
+                        return err
+                    }
+                {{else if eq .NameTag "Multipart" -}}
+                    defer writer.Close()
+                    return {{if $hasBodyVar}}response.Body{{else}}response{{end}}(writer);
+                {{else -}}
+                    if closer, ok := response.Body.(io.ReadCloser); ok {
+                        defer closer.Close()
+                    }
+                    _, err := io.Copy(ctx.ResponseWriter(), response.Body)
+                    return err
+                {{end}}{{/* if eq .NameTag "JSON" */ -}}
+            }
+        {{end}}
+
+        {{if eq 0 (len .Contents) -}}
+            {{if and $fixedStatusCode $isRef -}}
+                type {{$opid}}{{$statusCode}}Response = {{$ref}}Response
+            {{else -}}
+                type {{$opid}}{{$statusCode}}Response struct {
+                    {{if $hasHeaders -}}
+                        Headers {{if $isRef}}{{$ref}}{{else}}{{$opid}}{{$statusCode}}{{end}}ResponseHeaders
+                    {{end}}
+                    {{if not $fixedStatusCode -}}
+                        StatusCode int
+                    {{end -}}
+                }
+            {{end -}}
+            func (response {{$opid}}{{$statusCode}}Response) Visit{{$opid}}Response(ctx iris.Context) error {
+                {{range $headers -}}
+                    ctx.Response().Header.Set("{{.Name}}", fmt.Sprint(response.Headers.{{.GoName}}))
+                {{end -}}
+                ctx.StatusCode({{if $fixedStatusCode}}{{$statusCode}}{{else}}response.StatusCode{{end}})
+                return nil
+            }
+        {{end}}
+    {{end}}
+{{end}}
+
+// StrictServerInterface represents all server handlers.
+type StrictServerInterface interface {
+{{range .}}{{.SummaryAsComment }}
+// ({{.Method}} {{.Path}})
+{{$opid := .OperationId -}}
+{{$opid}}(ctx context.Context, request {{$opid | ucFirst}}RequestObject) ({{$opid | ucFirst}}ResponseObject, error)
+{{end}}{{/* range . */ -}}
+}
diff --git a/pkg/codegen/templates/strict/strict-iris.tmpl b/pkg/codegen/templates/strict/strict-iris.tmpl
new file mode 100644
index 0000000..5760dbb
--- /dev/null
+++ b/pkg/codegen/templates/strict/strict-iris.tmpl
@@ -0,0 +1,95 @@
+type StrictHandlerFunc = runtime.StrictIrisHandlerFunc
+type StrictMiddlewareFunc = runtime.StrictIrisMiddlewareFunc
+
+func NewStrictHandler(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc) ServerInterface {
+    return &strictHandler{ssi: ssi, middlewares: middlewares}
+}
+
+type strictHandler struct {
+    ssi StrictServerInterface
+    middlewares []StrictMiddlewareFunc
+}
+
+{{range .}}
+    {{$opid := .OperationId}}
+    // {{$opid}} operation middleware
+    func (sh *strictHandler) {{.OperationId}}(ctx iris.Context{{genParamArgs .PathParams}}{{if .RequiresParamObject}}, params {{.OperationId}}Params{{end}}) {
+        var request {{$opid | ucFirst}}RequestObject
+
+        {{range .PathParams -}}
+            request.{{.GoName}} = {{.GoVariableName}}
+        {{end -}}
+
+        {{if .RequiresParamObject -}}
+            request.Params = params
+        {{end -}}
+
+        {{ if .HasMaskedRequestContentTypes -}}
+            request.ContentType = ctx.GetContentTypeRequested()
+        {{end -}}
+
+        {{$multipleBodies := gt (len .Bodies) 1 -}}
+        {{range .Bodies -}}
+            {{if $multipleBodies}}if strings.HasPrefix(ctx.GetHeader("Content-Type"), "{{.ContentType}}") { {{end}}
+                {{if .IsJSON }}
+                    var body {{$opid}}{{.NameTag}}RequestBody
+                    if err := ctx.ReadJSON(&body); err != nil {
+                        ctx.StopWithError(http.StatusBadRequest, err)
+                        return
+                    }
+                    request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = &body
+                {{else if eq .NameTag "Formdata" -}}
+                    if err := ctx.Request().ParseForm(); err != nil {
+                        ctx.StopWithError(http.StatusBadRequest, err)
+                        return
+                    }
+                    var body {{$opid}}{{.NameTag}}RequestBody
+                    if err := runtime.BindForm(&body, ctx.Request().Form, nil, nil); err != nil {
+                        ctx.StopWithError(http.StatusBadRequest, err)
+                        return
+                    }
+                    request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = &body
+                {{else if eq .NameTag "Multipart" -}}
+                    if reader, err := ctx.Request().MultipartReader(); err == nil {
+                        request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = reader
+                    } else {
+                        ctx.StopWithError(http.StatusBadRequest, err)
+                        return
+                    }
+                {{else if eq .NameTag "Text" -}}
+                    data, err := io.ReadAll(ctx.Request().Body)
+                    if err != nil {
+                        ctx.StopWithError(http.StatusBadRequest, err)
+                        return
+                    }
+                    body := {{$opid}}{{.NameTag}}RequestBody(data)
+                    request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = &body
+                {{else -}}
+                    request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = ctx.Request().Body
+                {{end}}{{/* if eq .NameTag "JSON" */ -}}
+            {{if $multipleBodies}}}{{end}}
+        {{end}}{{/* range .Bodies */}}
+
+        handler := func(ctx iris.Context, request interface{}) (interface{}, error) {
+            return sh.ssi.{{.OperationId}}(ctx, request.({{$opid | ucFirst}}RequestObject))
+        }
+        for _, middleware := range sh.middlewares {
+            handler = middleware(handler, "{{.OperationId}}")
+        }
+
+        response, err := handler(ctx, request)
+
+        if err != nil {
+            ctx.StopWithError(http.StatusBadRequest, err)
+            return
+        } else if validResponse, ok := response.({{$opid | ucFirst}}ResponseObject); ok {
+            if err := validResponse.Visit{{$opid}}Response(ctx); err != nil {
+                ctx.StopWithError(http.StatusBadRequest, err)
+                return
+            }
+        } else if response != nil {
+            ctx.Writef("Unexpected response type: %T", response)
+            return
+        }
+    }
+{{end}}
diff --git a/pkg/codegen/utils.go b/pkg/codegen/utils.go
index 79aee73..61b6d97 100644
--- a/pkg/codegen/utils.go
+++ b/pkg/codegen/utils.go
@@ -410,6 +410,21 @@ func IsWholeDocumentReference(ref string) bool {
 	return ref != "" && !strings.ContainsAny(ref, "#")
 }
 
+// SwaggerUriToIrisUri converts a OpenAPI style path URI with parameters to an
+// Iris compatible path URI. We need to replace all of OpenAPI parameters with
+//
+//	{param}
+//	{param*}
+//	{.param}
+//	{.param*}
+//	{;param}
+//	{;param*}
+//	{?param}
+//	{?param*}
+func SwaggerUriToIrisUri(uri string) string {
+	return pathParamRE.ReplaceAllString(uri, ":$1")
+}
+
 // SwaggerUriToEchoUri converts a OpenAPI style path URI with parameters to an
 // Echo compatible path URI. We need to replace all of OpenAPI parameters with
 // ":param". Valid input parameters are:
@@ -725,7 +740,7 @@ func StringWithTypeNameToGoComment(in, typeName string) string {
 }
 
 func DeprecationComment(reason string) string {
-	var content = "Deprecated:" // The colon is required at the end even without reason
+	content := "Deprecated:" // The colon is required at the end even without reason
 	if reason != "" {
 		content += fmt.Sprintf(" %s", reason)
 	}
diff --git a/pkg/iris-middleware/oapi_validate.go b/pkg/iris-middleware/oapi_validate.go
new file mode 100644
index 0000000..1ed2454
--- /dev/null
+++ b/pkg/iris-middleware/oapi_validate.go
@@ -0,0 +1,184 @@
+package middleware
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"net/http"
+	"os"
+	"strings"
+
+	"github.com/getkin/kin-openapi/openapi3"
+	"github.com/getkin/kin-openapi/openapi3filter"
+	"github.com/getkin/kin-openapi/routers"
+	"github.com/getkin/kin-openapi/routers/gorillamux"
+	"github.com/kataras/iris/v12"
+)
+
+const (
+	IrisContextKey = "oapi-codegen/iris-context"
+	UserDataKey    = "oapi-codegen/user-data"
+)
+
+// OapiValidatorFromYamlFile creates a validator middleware from a YAML file path
+func OapiValidatorFromYamlFile(path string) (iris.Handler, error) {
+	data, err := os.ReadFile(path)
+	if err != nil {
+		return nil, fmt.Errorf("error reading %s: %w", path, err)
+	}
+
+	swagger, err := openapi3.NewLoader().LoadFromData(data)
+	if err != nil {
+		return nil, fmt.Errorf("error parsing %s as Swagger YAML: %w",
+			path, err)
+	}
+
+	return OapiRequestValidator(swagger), nil
+}
+
+// OapiRequestValidator is a iris middleware function which validates incoming HTTP requests
+// to make sure that they conform to the given OAPI 3.0 specification. When
+// OAPI validation fails on the request, we return an HTTP/400 with error message
+func OapiRequestValidator(swagger *openapi3.T) iris.Handler {
+	return OapiRequestValidatorWithOptions(swagger, nil)
+}
+
+// ErrorHandler is called when there is an error in validation
+type ErrorHandler func(ctx iris.Context, message string, statusCode int)
+
+// MultiErrorHandler is called when oapi returns a MultiError type
+type MultiErrorHandler func(openapi3.MultiError) error
+
+// Options to customize request validation. These are passed through to
+// openapi3filter.
+type Options struct {
+	Options           openapi3filter.Options
+	ErrorHandler      ErrorHandler
+	ParamDecoder      openapi3filter.ContentParameterDecoder
+	UserData          interface{}
+	MultiErrorHandler MultiErrorHandler
+	// SilenceServersWarning allows silencing a warning for https://github.com/deepmap/oapi-codegen/issues/882 that reports when an OpenAPI spec has `spec.Servers != nil`
+	SilenceServersWarning bool
+}
+
+// OapiRequestValidatorWithOptions creates a validator from a swagger object, with validation options
+func OapiRequestValidatorWithOptions(swagger *openapi3.T, options *Options) iris.Handler {
+	router, err := gorillamux.NewRouter(swagger)
+	if err != nil {
+		panic(err)
+	}
+
+	return func(ctx iris.Context) {
+		err := ValidateRequestFromContext(ctx, router, options)
+		if err != nil {
+			if options != nil && options.ErrorHandler != nil {
+				options.ErrorHandler(ctx, err.Error(), http.StatusBadRequest)
+			} else {
+				ctx.StopWithError(http.StatusBadRequest, err)
+			}
+		}
+		ctx.Next()
+	}
+}
+
+// ValidateRequestFromContext is called from the middleware above and actually does the work
+// of validating a request.
+func ValidateRequestFromContext(ctx iris.Context, router routers.Router, options *Options) error {
+	req := ctx.Request()
+	route, pathParams, err := router.FindRoute(req)
+	// We failed to find a matching route for the request.
+	if err != nil {
+		switch e := err.(type) {
+		case *routers.RouteError:
+			// We've got a bad request, the path requested doesn't match
+			// either server, or path, or something.
+			return errors.New(e.Reason)
+		default:
+			// This should never happen today, but if our upstream code changes,
+			// we don't want to crash the server, so handle the unexpected error.
+			return fmt.Errorf("error validating route: %w", err)
+		}
+	}
+
+	// Validate request
+	requestValidationInput := &openapi3filter.RequestValidationInput{
+		Request:    req,
+		PathParams: pathParams,
+		Route:      route,
+	}
+
+	// Pass the iris context into the request validator, so that any callbacks
+	// which it invokes make it available.
+	requestContext := context.WithValue(context.Background(), IrisContextKey, ctx) //nolint:staticcheck
+
+	if options != nil {
+		requestValidationInput.Options = &options.Options
+		requestValidationInput.ParamDecoder = options.ParamDecoder
+		requestContext = context.WithValue(requestContext, UserDataKey, options.UserData) //nolint:staticcheck
+	}
+
+	err = openapi3filter.ValidateRequest(requestContext, requestValidationInput)
+	if err != nil {
+		me := openapi3.MultiError{}
+		if errors.As(err, &me) {
+			errFunc := getMultiErrorHandlerFromOptions(options)
+			return errFunc(me)
+		}
+
+		switch e := err.(type) {
+		case *openapi3filter.RequestError:
+			// We've got a bad request
+			// Split up the verbose error by lines and return the first one
+			// openapi errors seem to be multi-line with a decent message on the first
+			errorLines := strings.Split(e.Error(), "\n")
+			return fmt.Errorf("error in openapi3filter.RequestError: %s", errorLines[0])
+		case *openapi3filter.SecurityRequirementsError:
+			return fmt.Errorf("error in openapi3filter.SecurityRequirementsError: %s", e.Error())
+		default:
+			// This should never happen today, but if our upstream code changes,
+			// we don't want to crash the server, so handle the unexpected error.
+			return fmt.Errorf("error validating request: %w", err)
+		}
+	}
+	return nil
+}
+
+// GetIrisContext gets the iris context from within requests. It returns
+// nil if not found or wrong type.
+func GetIrisContext(ctx context.Context) iris.Context {
+	iface := ctx.Value(IrisContextKey)
+	if iface == nil {
+		return nil
+	}
+
+	iCtx, ok := iface.(iris.Context)
+	if ok {
+		return iCtx
+	}
+	return nil
+}
+
+func GetUserData(ctx context.Context) interface{} {
+	return ctx.Value(UserDataKey)
+}
+
+// getMultiErrorHandlerFromOptions attempts to get the MultiErrorHandler from the options. If it is not set,
+// return a default handler
+func getMultiErrorHandlerFromOptions(options *Options) MultiErrorHandler {
+	if options == nil {
+		return defaultMultiErrorHandler
+	}
+
+	if options.MultiErrorHandler == nil {
+		return defaultMultiErrorHandler
+	}
+
+	return options.MultiErrorHandler
+}
+
+// defaultMultiErrorHandler returns a StatusBadRequest (400) and a list
+// of all the errors. This method is called if there are no other
+// methods defined on the options.
+func defaultMultiErrorHandler(me openapi3.MultiError) error {
+	return fmt.Errorf("multiple errors encountered: %s", me)
+}
diff --git a/pkg/runtime/strictmiddleware.go b/pkg/runtime/strictmiddleware.go
index fac240e..1d397f1 100644
--- a/pkg/runtime/strictmiddleware.go
+++ b/pkg/runtime/strictmiddleware.go
@@ -5,9 +5,14 @@ import (
 	"net/http"
 
 	"github.com/gin-gonic/gin"
+	"github.com/kataras/iris/v12"
 	"github.com/labstack/echo/v4"
 )
 
+type StrictIrisHandlerFunc func(ctx iris.Context, request interface{}) (response interface{}, err error)
+
+type StrictIrisMiddlewareFunc func(f StrictIrisHandlerFunc, operationID string) StrictIrisHandlerFunc
+
 type StrictEchoHandlerFunc func(ctx echo.Context, request interface{}) (response interface{}, err error)
 
 type StrictEchoMiddlewareFunc func(f StrictEchoHandlerFunc, operationID string) StrictEchoHandlerFunc


Test patch:
diff --git a/examples/petstore-expanded/iris/petstore_test.go b/examples/petstore-expanded/iris/petstore_test.go
new file mode 100644
index 0000000..f59d2bc
--- /dev/null
+++ b/examples/petstore-expanded/iris/petstore_test.go
@@ -0,0 +1,151 @@
+// Copyright 2019 DeepMap, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"net/http/httptest"
+	"testing"
+
+	"github.com/deepmap/oapi-codegen/examples/petstore-expanded/iris/api"
+	"github.com/deepmap/oapi-codegen/pkg/testutil"
+	"github.com/stretchr/testify/assert"
+)
+
+func doGet(t *testing.T, handler http.Handler, url string) *httptest.ResponseRecorder {
+	response := testutil.NewRequest().Get(url).WithAcceptJson().GoWithHTTPHandler(t, handler)
+	return response.Recorder
+}
+
+func TestPetStore(t *testing.T) {
+	store := api.NewPetStore()
+	irisPetServer := NewIrisPetServer(store, 8080)
+
+	t.Run("Add pet", func(t *testing.T) {
+		tag := "TagOfSpot"
+		newPet := api.NewPet{
+			Name: "Spot",
+			Tag:  &tag,
+		}
+
+		rr := testutil.NewRequest().Post("/pets").WithJsonBody(newPet).GoWithHTTPHandler(t, irisPetServer).Recorder
+		assert.Equal(t, http.StatusCreated, rr.Code)
+
+		var resultPet api.Pet
+		err := json.NewDecoder(rr.Body).Decode(&resultPet)
+		assert.NoError(t, err, "error unmarshaling response")
+		assert.Equal(t, newPet.Name, resultPet.Name)
+		assert.Equal(t, *newPet.Tag, *resultPet.Tag)
+	})
+
+	t.Run("Find pet by ID", func(t *testing.T) {
+		pet := api.Pet{
+			Id: 100,
+		}
+		store.Pets[pet.Id] = pet
+		rr := doGet(t, irisPetServer, fmt.Sprintf("/pets/%d", pet.Id))
+
+		var resultPet api.Pet
+		err := json.NewDecoder(rr.Body).Decode(&resultPet)
+		assert.NoError(t, err, "error getting pet")
+		assert.Equal(t, pet, resultPet)
+	})
+
+	t.Run("Pet not found", func(t *testing.T) {
+		rr := doGet(t, irisPetServer, "/pets/27179095781")
+		assert.Equal(t, http.StatusNotFound, rr.Code)
+
+		var petError api.Error
+		err := json.NewDecoder(rr.Body).Decode(&petError)
+		assert.NoError(t, err, "error getting response", err)
+		assert.Equal(t, int32(http.StatusNotFound), petError.Code)
+	})
+
+	t.Run("List all pets", func(t *testing.T) {
+		store.Pets = map[int64]api.Pet{1: {}, 2: {}}
+
+		// Now, list all pets, we should have two
+		rr := doGet(t, irisPetServer, "/pets")
+		assert.Equal(t, http.StatusOK, rr.Code)
+
+		var petList []api.Pet
+		err := json.NewDecoder(rr.Body).Decode(&petList)
+		assert.NoError(t, err, "error getting response", err)
+		assert.Equal(t, 2, len(petList))
+	})
+
+	t.Run("Filter pets by tag", func(t *testing.T) {
+		tag := "TagOfFido"
+
+		store.Pets = map[int64]api.Pet{
+			1: {
+				Tag: &tag,
+			},
+			2: {},
+		}
+
+		// Filter pets by tag, we should have 1
+		rr := doGet(t, irisPetServer, "/pets?tags=TagOfFido")
+		assert.Equal(t, http.StatusOK, rr.Code)
+
+		var petList []api.Pet
+		err := json.NewDecoder(rr.Body).Decode(&petList)
+		assert.NoError(t, err, "error getting response", err)
+		assert.Equal(t, 1, len(petList))
+	})
+
+	t.Run("Filter pets by tag", func(t *testing.T) {
+		store.Pets = map[int64]api.Pet{1: {}, 2: {}}
+
+		// Filter pets by non existent tag, we should have 0
+		rr := doGet(t, irisPetServer, "/pets?tags=NotExists")
+		assert.Equal(t, http.StatusOK, rr.Code)
+
+		var petList []api.Pet
+		err := json.NewDecoder(rr.Body).Decode(&petList)
+		assert.NoError(t, err, "error getting response", err)
+		assert.Equal(t, 0, len(petList))
+	})
+
+	t.Run("Delete pets", func(t *testing.T) {
+		store.Pets = map[int64]api.Pet{1: {}, 2: {}}
+
+		// Let's delete non-existent pet
+		rr := testutil.NewRequest().Delete("/pets/7").GoWithHTTPHandler(t, irisPetServer).Recorder
+		assert.Equal(t, http.StatusNotFound, rr.Code)
+
+		var petError api.Error
+		err := json.NewDecoder(rr.Body).Decode(&petError)
+		assert.NoError(t, err, "error unmarshaling PetError")
+		assert.Equal(t, int32(http.StatusNotFound), petError.Code)
+
+		// Now, delete both real pets
+		rr = testutil.NewRequest().Delete("/pets/1").GoWithHTTPHandler(t, irisPetServer).Recorder
+		assert.Equal(t, http.StatusNoContent, rr.Code)
+
+		rr = testutil.NewRequest().Delete("/pets/2").GoWithHTTPHandler(t, irisPetServer).Recorder
+		assert.Equal(t, http.StatusNoContent, rr.Code)
+
+		// Should have no pets left.
+		var petList []api.Pet
+		rr = doGet(t, irisPetServer, "/pets")
+		assert.Equal(t, http.StatusOK, rr.Code)
+		err = json.NewDecoder(rr.Body).Decode(&petList)
+		assert.NoError(t, err, "error getting response", err)
+		assert.Equal(t, 0, len(petList))
+	})
+}
diff --git a/internal/test/strict-server/gorilla/server.cfg.yaml b/internal/test/strict-server/gorilla/server.cfg.yaml
new file mode 100644
index 0000000..4c40e5a
--- /dev/null
+++ b/internal/test/strict-server/gorilla/server.cfg.yaml
@@ -0,0 +1,6 @@
+package: api
+generate:
+  gorilla-server: true
+  strict-server: true
+  embedded-spec: true
+output: server.gen.go
diff --git a/internal/test/strict-server/gorilla/server.gen.go b/internal/test/strict-server/gorilla/server.gen.go
new file mode 100644
index 0000000..637295c
--- /dev/null
+++ b/internal/test/strict-server/gorilla/server.gen.go
@@ -0,0 +1,1457 @@
+// Package api provides primitives to interact with the openapi HTTP API.
+//
+// Code generated by github.com/deepmap/oapi-codegen version (devel) DO NOT EDIT.
+package api
+
+import (
+	"bytes"
+	"compress/gzip"
+	"context"
+	"encoding/base64"
+	"encoding/json"
+	"fmt"
+	"io"
+	"mime/multipart"
+	"net/http"
+	"net/url"
+	"path"
+	"strings"
+
+	"github.com/deepmap/oapi-codegen/pkg/runtime"
+	"github.com/getkin/kin-openapi/openapi3"
+	"github.com/gorilla/mux"
+)
+
+// ServerInterface represents all server handlers.
+type ServerInterface interface {
+
+	// (POST /json)
+	JSONExample(w http.ResponseWriter, r *http.Request)
+
+	// (POST /multipart)
+	MultipartExample(w http.ResponseWriter, r *http.Request)
+
+	// (POST /multiple)
+	MultipleRequestAndResponseTypes(w http.ResponseWriter, r *http.Request)
+
+	// (GET /reserved-go-keyword-parameters/{type})
+	ReservedGoKeywordParameters(w http.ResponseWriter, r *http.Request, pType string)
+
+	// (POST /reusable-responses)
+	ReusableResponses(w http.ResponseWriter, r *http.Request)
+
+	// (POST /text)
+	TextExample(w http.ResponseWriter, r *http.Request)
+
+	// (POST /unknown)
+	UnknownExample(w http.ResponseWriter, r *http.Request)
+
+	// (POST /unspecified-content-type)
+	UnspecifiedContentType(w http.ResponseWriter, r *http.Request)
+
+	// (POST /urlencoded)
+	URLEncodedExample(w http.ResponseWriter, r *http.Request)
+
+	// (POST /with-headers)
+	HeadersExample(w http.ResponseWriter, r *http.Request, params HeadersExampleParams)
+
+	// (POST /with-union)
+	UnionExample(w http.ResponseWriter, r *http.Request)
+}
+
+// ServerInterfaceWrapper converts contexts to parameters.
+type ServerInterfaceWrapper struct {
+	Handler            ServerInterface
+	HandlerMiddlewares []MiddlewareFunc
+	ErrorHandlerFunc   func(w http.ResponseWriter, r *http.Request, err error)
+}
+
+type MiddlewareFunc func(http.HandlerFunc) http.HandlerFunc
+
+// JSONExample operation middleware
+func (siw *ServerInterfaceWrapper) JSONExample(w http.ResponseWriter, r *http.Request) {
+	ctx := r.Context()
+
+	handler := func(w http.ResponseWriter, r *http.Request) {
+		siw.Handler.JSONExample(w, r)
+	}
+
+	for _, middleware := range siw.HandlerMiddlewares {
+		handler = middleware(handler)
+	}
+
+	handler(w, r.WithContext(ctx))
+}
+
+// MultipartExample operation middleware
+func (siw *ServerInterfaceWrapper) MultipartExample(w http.ResponseWriter, r *http.Request) {
+	ctx := r.Context()
+
+	handler := func(w http.ResponseWriter, r *http.Request) {
+		siw.Handler.MultipartExample(w, r)
+	}
+
+	for _, middleware := range siw.HandlerMiddlewares {
+		handler = middleware(handler)
+	}
+
+	handler(w, r.WithContext(ctx))
+}
+
+// MultipleRequestAndResponseTypes operation middleware
+func (siw *ServerInterfaceWrapper) MultipleRequestAndResponseTypes(w http.ResponseWriter, r *http.Request) {
+	ctx := r.Context()
+
+	handler := func(w http.ResponseWriter, r *http.Request) {
+		siw.Handler.MultipleRequestAndResponseTypes(w, r)
+	}
+
+	for _, middleware := range siw.HandlerMiddlewares {
+		handler = middleware(handler)
+	}
+
+	handler(w, r.WithContext(ctx))
+}
+
+// ReservedGoKeywordParameters operation middleware
+func (siw *ServerInterfaceWrapper) ReservedGoKeywordParameters(w http.ResponseWriter, r *http.Request) {
+	ctx := r.Context()
+
+	var err error
+
+	// ------------- Path parameter "type" -------------
+	var pType string
+
+	err = runtime.BindStyledParameter("simple", false, "type", mux.Vars(r)["type"], &pType)
+	if err != nil {
+		siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "type", Err: err})
+		return
+	}
+
+	handler := func(w http.ResponseWriter, r *http.Request) {
+		siw.Handler.ReservedGoKeywordParameters(w, r, pType)
+	}
+
+	for _, middleware := range siw.HandlerMiddlewares {
+		handler = middleware(handler)
+	}
+
+	handler(w, r.WithContext(ctx))
+}
+
+// ReusableResponses operation middleware
+func (siw *ServerInterfaceWrapper) ReusableResponses(w http.ResponseWriter, r *http.Request) {
+	ctx := r.Context()
+
+	handler := func(w http.ResponseWriter, r *http.Request) {
+		siw.Handler.ReusableResponses(w, r)
+	}
+
+	for _, middleware := range siw.HandlerMiddlewares {
+		handler = middleware(handler)
+	}
+
+	handler(w, r.WithContext(ctx))
+}
+
+// TextExample operation middleware
+func (siw *ServerInterfaceWrapper) TextExample(w http.ResponseWriter, r *http.Request) {
+	ctx := r.Context()
+
+	handler := func(w http.ResponseWriter, r *http.Request) {
+		siw.Handler.TextExample(w, r)
+	}
+
+	for _, middleware := range siw.HandlerMiddlewares {
+		handler = middleware(handler)
+	}
+
+	handler(w, r.WithContext(ctx))
+}
+
+// UnknownExample operation middleware
+func (siw *ServerInterfaceWrapper) UnknownExample(w http.ResponseWriter, r *http.Request) {
+	ctx := r.Context()
+
+	handler := func(w http.ResponseWriter, r *http.Request) {
+		siw.Handler.UnknownExample(w, r)
+	}
+
+	for _, middleware := range siw.HandlerMiddlewares {
+		handler = middleware(handler)
+	}
+
+	handler(w, r.WithContext(ctx))
+}
+
+// UnspecifiedContentType operation middleware
+func (siw *ServerInterfaceWrapper) UnspecifiedContentType(w http.ResponseWriter, r *http.Request) {
+	ctx := r.Context()
+
+	handler := func(w http.ResponseWriter, r *http.Request) {
+		siw.Handler.UnspecifiedContentType(w, r)
+	}
+
+	for _, middleware := range siw.HandlerMiddlewares {
+		handler = middleware(handler)
+	}
+
+	handler(w, r.WithContext(ctx))
+}
+
+// URLEncodedExample operation middleware
+func (siw *ServerInterfaceWrapper) URLEncodedExample(w http.ResponseWriter, r *http.Request) {
+	ctx := r.Context()
+
+	handler := func(w http.ResponseWriter, r *http.Request) {
+		siw.Handler.URLEncodedExample(w, r)
+	}
+
+	for _, middleware := range siw.HandlerMiddlewares {
+		handler = middleware(handler)
+	}
+
+	handler(w, r.WithContext(ctx))
+}
+
+// HeadersExample operation middleware
+func (siw *ServerInterfaceWrapper) HeadersExample(w http.ResponseWriter, r *http.Request) {
+	ctx := r.Context()
+
+	var err error
+
+	// Parameter object where we will unmarshal all parameters from the context
+	var params HeadersExampleParams
+
+	headers := r.Header
+
+	// ------------- Required header parameter "header1" -------------
+	if valueList, found := headers[http.CanonicalHeaderKey("header1")]; found {
+		var Header1 string
+		n := len(valueList)
+		if n != 1 {
+			siw.ErrorHandlerFunc(w, r, &TooManyValuesForParamError{ParamName: "header1", Count: n})
+			return
+		}
+
+		err = runtime.BindStyledParameterWithLocation("simple", false, "header1", runtime.ParamLocationHeader, valueList[0], &Header1)
+		if err != nil {
+			siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "header1", Err: err})
+			return
+		}
+
+		params.Header1 = Header1
+
+	} else {
+		err = fmt.Errorf("Header parameter header1 is required, but not found")
+		siw.ErrorHandlerFunc(w, r, &RequiredHeaderError{ParamName: "header1", Err: err})
+		return
+	}
+
+	// ------------- Optional header parameter "header2" -------------
+	if valueList, found := headers[http.CanonicalHeaderKey("header2")]; found {
+		var Header2 int
+		n := len(valueList)
+		if n != 1 {
+			siw.ErrorHandlerFunc(w, r, &TooManyValuesForParamError{ParamName: "header2", Count: n})
+			return
+		}
+
+		err = runtime.BindStyledParameterWithLocation("simple", false, "header2", runtime.ParamLocationHeader, valueList[0], &Header2)
+		if err != nil {
+			siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "header2", Err: err})
+			return
+		}
+
+		params.Header2 = &Header2
+
+	}
+
+	handler := func(w http.ResponseWriter, r *http.Request) {
+		siw.Handler.HeadersExample(w, r, params)
+	}
+
+	for _, middleware := range siw.HandlerMiddlewares {
+		handler = middleware(handler)
+	}
+
+	handler(w, r.WithContext(ctx))
+}
+
+// UnionExample operation middleware
+func (siw *ServerInterfaceWrapper) UnionExample(w http.ResponseWriter, r *http.Request) {
+	ctx := r.Context()
+
+	handler := func(w http.ResponseWriter, r *http.Request) {
+		siw.Handler.UnionExample(w, r)
+	}
+
+	for _, middleware := range siw.HandlerMiddlewares {
+		handler = middleware(handler)
+	}
+
+	handler(w, r.WithContext(ctx))
+}
+
+type UnescapedCookieParamError struct {
+	ParamName string
+	Err       error
+}
+
+func (e *UnescapedCookieParamError) Error() string {
+	return fmt.Sprintf("error unescaping cookie parameter '%s'", e.ParamName)
+}
+
+func (e *UnescapedCookieParamError) Unwrap() error {
+	return e.Err
+}
+
+type UnmarshalingParamError struct {
+	ParamName string
+	Err       error
+}
+
+func (e *UnmarshalingParamError) Error() string {
+	return fmt.Sprintf("Error unmarshaling parameter %s as JSON: %s", e.ParamName, e.Err.Error())
+}
+
+func (e *UnmarshalingParamError) Unwrap() error {
+	return e.Err
+}
+
+type RequiredParamError struct {
+	ParamName string
+}
+
+func (e *RequiredParamError) Error() string {
+	return fmt.Sprintf("Query argument %s is required, but not found", e.ParamName)
+}
+
+type RequiredHeaderError struct {
+	ParamName string
+	Err       error
+}
+
+func (e *RequiredHeaderError) Error() string {
+	return fmt.Sprintf("Header parameter %s is required, but not found", e.ParamName)
+}
+
+func (e *RequiredHeaderError) Unwrap() error {
+	return e.Err
+}
+
+type InvalidParamFormatError struct {
+	ParamName string
+	Err       error
+}
+
+func (e *InvalidParamFormatError) Error() string {
+	return fmt.Sprintf("Invalid format for parameter %s: %s", e.ParamName, e.Err.Error())
+}
+
+func (e *InvalidParamFormatError) Unwrap() error {
+	return e.Err
+}
+
+type TooManyValuesForParamError struct {
+	ParamName string
+	Count     int
+}
+
+func (e *TooManyValuesForParamError) Error() string {
+	return fmt.Sprintf("Expected one value for %s, got %d", e.ParamName, e.Count)
+}
+
+// Handler creates http.Handler with routing matching OpenAPI spec.
+func Handler(si ServerInterface) http.Handler {
+	return HandlerWithOptions(si, GorillaServerOptions{})
+}
+
+type GorillaServerOptions struct {
+	BaseURL          string
+	BaseRouter       *mux.Router
+	Middlewares      []MiddlewareFunc
+	ErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error)
+}
+
+// HandlerFromMux creates http.Handler with routing matching OpenAPI spec based on the provided mux.
+func HandlerFromMux(si ServerInterface, r *mux.Router) http.Handler {
+	return HandlerWithOptions(si, GorillaServerOptions{
+		BaseRouter: r,
+	})
+}
+
+func HandlerFromMuxWithBaseURL(si ServerInterface, r *mux.Router, baseURL string) http.Handler {
+	return HandlerWithOptions(si, GorillaServerOptions{
+		BaseURL:    baseURL,
+		BaseRouter: r,
+	})
+}
+
+// HandlerWithOptions creates http.Handler with additional options
+func HandlerWithOptions(si ServerInterface, options GorillaServerOptions) http.Handler {
+	r := options.BaseRouter
+
+	if r == nil {
+		r = mux.NewRouter()
+	}
+	if options.ErrorHandlerFunc == nil {
+		options.ErrorHandlerFunc = func(w http.ResponseWriter, r *http.Request, err error) {
+			http.Error(w, err.Error(), http.StatusBadRequest)
+		}
+	}
+	wrapper := ServerInterfaceWrapper{
+		Handler:            si,
+		HandlerMiddlewares: options.Middlewares,
+		ErrorHandlerFunc:   options.ErrorHandlerFunc,
+	}
+
+	r.HandleFunc(options.BaseURL+"/json", wrapper.JSONExample).Methods("POST")
+
+	r.HandleFunc(options.BaseURL+"/multipart", wrapper.MultipartExample).Methods("POST")
+
+	r.HandleFunc(options.BaseURL+"/multiple", wrapper.MultipleRequestAndResponseTypes).Methods("POST")
+
+	r.HandleFunc(options.BaseURL+"/reserved-go-keyword-parameters/{type}", wrapper.ReservedGoKeywordParameters).Methods("GET")
+
+	r.HandleFunc(options.BaseURL+"/reusable-responses", wrapper.ReusableResponses).Methods("POST")
+
+	r.HandleFunc(options.BaseURL+"/text", wrapper.TextExample).Methods("POST")
+
+	r.HandleFunc(options.BaseURL+"/unknown", wrapper.UnknownExample).Methods("POST")
+
+	r.HandleFunc(options.BaseURL+"/unspecified-content-type", wrapper.UnspecifiedContentType).Methods("POST")
+
+	r.HandleFunc(options.BaseURL+"/urlencoded", wrapper.URLEncodedExample).Methods("POST")
+
+	r.HandleFunc(options.BaseURL+"/with-headers", wrapper.HeadersExample).Methods("POST")
+
+	r.HandleFunc(options.BaseURL+"/with-union", wrapper.UnionExample).Methods("POST")
+
+	return r
+}
+
+type BadrequestResponse struct {
+}
+
+type ReusableresponseResponseHeaders struct {
+	Header1 string
+	Header2 int
+}
+type ReusableresponseJSONResponse struct {
+	Body Example
+
+	Headers ReusableresponseResponseHeaders
+}
+
+type JSONExampleRequestObject struct {
+	Body *JSONExampleJSONRequestBody
+}
+
+type JSONExampleResponseObject interface {
+	VisitJSONExampleResponse(w http.ResponseWriter) error
+}
+
+type JSONExample200JSONResponse Example
+
+func (response JSONExample200JSONResponse) VisitJSONExampleResponse(w http.ResponseWriter) error {
+	w.Header().Set("Content-Type", "application/json")
+	w.WriteHeader(200)
+
+	return json.NewEncoder(w).Encode(response)
+}
+
+type JSONExample400Response = BadrequestResponse
+
+func (response JSONExample400Response) VisitJSONExampleResponse(w http.ResponseWriter) error {
+	w.WriteHeader(400)
+	return nil
+}
+
+type JSONExampledefaultResponse struct {
+	StatusCode int
+}
+
+func (response JSONExampledefaultResponse) VisitJSONExampleResponse(w http.ResponseWriter) error {
+	w.WriteHeader(response.StatusCode)
+	return nil
+}
+
+type MultipartExampleRequestObject struct {
+	Body *multipart.Reader
+}
+
+type MultipartExampleResponseObject interface {
+	VisitMultipartExampleResponse(w http.ResponseWriter) error
+}
+
+type MultipartExample200MultipartResponse func(writer *multipart.Writer) error
+
+func (response MultipartExample200MultipartResponse) VisitMultipartExampleResponse(w http.ResponseWriter) error {
+	writer := multipart.NewWriter(w)
+	w.Header().Set("Content-Type", writer.FormDataContentType())
+	w.WriteHeader(200)
+
+	defer writer.Close()
+	return response(writer)
+}
+
+type MultipartExample400Response = BadrequestResponse
+
+func (response MultipartExample400Response) VisitMultipartExampleResponse(w http.ResponseWriter) error {
+	w.WriteHeader(400)
+	return nil
+}
+
+type MultipartExampledefaultResponse struct {
+	StatusCode int
+}
+
+func (response MultipartExampledefaultResponse) VisitMultipartExampleResponse(w http.ResponseWriter) error {
+	w.WriteHeader(response.StatusCode)
+	return nil
+}
+
+type MultipleRequestAndResponseTypesRequestObject struct {
+	JSONBody      *MultipleRequestAndResponseTypesJSONRequestBody
+	FormdataBody  *MultipleRequestAndResponseTypesFormdataRequestBody
+	Body          io.Reader
+	MultipartBody *multipart.Reader
+	TextBody      *MultipleRequestAndResponseTypesTextRequestBody
+}
+
+type MultipleRequestAndResponseTypesResponseObject interface {
+	VisitMultipleRequestAndResponseTypesResponse(w http.ResponseWriter) error
+}
+
+type MultipleRequestAndResponseTypes200JSONResponse Example
+
+func (response MultipleRequestAndResponseTypes200JSONResponse) VisitMultipleRequestAndResponseTypesResponse(w http.ResponseWriter) error {
+	w.Header().Set("Content-Type", "application/json")
+	w.WriteHeader(200)
+
+	return json.NewEncoder(w).Encode(response)
+}
+
+type MultipleRequestAndResponseTypes200FormdataResponse Example
+
+func (response MultipleRequestAndResponseTypes200FormdataResponse) VisitMultipleRequestAndResponseTypesResponse(w http.ResponseWriter) error {
+	w.Header().Set("Content-Type", "application/x-www-form-urlencoded")
+	w.WriteHeader(200)
+
+	if form, err := runtime.MarshalForm(response, nil); err != nil {
+		return err
+	} else {
+		_, err := w.Write([]byte(form.Encode()))
+		return err
+	}
+}
+
+type MultipleRequestAndResponseTypes200ImagepngResponse struct {
+	Body          io.Reader
+	ContentLength int64
+}
+
+func (response MultipleRequestAndResponseTypes200ImagepngResponse) VisitMultipleRequestAndResponseTypesResponse(w http.ResponseWriter) error {
+	w.Header().Set("Content-Type", "image/png")
+	if response.ContentLength != 0 {
+		w.Header().Set("Content-Length", fmt.Sprint(response.ContentLength))
+	}
+	w.WriteHeader(200)
+
+	if closer, ok := response.Body.(io.ReadCloser); ok {
+		defer closer.Close()
+	}
+	_, err := io.Copy(w, response.Body)
+	return err
+}
+
+type MultipleRequestAndResponseTypes200MultipartResponse func(writer *multipart.Writer) error
+
+func (response MultipleRequestAndResponseTypes200MultipartResponse) VisitMultipleRequestAndResponseTypesResponse(w http.ResponseWriter) error {
+	writer := multipart.NewWriter(w)
+	w.Header().Set("Content-Type", writer.FormDataContentType())
+	w.WriteHeader(200)
+
+	defer writer.Close()
+	return response(writer)
+}
+
+type MultipleRequestAndResponseTypes200TextResponse string
+
+func (response MultipleRequestAndResponseTypes200TextResponse) VisitMultipleRequestAndResponseTypesResponse(w http.ResponseWriter) error {
+	w.Header().Set("Content-Type", "text/plain")
+	w.WriteHeader(200)
+
+	_, err := w.Write([]byte(response))
+	return err
+}
+
+type MultipleRequestAndResponseTypes400Response = BadrequestResponse
+
+func (response MultipleRequestAndResponseTypes400Response) VisitMultipleRequestAndResponseTypesResponse(w http.ResponseWriter) error {
+	w.WriteHeader(400)
+	return nil
+}
+
+type ReservedGoKeywordParametersRequestObject struct {
+	Type string `json:"type"`
+}
+
+type ReservedGoKeywordParametersResponseObject interface {
+	VisitReservedGoKeywordParametersResponse(w http.ResponseWriter) error
+}
+
+type ReservedGoKeywordParameters200TextResponse string
+
+func (response ReservedGoKeywordParameters200TextResponse) VisitReservedGoKeywordParametersResponse(w http.ResponseWriter) error {
+	w.Header().Set("Content-Type", "text/plain")
+	w.WriteHeader(200)
+
+	_, err := w.Write([]byte(response))
+	return err
+}
+
+type ReusableResponsesRequestObject struct {
+	Body *ReusableResponsesJSONRequestBody
+}
+
+type ReusableResponsesResponseObject interface {
+	VisitReusableResponsesResponse(w http.ResponseWriter) error
+}
+
+type ReusableResponses200JSONResponse struct{ ReusableresponseJSONResponse }
+
+func (response ReusableResponses200JSONResponse) VisitReusableResponsesResponse(w http.ResponseWriter) error {
+	w.Header().Set("Content-Type", "application/json")
+	w.Header().Set("header1", fmt.Sprint(response.Headers.Header1))
+	w.Header().Set("header2", fmt.Sprint(response.Headers.Header2))
+	w.WriteHeader(200)
+
+	return json.NewEncoder(w).Encode(response.Body)
+}
+
+type ReusableResponses400Response = BadrequestResponse
+
+func (response ReusableResponses400Response) VisitReusableResponsesResponse(w http.ResponseWriter) error {
+	w.WriteHeader(400)
+	return nil
+}
+
+type ReusableResponsesdefaultResponse struct {
+	StatusCode int
+}
+
+func (response ReusableResponsesdefaultResponse) VisitReusableResponsesResponse(w http.ResponseWriter) error {
+	w.WriteHeader(response.StatusCode)
+	return nil
+}
+
+type TextExampleRequestObject struct {
+	Body *TextExampleTextRequestBody
+}
+
+type TextExampleResponseObject interface {
+	VisitTextExampleResponse(w http.ResponseWriter) error
+}
+
+type TextExample200TextResponse string
+
+func (response TextExample200TextResponse) VisitTextExampleResponse(w http.ResponseWriter) error {
+	w.Header().Set("Content-Type", "text/plain")
+	w.WriteHeader(200)
+
+	_, err := w.Write([]byte(response))
+	return err
+}
+
+type TextExample400Response = BadrequestResponse
+
+func (response TextExample400Response) VisitTextExampleResponse(w http.ResponseWriter) error {
+	w.WriteHeader(400)
+	return nil
+}
+
+type TextExampledefaultResponse struct {
+	StatusCode int
+}
+
+func (response TextExampledefaultResponse) VisitTextExampleResponse(w http.ResponseWriter) error {
+	w.WriteHeader(response.StatusCode)
+	return nil
+}
+
+type UnknownExampleRequestObject struct {
+	Body io.Reader
+}
+
+type UnknownExampleResponseObject interface {
+	VisitUnknownExampleResponse(w http.ResponseWriter) error
+}
+
+type UnknownExample200Videomp4Response struct {
+	Body          io.Reader
+	ContentLength int64
+}
+
+func (response UnknownExample200Videomp4Response) VisitUnknownExampleResponse(w http.ResponseWriter) error {
+	w.Header().Set("Content-Type", "video/mp4")
+	if response.ContentLength != 0 {
+		w.Header().Set("Content-Length", fmt.Sprint(response.ContentLength))
+	}
+	w.WriteHeader(200)
+
+	if closer, ok := response.Body.(io.ReadCloser); ok {
+		defer closer.Close()
+	}
+	_, err := io.Copy(w, response.Body)
+	return err
+}
+
+type UnknownExample400Response = BadrequestResponse
+
+func (response UnknownExample400Response) VisitUnknownExampleResponse(w http.ResponseWriter) error {
+	w.WriteHeader(400)
+	return nil
+}
+
+type UnknownExampledefaultResponse struct {
+	StatusCode int
+}
+
+func (response UnknownExampledefaultResponse) VisitUnknownExampleResponse(w http.ResponseWriter) error {
+	w.WriteHeader(response.StatusCode)
+	return nil
+}
+
+type UnspecifiedContentTypeRequestObject struct {
+	ContentType string
+	Body        io.Reader
+}
+
+type UnspecifiedContentTypeResponseObject interface {
+	VisitUnspecifiedContentTypeResponse(w http.ResponseWriter) error
+}
+
+type UnspecifiedContentType200VideoResponse struct {
+	Body          io.Reader
+	ContentType   string
+	ContentLength int64
+}
+
+func (response UnspecifiedContentType200VideoResponse) VisitUnspecifiedContentTypeResponse(w http.ResponseWriter) error {
+	w.Header().Set("Content-Type", response.ContentType)
+	if response.ContentLength != 0 {
+		w.Header().Set("Content-Length", fmt.Sprint(response.ContentLength))
+	}
+	w.WriteHeader(200)
+
+	if closer, ok := response.Body.(io.ReadCloser); ok {
+		defer closer.Close()
+	}
+	_, err := io.Copy(w, response.Body)
+	return err
+}
+
+type UnspecifiedContentType400Response = BadrequestResponse
+
+func (response UnspecifiedContentType400Response) VisitUnspecifiedContentTypeResponse(w http.ResponseWriter) error {
+	w.WriteHeader(400)
+	return nil
+}
+
+type UnspecifiedContentType401Response struct {
+}
+
+func (response UnspecifiedContentType401Response) VisitUnspecifiedContentTypeResponse(w http.ResponseWriter) error {
+	w.WriteHeader(401)
+	return nil
+}
+
+type UnspecifiedContentType403Response struct {
+}
+
+func (response UnspecifiedContentType403Response) VisitUnspecifiedContentTypeResponse(w http.ResponseWriter) error {
+	w.WriteHeader(403)
+	return nil
+}
+
+type UnspecifiedContentTypedefaultResponse struct {
+	StatusCode int
+}
+
+func (response UnspecifiedContentTypedefaultResponse) VisitUnspecifiedContentTypeResponse(w http.ResponseWriter) error {
+	w.WriteHeader(response.StatusCode)
+	return nil
+}
+
+type URLEncodedExampleRequestObject struct {
+	Body *URLEncodedExampleFormdataRequestBody
+}
+
+type URLEncodedExampleResponseObject interface {
+	VisitURLEncodedExampleResponse(w http.ResponseWriter) error
+}
+
+type URLEncodedExample200FormdataResponse Example
+
+func (response URLEncodedExample200FormdataResponse) VisitURLEncodedExampleResponse(w http.ResponseWriter) error {
+	w.Header().Set("Content-Type", "application/x-www-form-urlencoded")
+	w.WriteHeader(200)
+
+	if form, err := runtime.MarshalForm(response, nil); err != nil {
+		return err
+	} else {
+		_, err := w.Write([]byte(form.Encode()))
+		return err
+	}
+}
+
+type URLEncodedExample400Response = BadrequestResponse
+
+func (response URLEncodedExample400Response) VisitURLEncodedExampleResponse(w http.ResponseWriter) error {
+	w.WriteHeader(400)
+	return nil
+}
+
+type URLEncodedExampledefaultResponse struct {
+	StatusCode int
+}
+
+func (response URLEncodedExampledefaultResponse) VisitURLEncodedExampleResponse(w http.ResponseWriter) error {
+	w.WriteHeader(response.StatusCode)
+	return nil
+}
+
+type HeadersExampleRequestObject struct {
+	Params HeadersExampleParams
+	Body   *HeadersExampleJSONRequestBody
+}
+
+type HeadersExampleResponseObject interface {
+	VisitHeadersExampleResponse(w http.ResponseWriter) error
+}
+
+type HeadersExample200ResponseHeaders struct {
+	Header1 string
+	Header2 int
+}
+
+type HeadersExample200JSONResponse struct {
+	Body    Example
+	Headers HeadersExample200ResponseHeaders
+}
+
+func (response HeadersExample200JSONResponse) VisitHeadersExampleResponse(w http.ResponseWriter) error {
+	w.Header().Set("Content-Type", "application/json")
+	w.Header().Set("header1", fmt.Sprint(response.Headers.Header1))
+	w.Header().Set("header2", fmt.Sprint(response.Headers.Header2))
+	w.WriteHeader(200)
+
+	return json.NewEncoder(w).Encode(response.Body)
+}
+
+type HeadersExample400Response = BadrequestResponse
+
+func (response HeadersExample400Response) VisitHeadersExampleResponse(w http.ResponseWriter) error {
+	w.WriteHeader(400)
+	return nil
+}
+
+type HeadersExampledefaultResponse struct {
+	StatusCode int
+}
+
+func (response HeadersExampledefaultResponse) VisitHeadersExampleResponse(w http.ResponseWriter) error {
+	w.WriteHeader(response.StatusCode)
+	return nil
+}
+
+type UnionExampleRequestObject struct {
+	Body *UnionExampleJSONRequestBody
+}
+
+type UnionExampleResponseObject interface {
+	VisitUnionExampleResponse(w http.ResponseWriter) error
+}
+
+type UnionExample200ResponseHeaders struct {
+	Header1 string
+	Header2 int
+}
+
+type UnionExample200ApplicationAlternativePlusJSONResponse struct {
+	Body    Example
+	Headers UnionExample200ResponseHeaders
+}
+
+func (response UnionExample200ApplicationAlternativePlusJSONResponse) VisitUnionExampleResponse(w http.ResponseWriter) error {
+	w.Header().Set("Content-Type", "application/alternative+json")
+	w.Header().Set("header1", fmt.Sprint(response.Headers.Header1))
+	w.Header().Set("header2", fmt.Sprint(response.Headers.Header2))
+	w.WriteHeader(200)
+
+	return json.NewEncoder(w).Encode(response.Body)
+}
+
+type UnionExample200JSONResponse struct {
+	Body struct {
+		union json.RawMessage
+	}
+	Headers UnionExample200ResponseHeaders
+}
+
+func (response UnionExample200JSONResponse) VisitUnionExampleResponse(w http.ResponseWriter) error {
+	w.Header().Set("Content-Type", "application/json")
+	w.Header().Set("header1", fmt.Sprint(response.Headers.Header1))
+	w.Header().Set("header2", fmt.Sprint(response.Headers.Header2))
+	w.WriteHeader(200)
+
+	return json.NewEncoder(w).Encode(response.Body.union)
+}
+
+type UnionExample400Response = BadrequestResponse
+
+func (response UnionExample400Response) VisitUnionExampleResponse(w http.ResponseWriter) error {
+	w.WriteHeader(400)
+	return nil
+}
+
+type UnionExampledefaultResponse struct {
+	StatusCode int
+}
+
+func (response UnionExampledefaultResponse) VisitUnionExampleResponse(w http.ResponseWriter) error {
+	w.WriteHeader(response.StatusCode)
+	return nil
+}
+
+// StrictServerInterface represents all server handlers.
+type StrictServerInterface interface {
+
+	// (POST /json)
+	JSONExample(ctx context.Context, request JSONExampleRequestObject) (JSONExampleResponseObject, error)
+
+	// (POST /multipart)
+	MultipartExample(ctx context.Context, request MultipartExampleRequestObject) (MultipartExampleResponseObject, error)
+
+	// (POST /multiple)
+	MultipleRequestAndResponseTypes(ctx context.Context, request MultipleRequestAndResponseTypesRequestObject) (MultipleRequestAndResponseTypesResponseObject, error)
+
+	// (GET /reserved-go-keyword-parameters/{type})
+	ReservedGoKeywordParameters(ctx context.Context, request ReservedGoKeywordParametersRequestObject) (ReservedGoKeywordParametersResponseObject, error)
+
+	// (POST /reusable-responses)
+	ReusableResponses(ctx context.Context, request ReusableResponsesRequestObject) (ReusableResponsesResponseObject, error)
+
+	// (POST /text)
+	TextExample(ctx context.Context, request TextExampleRequestObject) (TextExampleResponseObject, error)
+
+	// (POST /unknown)
+	UnknownExample(ctx context.Context, request UnknownExampleRequestObject) (UnknownExampleResponseObject, error)
+
+	// (POST /unspecified-content-type)
+	UnspecifiedContentType(ctx context.Context, request UnspecifiedContentTypeRequestObject) (UnspecifiedContentTypeResponseObject, error)
+
+	// (POST /urlencoded)
+	URLEncodedExample(ctx context.Context, request URLEncodedExampleRequestObject) (URLEncodedExampleResponseObject, error)
+
+	// (POST /with-headers)
+	HeadersExample(ctx context.Context, request HeadersExampleRequestObject) (HeadersExampleResponseObject, error)
+
+	// (POST /with-union)
+	UnionExample(ctx context.Context, request UnionExampleRequestObject) (UnionExampleResponseObject, error)
+}
+
+type StrictHandlerFunc = runtime.StrictHttpHandlerFunc
+type StrictMiddlewareFunc = runtime.StrictHttpMiddlewareFunc
+
+type StrictHTTPServerOptions struct {
+	RequestErrorHandlerFunc  func(w http.ResponseWriter, r *http.Request, err error)
+	ResponseErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error)
+}
+
+func NewStrictHandler(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc) ServerInterface {
+	return &strictHandler{ssi: ssi, middlewares: middlewares, options: StrictHTTPServerOptions{
+		RequestErrorHandlerFunc: func(w http.ResponseWriter, r *http.Request, err error) {
+			http.Error(w, err.Error(), http.StatusBadRequest)
+		},
+		ResponseErrorHandlerFunc: func(w http.ResponseWriter, r *http.Request, err error) {
+			http.Error(w, err.Error(), http.StatusInternalServerError)
+		},
+	}}
+}
+
+func NewStrictHandlerWithOptions(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc, options StrictHTTPServerOptions) ServerInterface {
+	return &strictHandler{ssi: ssi, middlewares: middlewares, options: options}
+}
+
+type strictHandler struct {
+	ssi         StrictServerInterface
+	middlewares []StrictMiddlewareFunc
+	options     StrictHTTPServerOptions
+}
+
+// JSONExample operation middleware
+func (sh *strictHandler) JSONExample(w http.ResponseWriter, r *http.Request) {
+	var request JSONExampleRequestObject
+
+	var body JSONExampleJSONRequestBody
+	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
+		sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode JSON body: %w", err))
+		return
+	}
+	request.Body = &body
+
+	handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) {
+		return sh.ssi.JSONExample(ctx, request.(JSONExampleRequestObject))
+	}
+	for _, middleware := range sh.middlewares {
+		handler = middleware(handler, "JSONExample")
+	}
+
+	response, err := handler(r.Context(), w, r, request)
+
+	if err != nil {
+		sh.options.ResponseErrorHandlerFunc(w, r, err)
+	} else if validResponse, ok := response.(JSONExampleResponseObject); ok {
+		if err := validResponse.VisitJSONExampleResponse(w); err != nil {
+			sh.options.ResponseErrorHandlerFunc(w, r, err)
+		}
+	} else if response != nil {
+		sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response))
+	}
+}
+
+// MultipartExample operation middleware
+func (sh *strictHandler) MultipartExample(w http.ResponseWriter, r *http.Request) {
+	var request MultipartExampleRequestObject
+
+	if reader, err := r.MultipartReader(); err != nil {
+		sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode multipart body: %w", err))
+		return
+	} else {
+		request.Body = reader
+	}
+
+	handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) {
+		return sh.ssi.MultipartExample(ctx, request.(MultipartExampleRequestObject))
+	}
+	for _, middleware := range sh.middlewares {
+		handler = middleware(handler, "MultipartExample")
+	}
+
+	response, err := handler(r.Context(), w, r, request)
+
+	if err != nil {
+		sh.options.ResponseErrorHandlerFunc(w, r, err)
+	} else if validResponse, ok := response.(MultipartExampleResponseObject); ok {
+		if err := validResponse.VisitMultipartExampleResponse(w); err != nil {
+			sh.options.ResponseErrorHandlerFunc(w, r, err)
+		}
+	} else if response != nil {
+		sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response))
+	}
+}
+
+// MultipleRequestAndResponseTypes operation middleware
+func (sh *strictHandler) MultipleRequestAndResponseTypes(w http.ResponseWriter, r *http.Request) {
+	var request MultipleRequestAndResponseTypesRequestObject
+
+	if strings.HasPrefix(r.Header.Get("Content-Type"), "application/json") {
+
+		var body MultipleRequestAndResponseTypesJSONRequestBody
+		if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
+			sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode JSON body: %w", err))
+			return
+		}
+		request.JSONBody = &body
+	}
+	if strings.HasPrefix(r.Header.Get("Content-Type"), "application/x-www-form-urlencoded") {
+		if err := r.ParseForm(); err != nil {
+			sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode formdata: %w", err))
+			return
+		}
+		var body MultipleRequestAndResponseTypesFormdataRequestBody
+		if err := runtime.BindForm(&body, r.Form, nil, nil); err != nil {
+			sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't bind formdata: %w", err))
+			return
+		}
+		request.FormdataBody = &body
+	}
+	if strings.HasPrefix(r.Header.Get("Content-Type"), "image/png") {
+		request.Body = r.Body
+	}
+	if strings.HasPrefix(r.Header.Get("Content-Type"), "multipart/form-data") {
+		if reader, err := r.MultipartReader(); err != nil {
+			sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode multipart body: %w", err))
+			return
+		} else {
+			request.MultipartBody = reader
+		}
+	}
+	if strings.HasPrefix(r.Header.Get("Content-Type"), "text/plain") {
+		data, err := io.ReadAll(r.Body)
+		if err != nil {
+			sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't read body: %w", err))
+			return
+		}
+		body := MultipleRequestAndResponseTypesTextRequestBody(data)
+		request.TextBody = &body
+	}
+
+	handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) {
+		return sh.ssi.MultipleRequestAndResponseTypes(ctx, request.(MultipleRequestAndResponseTypesRequestObject))
+	}
+	for _, middleware := range sh.middlewares {
+		handler = middleware(handler, "MultipleRequestAndResponseTypes")
+	}
+
+	response, err := handler(r.Context(), w, r, request)
+
+	if err != nil {
+		sh.options.ResponseErrorHandlerFunc(w, r, err)
+	} else if validResponse, ok := response.(MultipleRequestAndResponseTypesResponseObject); ok {
+		if err := validResponse.VisitMultipleRequestAndResponseTypesResponse(w); err != nil {
+			sh.options.ResponseErrorHandlerFunc(w, r, err)
+		}
+	} else if response != nil {
+		sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response))
+	}
+}
+
+// ReservedGoKeywordParameters operation middleware
+func (sh *strictHandler) ReservedGoKeywordParameters(w http.ResponseWriter, r *http.Request, pType string) {
+	var request ReservedGoKeywordParametersRequestObject
+
+	request.Type = pType
+
+	handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) {
+		return sh.ssi.ReservedGoKeywordParameters(ctx, request.(ReservedGoKeywordParametersRequestObject))
+	}
+	for _, middleware := range sh.middlewares {
+		handler = middleware(handler, "ReservedGoKeywordParameters")
+	}
+
+	response, err := handler(r.Context(), w, r, request)
+
+	if err != nil {
+		sh.options.ResponseErrorHandlerFunc(w, r, err)
+	} else if validResponse, ok := response.(ReservedGoKeywordParametersResponseObject); ok {
+		if err := validResponse.VisitReservedGoKeywordParametersResponse(w); err != nil {
+			sh.options.ResponseErrorHandlerFunc(w, r, err)
+		}
+	} else if response != nil {
+		sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response))
+	}
+}
+
+// ReusableResponses operation middleware
+func (sh *strictHandler) ReusableResponses(w http.ResponseWriter, r *http.Request) {
+	var request ReusableResponsesRequestObject
+
+	var body ReusableResponsesJSONRequestBody
+	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
+		sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode JSON body: %w", err))
+		return
+	}
+	request.Body = &body
+
+	handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) {
+		return sh.ssi.ReusableResponses(ctx, request.(ReusableResponsesRequestObject))
+	}
+	for _, middleware := range sh.middlewares {
+		handler = middleware(handler, "ReusableResponses")
+	}
+
+	response, err := handler(r.Context(), w, r, request)
+
+	if err != nil {
+		sh.options.ResponseErrorHandlerFunc(w, r, err)
+	} else if validResponse, ok := response.(ReusableResponsesResponseObject); ok {
+		if err := validResponse.VisitReusableResponsesResponse(w); err != nil {
+			sh.options.ResponseErrorHandlerFunc(w, r, err)
+		}
+	} else if response != nil {
+		sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response))
+	}
+}
+
+// TextExample operation middleware
+func (sh *strictHandler) TextExample(w http.ResponseWriter, r *http.Request) {
+	var request TextExampleRequestObject
+
+	data, err := io.ReadAll(r.Body)
+	if err != nil {
+		sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't read body: %w", err))
+		return
+	}
+	body := TextExampleTextRequestBody(data)
+	request.Body = &body
+
+	handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) {
+		return sh.ssi.TextExample(ctx, request.(TextExampleRequestObject))
+	}
+	for _, middleware := range sh.middlewares {
+		handler = middleware(handler, "TextExample")
+	}
+
+	response, err := handler(r.Context(), w, r, request)
+
+	if err != nil {
+		sh.options.ResponseErrorHandlerFunc(w, r, err)
+	} else if validResponse, ok := response.(TextExampleResponseObject); ok {
+		if err := validResponse.VisitTextExampleResponse(w); err != nil {
+			sh.options.ResponseErrorHandlerFunc(w, r, err)
+		}
+	} else if response != nil {
+		sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response))
+	}
+}
+
+// UnknownExample operation middleware
+func (sh *strictHandler) UnknownExample(w http.ResponseWriter, r *http.Request) {
+	var request UnknownExampleRequestObject
+
+	request.Body = r.Body
+
+	handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) {
+		return sh.ssi.UnknownExample(ctx, request.(UnknownExampleRequestObject))
+	}
+	for _, middleware := range sh.middlewares {
+		handler = middleware(handler, "UnknownExample")
+	}
+
+	response, err := handler(r.Context(), w, r, request)
+
+	if err != nil {
+		sh.options.ResponseErrorHandlerFunc(w, r, err)
+	} else if validResponse, ok := response.(UnknownExampleResponseObject); ok {
+		if err := validResponse.VisitUnknownExampleResponse(w); err != nil {
+			sh.options.ResponseErrorHandlerFunc(w, r, err)
+		}
+	} else if response != nil {
+		sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response))
+	}
+}
+
+// UnspecifiedContentType operation middleware
+func (sh *strictHandler) UnspecifiedContentType(w http.ResponseWriter, r *http.Request) {
+	var request UnspecifiedContentTypeRequestObject
+
+	request.ContentType = r.Header.Get("Content-Type")
+
+	request.Body = r.Body
+
+	handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) {
+		return sh.ssi.UnspecifiedContentType(ctx, request.(UnspecifiedContentTypeRequestObject))
+	}
+	for _, middleware := range sh.middlewares {
+		handler = middleware(handler, "UnspecifiedContentType")
+	}
+
+	response, err := handler(r.Context(), w, r, request)
+
+	if err != nil {
+		sh.options.ResponseErrorHandlerFunc(w, r, err)
+	} else if validResponse, ok := response.(UnspecifiedContentTypeResponseObject); ok {
+		if err := validResponse.VisitUnspecifiedContentTypeResponse(w); err != nil {
+			sh.options.ResponseErrorHandlerFunc(w, r, err)
+		}
+	} else if response != nil {
+		sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response))
+	}
+}
+
+// URLEncodedExample operation middleware
+func (sh *strictHandler) URLEncodedExample(w http.ResponseWriter, r *http.Request) {
+	var request URLEncodedExampleRequestObject
+
+	if err := r.ParseForm(); err != nil {
+		sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode formdata: %w", err))
+		return
+	}
+	var body URLEncodedExampleFormdataRequestBody
+	if err := runtime.BindForm(&body, r.Form, nil, nil); err != nil {
+		sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't bind formdata: %w", err))
+		return
+	}
+	request.Body = &body
+
+	handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) {
+		return sh.ssi.URLEncodedExample(ctx, request.(URLEncodedExampleRequestObject))
+	}
+	for _, middleware := range sh.middlewares {
+		handler = middleware(handler, "URLEncodedExample")
+	}
+
+	response, err := handler(r.Context(), w, r, request)
+
+	if err != nil {
+		sh.options.ResponseErrorHandlerFunc(w, r, err)
+	} else if validResponse, ok := response.(URLEncodedExampleResponseObject); ok {
+		if err := validResponse.VisitURLEncodedExampleResponse(w); err != nil {
+			sh.options.ResponseErrorHandlerFunc(w, r, err)
+		}
+	} else if response != nil {
+		sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response))
+	}
+}
+
+// HeadersExample operation middleware
+func (sh *strictHandler) HeadersExample(w http.ResponseWriter, r *http.Request, params HeadersExampleParams) {
+	var request HeadersExampleRequestObject
+
+	request.Params = params
+
+	var body HeadersExampleJSONRequestBody
+	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
+		sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode JSON body: %w", err))
+		return
+	}
+	request.Body = &body
+
+	handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) {
+		return sh.ssi.HeadersExample(ctx, request.(HeadersExampleRequestObject))
+	}
+	for _, middleware := range sh.middlewares {
+		handler = middleware(handler, "HeadersExample")
+	}
+
+	response, err := handler(r.Context(), w, r, request)
+
+	if err != nil {
+		sh.options.ResponseErrorHandlerFunc(w, r, err)
+	} else if validResponse, ok := response.(HeadersExampleResponseObject); ok {
+		if err := validResponse.VisitHeadersExampleResponse(w); err != nil {
+			sh.options.ResponseErrorHandlerFunc(w, r, err)
+		}
+	} else if response != nil {
+		sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response))
+	}
+}
+
+// UnionExample operation middleware
+func (sh *strictHandler) UnionExample(w http.ResponseWriter, r *http.Request) {
+	var request UnionExampleRequestObject
+
+	var body UnionExampleJSONRequestBody
+	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
+		sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode JSON body: %w", err))
+		return
+	}
+	request.Body = &body
+
+	handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) {
+		return sh.ssi.UnionExample(ctx, request.(UnionExampleRequestObject))
+	}
+	for _, middleware := range sh.middlewares {
+		handler = middleware(handler, "UnionExample")
+	}
+
+	response, err := handler(r.Context(), w, r, request)
+
+	if err != nil {
+		sh.options.ResponseErrorHandlerFunc(w, r, err)
+	} else if validResponse, ok := response.(UnionExampleResponseObject); ok {
+		if err := validResponse.VisitUnionExampleResponse(w); err != nil {
+			sh.options.ResponseErrorHandlerFunc(w, r, err)
+		}
+	} else if response != nil {
+		sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response))
+	}
+}
+
+// Base64 encoded, gzipped, json marshaled Swagger object
+var swaggerSpec = []string{
+
+	"H4sIAAAAAAAC/+xYS2/jNhD+KwTb01aynGxOunWDxbbdtimc5FTkQIsjm7sSyQ5HVgzD/72gKL9ixbW3",
+	"fhRBb3oMvxl+8+BwZjwzpTUaNDmezjiCs0Y7aF6GQiL8VYEj/ybBZagsKaN5yj8IOWj/zSOOUDkxLGCx",
+	"3MtnRhPoZqmwtlCZ8EuTL86vn3GXjaEU/ul7hJyn/LtkZUoS/roEnkVpC+Dz+Tx6YcHdZx7xMQgJ2Fgb",
+	"Hq82sWlqgafcESo94h4kiF13iilNMAL02rxoa4QXWNiRzrhFYwFJBY4moqigW1P7xQy/QEZhB0rnZpvL",
+	"W6NJKO2YVHkOCJpYSx7zGI65ylqDBJINp8xryIg5wAkgjzgp8obx+/XvrDXY8YhPAF1QdNXr9/reX8aC",
+	"FlbxlL9vPkXcCho3G1o6yJouv/9yf/c7U46JikwpSGWiKKasFOjGogDJlCbjTawycj3eaMLG8T/LdvXH",
+	"lkofNU0AfTByeoqAaeJyLZyv+/0zxeU84jdBWRfG0qhkLcEamFxURQfnj/qrNrVmgGiw3VlSVgUpK5DW",
+	"fbXJ9m8LkX0oX+IlucEyloLEiVg/lqaLEt/Wgs4cuR+b2rGxqRkZJkEUrFY0ZouFL5JbaSaYU3pUAFsY",
+	"FXV6soC25P6o5aDdy4PHOHkuRRsoz3Fd13HjvAoL0JmRIL8NVpViBInVo83lHlsQT/lwSj5st4vrkYIo",
+	"4gTPlNhCKL375DhTOfmf6aMldkhXhOZElPHIxF9hWhuUsRUoSiBAl8y89rkHHkFHKv+xlGSZ0GwITIsS",
+	"JBM5AbJPhrWQbitlB63eT+ZzEFlBNcft8iX9c8Y9Jc0RzCPuFfA0sBLyWqF3OmEF0Q7anv4xPv+VAxZs",
+	"hkYv3lDVXQYXJWpJHULufEns8lwHf0HTYE3iMg3D7ojban3PcQZ5T75+7j/A815H/hFL37lz+1DCqvDx",
+	"dc7aVfvQ9o2VdA8WJ0qCSUp7cyDyxUh1FjKVK5Bxu4s42PZaSbg1OkOgzRbIXye0IbYE87ccGgMLDETM",
+	"GVYDKytHzArnmKKmihQq3JQkbBWPx5Vlt0HTw6qc7vLquxP59N2lPHrTvzp8yfsTx81GK/NKPg5+/Rhk",
+	"Dr0vHq1nOrDjO57eC6Wzv6TEawOV7hT+KQiszvQM1MR3RFoyBKpQg2QTJRZDgK3cbAFWbu3qhYIZq25o",
+	"Mdw5pCGKdmJd82jXAOjpDY8nTjk2O1ecVlrtGlM9+t+s7aFfng3K6P/oEEoUBKgFqQn8cJwb5DaK0XCX",
+	"N5n2wsvRnhqe3l5UzSMe5qahBFVY+DpBZNMkCfPWnqvFaATYUyYRVnkW/g4AAP//Pk3lbjwXAAA=",
+}
+
+// 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: %w", err)
+	}
+	zr, err := gzip.NewReader(bytes.NewReader(zipped))
+	if err != nil {
+		return nil, fmt.Errorf("error decompressing spec: %w", err)
+	}
+	var buf bytes.Buffer
+	_, err = buf.ReadFrom(zr)
+	if err != nil {
+		return nil, fmt.Errorf("error decompressing spec: %w", 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) {
+	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) {
+	resolvePath := PathToRawSpec("")
+
+	loader := openapi3.NewLoader()
+	loader.IsExternalRefsAllowed = true
+	loader.ReadFromURIFunc = func(loader *openapi3.Loader, url *url.URL) ([]byte, error) {
+		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/strict-server/gorilla/server.go b/internal/test/strict-server/gorilla/server.go
new file mode 100644
index 0000000..669d4da
--- /dev/null
+++ b/internal/test/strict-server/gorilla/server.go
@@ -0,0 +1,120 @@
+//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen --config=server.cfg.yaml ../strict-schema.yaml
+//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen --config=types.cfg.yaml ../strict-schema.yaml
+
+package api
+
+import (
+	"context"
+	"encoding/json"
+	"io"
+	"mime/multipart"
+)
+
+type StrictServer struct {
+}
+
+func (s StrictServer) JSONExample(ctx context.Context, request JSONExampleRequestObject) (JSONExampleResponseObject, error) {
+	return JSONExample200JSONResponse(*request.Body), nil
+}
+
+func (s StrictServer) MultipartExample(ctx context.Context, request MultipartExampleRequestObject) (MultipartExampleResponseObject, error) {
+	return MultipartExample200MultipartResponse(func(writer *multipart.Writer) error {
+		for {
+			part, err := request.Body.NextPart()
+			if err == io.EOF {
+				return nil
+			} else if err != nil {
+				return err
+			}
+			w, err := writer.CreatePart(part.Header)
+			if err != nil {
+				return err
+			}
+			_, err = io.Copy(w, part)
+			if err != nil {
+				return err
+			}
+			if err = part.Close(); err != nil {
+				return err
+			}
+		}
+	}), nil
+}
+
+func (s StrictServer) MultipleRequestAndResponseTypes(ctx context.Context, request MultipleRequestAndResponseTypesRequestObject) (MultipleRequestAndResponseTypesResponseObject, error) {
+	switch {
+	case request.Body != nil:
+		return MultipleRequestAndResponseTypes200ImagepngResponse{Body: request.Body}, nil
+	case request.JSONBody != nil:
+		return MultipleRequestAndResponseTypes200JSONResponse(*request.JSONBody), nil
+	case request.FormdataBody != nil:
+		return MultipleRequestAndResponseTypes200FormdataResponse(*request.FormdataBody), nil
+	case request.TextBody != nil:
+		return MultipleRequestAndResponseTypes200TextResponse(*request.TextBody), nil
+	case request.MultipartBody != nil:
+		return MultipleRequestAndResponseTypes200MultipartResponse(func(writer *multipart.Writer) error {
+			for {
+				part, err := request.MultipartBody.NextPart()
+				if err == io.EOF {
+					return nil
+				} else if err != nil {
+					return err
+				}
+				w, err := writer.CreatePart(part.Header)
+				if err != nil {
+					return err
+				}
+				_, err = io.Copy(w, part)
+				if err != nil {
+					return err
+				}
+				if err = part.Close(); err != nil {
+					return err
+				}
+			}
+		}), nil
+	default:
+		return MultipleRequestAndResponseTypes400Response{}, nil
+	}
+}
+
+func (s StrictServer) TextExample(ctx context.Context, request TextExampleRequestObject) (TextExampleResponseObject, error) {
+	return TextExample200TextResponse(*request.Body), nil
+}
+
+func (s StrictServer) UnknownExample(ctx context.Context, request UnknownExampleRequestObject) (UnknownExampleResponseObject, error) {
+	return UnknownExample200Videomp4Response{Body: request.Body}, nil
+}
+
+func (s StrictServer) UnspecifiedContentType(ctx context.Context, request UnspecifiedContentTypeRequestObject) (UnspecifiedContentTypeResponseObject, error) {
+	return UnspecifiedContentType200VideoResponse{Body: request.Body, ContentType: request.ContentType}, nil
+}
+
+func (s StrictServer) URLEncodedExample(ctx context.Context, request URLEncodedExampleRequestObject) (URLEncodedExampleResponseObject, error) {
+	return URLEncodedExample200FormdataResponse(*request.Body), nil
+}
+
+func (s StrictServer) HeadersExample(ctx context.Context, request HeadersExampleRequestObject) (HeadersExampleResponseObject, error) {
+	return HeadersExample200JSONResponse{Body: *request.Body, Headers: HeadersExample200ResponseHeaders{Header1: request.Params.Header1, Header2: *request.Params.Header2}}, nil
+}
+
+func (s StrictServer) ReusableResponses(ctx context.Context, request ReusableResponsesRequestObject) (ReusableResponsesResponseObject, error) {
+	return ReusableResponses200JSONResponse{ReusableresponseJSONResponse: ReusableresponseJSONResponse{Body: *request.Body}}, nil
+}
+
+func (s StrictServer) ReservedGoKeywordParameters(ctx context.Context, request ReservedGoKeywordParametersRequestObject) (ReservedGoKeywordParametersResponseObject, error) {
+	return ReservedGoKeywordParameters200TextResponse(""), nil
+}
+
+func (s StrictServer) UnionExample(ctx context.Context, request UnionExampleRequestObject) (UnionExampleResponseObject, error) {
+	union, err := json.Marshal(*request.Body)
+	if err != nil {
+		return nil, err
+	}
+
+	return UnionExample200JSONResponse{
+		Body: struct{ union json.RawMessage }{
+			union: union,
+		},
+	}, nil
+}
diff --git a/internal/test/strict-server/gorilla/types.cfg.yaml b/internal/test/strict-server/gorilla/types.cfg.yaml
new file mode 100644
index 0000000..4ea1d8a
--- /dev/null
+++ b/internal/test/strict-server/gorilla/types.cfg.yaml
@@ -0,0 +1,4 @@
+package: api
+generate:
+  models: true
+output: types.gen.go
diff --git a/internal/test/strict-server/gorilla/types.gen.go b/internal/test/strict-server/gorilla/types.gen.go
new file mode 100644
index 0000000..023ebed
--- /dev/null
+++ b/internal/test/strict-server/gorilla/types.gen.go
@@ -0,0 +1,57 @@
+// Package api provides primitives to interact with the openapi HTTP API.
+//
+// Code generated by github.com/deepmap/oapi-codegen version (devel) DO NOT EDIT.
+package api
+
+// Example defines model for example.
+type Example struct {
+	Value *string `json:"value,omitempty"`
+}
+
+// Reusableresponse defines model for reusableresponse.
+type Reusableresponse = Example
+
+// MultipleRequestAndResponseTypesTextBody defines parameters for MultipleRequestAndResponseTypes.
+type MultipleRequestAndResponseTypesTextBody = string
+
+// TextExampleTextBody defines parameters for TextExample.
+type TextExampleTextBody = string
+
+// HeadersExampleParams defines parameters for HeadersExample.
+type HeadersExampleParams struct {
+	Header1 string `json:"header1"`
+	Header2 *int   `json:"header2,omitempty"`
+}
+
+// JSONExampleJSONRequestBody defines body for JSONExample for application/json ContentType.
+type JSONExampleJSONRequestBody = Example
+
+// MultipartExampleMultipartRequestBody defines body for MultipartExample for multipart/form-data ContentType.
+type MultipartExampleMultipartRequestBody = Example
+
+// MultipleRequestAndResponseTypesJSONRequestBody defines body for MultipleRequestAndResponseTypes for application/json ContentType.
+type MultipleRequestAndResponseTypesJSONRequestBody = Example
+
+// MultipleRequestAndResponseTypesFormdataRequestBody defines body for MultipleRequestAndResponseTypes for application/x-www-form-urlencoded ContentType.
+type MultipleRequestAndResponseTypesFormdataRequestBody = Example
+
+// MultipleRequestAndResponseTypesMultipartRequestBody defines body for MultipleRequestAndResponseTypes for multipart/form-data ContentType.
+type MultipleRequestAndResponseTypesMultipartRequestBody = Example
+
+// MultipleRequestAndResponseTypesTextRequestBody defines body for MultipleRequestAndResponseTypes for text/plain ContentType.
+type MultipleRequestAndResponseTypesTextRequestBody = MultipleRequestAndResponseTypesTextBody
+
+// ReusableResponsesJSONRequestBody defines body for ReusableResponses for application/json ContentType.
+type ReusableResponsesJSONRequestBody = Example
+
+// TextExampleTextRequestBody defines body for TextExample for text/plain ContentType.
+type TextExampleTextRequestBody = TextExampleTextBody
+
+// URLEncodedExampleFormdataRequestBody defines body for URLEncodedExample for application/x-www-form-urlencoded ContentType.
+type URLEncodedExampleFormdataRequestBody = Example
+
+// HeadersExampleJSONRequestBody defines body for HeadersExample for application/json ContentType.
+type HeadersExampleJSONRequestBody = Example
+
+// UnionExampleJSONRequestBody defines body for UnionExample for application/json ContentType.
+type UnionExampleJSONRequestBody = Example
diff --git a/internal/test/strict-server/iris/server.cfg.yaml b/internal/test/strict-server/iris/server.cfg.yaml
new file mode 100644
index 0000000..e1d7782
--- /dev/null
+++ b/internal/test/strict-server/iris/server.cfg.yaml
@@ -0,0 +1,6 @@
+package: api
+generate:
+  iris-server: true
+  strict-server: true
+  embedded-spec: true
+output: server.gen.go
diff --git a/internal/test/strict-server/iris/server.gen.go b/internal/test/strict-server/iris/server.gen.go
new file mode 100644
index 0000000..10d892a
--- /dev/null
+++ b/internal/test/strict-server/iris/server.gen.go
@@ -0,0 +1,1279 @@
+// Package api provides primitives to interact with the openapi HTTP API.
+//
+// Code generated by github.com/deepmap/oapi-codegen version (devel) DO NOT EDIT.
+package api
+
+import (
+	"bytes"
+	"compress/gzip"
+	"context"
+	"encoding/base64"
+	"encoding/json"
+	"fmt"
+	"io"
+	"mime/multipart"
+	"net/http"
+	"net/url"
+	"path"
+	"strings"
+
+	"github.com/deepmap/oapi-codegen/pkg/runtime"
+	"github.com/getkin/kin-openapi/openapi3"
+	"github.com/kataras/iris/v12"
+)
+
+// ServerInterface represents all server handlers.
+type ServerInterface interface {
+
+	// (POST /json)
+	JSONExample(ctx iris.Context)
+
+	// (POST /multipart)
+	MultipartExample(ctx iris.Context)
+
+	// (POST /multiple)
+	MultipleRequestAndResponseTypes(ctx iris.Context)
+
+	// (GET /reserved-go-keyword-parameters/{type})
+	ReservedGoKeywordParameters(ctx iris.Context, pType string)
+
+	// (POST /reusable-responses)
+	ReusableResponses(ctx iris.Context)
+
+	// (POST /text)
+	TextExample(ctx iris.Context)
+
+	// (POST /unknown)
+	UnknownExample(ctx iris.Context)
+
+	// (POST /unspecified-content-type)
+	UnspecifiedContentType(ctx iris.Context)
+
+	// (POST /urlencoded)
+	URLEncodedExample(ctx iris.Context)
+
+	// (POST /with-headers)
+	HeadersExample(ctx iris.Context, params HeadersExampleParams)
+
+	// (POST /with-union)
+	UnionExample(ctx iris.Context)
+}
+
+// ServerInterfaceWrapper converts echo contexts to parameters.
+type ServerInterfaceWrapper struct {
+	Handler ServerInterface
+}
+
+type MiddlewareFunc iris.Handler
+
+// JSONExample converts iris context to params.
+func (w *ServerInterfaceWrapper) JSONExample(ctx iris.Context) {
+
+	// Invoke the callback with all the unmarshaled arguments
+	w.Handler.JSONExample(ctx)
+}
+
+// MultipartExample converts iris context to params.
+func (w *ServerInterfaceWrapper) MultipartExample(ctx iris.Context) {
+
+	// Invoke the callback with all the unmarshaled arguments
+	w.Handler.MultipartExample(ctx)
+}
+
+// MultipleRequestAndResponseTypes converts iris context to params.
+func (w *ServerInterfaceWrapper) MultipleRequestAndResponseTypes(ctx iris.Context) {
+
+	// Invoke the callback with all the unmarshaled arguments
+	w.Handler.MultipleRequestAndResponseTypes(ctx)
+}
+
+// ReservedGoKeywordParameters converts iris context to params.
+func (w *ServerInterfaceWrapper) ReservedGoKeywordParameters(ctx iris.Context) {
+
+	var err error
+
+	// ------------- Path parameter "type" -------------
+	var pType string
+
+	err = runtime.BindStyledParameterWithLocation("simple", false, "type", runtime.ParamLocationPath, ctx.Params().Get("type"), &pType)
+	if err != nil {
+		ctx.StatusCode(http.StatusBadRequest)
+		ctx.Writef("Invalid format for parameter type: %s", err)
+		return
+	}
+
+	// Invoke the callback with all the unmarshaled arguments
+	w.Handler.ReservedGoKeywordParameters(ctx, pType)
+}
+
+// ReusableResponses converts iris context to params.
+func (w *ServerInterfaceWrapper) ReusableResponses(ctx iris.Context) {
+
+	// Invoke the callback with all the unmarshaled arguments
+	w.Handler.ReusableResponses(ctx)
+}
+
+// TextExample converts iris context to params.
+func (w *ServerInterfaceWrapper) TextExample(ctx iris.Context) {
+
+	// Invoke the callback with all the unmarshaled arguments
+	w.Handler.TextExample(ctx)
+}
+
+// UnknownExample converts iris context to params.
+func (w *ServerInterfaceWrapper) UnknownExample(ctx iris.Context) {
+
+	// Invoke the callback with all the unmarshaled arguments
+	w.Handler.UnknownExample(ctx)
+}
+
+// UnspecifiedContentType converts iris context to params.
+func (w *ServerInterfaceWrapper) UnspecifiedContentType(ctx iris.Context) {
+
+	// Invoke the callback with all the unmarshaled arguments
+	w.Handler.UnspecifiedContentType(ctx)
+}
+
+// URLEncodedExample converts iris context to params.
+func (w *ServerInterfaceWrapper) URLEncodedExample(ctx iris.Context) {
+
+	// Invoke the callback with all the unmarshaled arguments
+	w.Handler.URLEncodedExample(ctx)
+}
+
+// HeadersExample converts iris context to params.
+func (w *ServerInterfaceWrapper) HeadersExample(ctx iris.Context) {
+
+	var err error
+
+	// Parameter object where we will unmarshal all parameters from the context
+	var params HeadersExampleParams
+
+	headers := ctx.Request().Header
+	// ------------- Required header parameter "header1" -------------
+	if valueList, found := headers[http.CanonicalHeaderKey("header1")]; found {
+		var Header1 string
+		n := len(valueList)
+		if n != 1 {
+			ctx.StatusCode(http.StatusBadRequest)
+			ctx.Writef("Expected one value for header1, got %d", n)
+			return
+		}
+
+		err = runtime.BindStyledParameterWithLocation("simple", false, "header1", runtime.ParamLocationHeader, valueList[0], &Header1)
+		if err != nil {
+			ctx.StatusCode(http.StatusBadRequest)
+			ctx.Writef("Invalid format for parameter header1: %s", err)
+			return
+		}
+
+		params.Header1 = Header1
+	} else {
+		ctx.StatusCode(http.StatusBadRequest)
+		ctx.WriteString("Header header1 is required, but not found")
+		return
+	}
+	// ------------- Optional header parameter "header2" -------------
+	if valueList, found := headers[http.CanonicalHeaderKey("header2")]; found {
+		var Header2 int
+		n := len(valueList)
+		if n != 1 {
+			ctx.StatusCode(http.StatusBadRequest)
+			ctx.Writef("Expected one value for header2, got %d", n)
+			return
+		}
+
+		err = runtime.BindStyledParameterWithLocation("simple", false, "header2", runtime.ParamLocationHeader, valueList[0], &Header2)
+		if err != nil {
+			ctx.StatusCode(http.StatusBadRequest)
+			ctx.Writef("Invalid format for parameter header2: %s", err)
+			return
+		}
+
+		params.Header2 = &Header2
+	}
+
+	// Invoke the callback with all the unmarshaled arguments
+	w.Handler.HeadersExample(ctx, params)
+}
+
+// UnionExample converts iris context to params.
+func (w *ServerInterfaceWrapper) UnionExample(ctx iris.Context) {
+
+	// Invoke the callback with all the unmarshaled arguments
+	w.Handler.UnionExample(ctx)
+}
+
+// IrisServerOption is the option for iris server
+type IrisServerOptions struct {
+	BaseURL     string
+	Middlewares []MiddlewareFunc
+}
+
+// RegisterHandlers creates http.Handler with routing matching OpenAPI spec.
+func RegisterHandlers(router *iris.Application, si ServerInterface) {
+	RegisterHandlersWithOptions(router, si, IrisServerOptions{})
+}
+
+// RegisterHandlersWithOptions creates http.Handler with additional options
+func RegisterHandlersWithOptions(router *iris.Application, si ServerInterface, options IrisServerOptions) {
+
+	wrapper := ServerInterfaceWrapper{
+		Handler: si,
+	}
+
+	router.Post(options.BaseURL+"/json", wrapper.JSONExample)
+	router.Post(options.BaseURL+"/multipart", wrapper.MultipartExample)
+	router.Post(options.BaseURL+"/multiple", wrapper.MultipleRequestAndResponseTypes)
+	router.Get(options.BaseURL+"/reserved-go-keyword-parameters/:type", wrapper.ReservedGoKeywordParameters)
+	router.Post(options.BaseURL+"/reusable-responses", wrapper.ReusableResponses)
+	router.Post(options.BaseURL+"/text", wrapper.TextExample)
+	router.Post(options.BaseURL+"/unknown", wrapper.UnknownExample)
+	router.Post(options.BaseURL+"/unspecified-content-type", wrapper.UnspecifiedContentType)
+	router.Post(options.BaseURL+"/urlencoded", wrapper.URLEncodedExample)
+	router.Post(options.BaseURL+"/with-headers", wrapper.HeadersExample)
+	router.Post(options.BaseURL+"/with-union", wrapper.UnionExample)
+
+	router.Build()
+}
+
+type BadrequestResponse struct {
+}
+
+type ReusableresponseResponseHeaders struct {
+	Header1 string
+	Header2 int
+}
+type ReusableresponseJSONResponse struct {
+	Body Example
+
+	Headers ReusableresponseResponseHeaders
+}
+
+type JSONExampleRequestObject struct {
+	Body *JSONExampleJSONRequestBody
+}
+
+type JSONExampleResponseObject interface {
+	VisitJSONExampleResponse(ctx iris.Context) error
+}
+
+type JSONExample200JSONResponse Example
+
+func (response JSONExample200JSONResponse) VisitJSONExampleResponse(ctx iris.Context) error {
+	ctx.ResponseWriter().Header().Set("Content-Type", "application/json")
+	ctx.StatusCode(200)
+
+	return ctx.JSON(&response)
+}
+
+type JSONExample400Response = BadrequestResponse
+
+func (response JSONExample400Response) VisitJSONExampleResponse(ctx iris.Context) error {
+	ctx.StatusCode(400)
+	return nil
+}
+
+type JSONExampledefaultResponse struct {
+	StatusCode int
+}
+
+func (response JSONExampledefaultResponse) VisitJSONExampleResponse(ctx iris.Context) error {
+	ctx.StatusCode(response.StatusCode)
+	return nil
+}
+
+type MultipartExampleRequestObject struct {
+	Body *multipart.Reader
+}
+
+type MultipartExampleResponseObject interface {
+	VisitMultipartExampleResponse(ctx iris.Context) error
+}
+
+type MultipartExample200MultipartResponse func(writer *multipart.Writer) error
+
+func (response MultipartExample200MultipartResponse) VisitMultipartExampleResponse(ctx iris.Context) error {
+	writer := multipart.NewWriter(ctx.ResponseWriter())
+	ctx.ResponseWriter().Header().Set("Content-Type", writer.FormDataContentType())
+	ctx.StatusCode(200)
+
+	defer writer.Close()
+	return response(writer)
+}
+
+type MultipartExample400Response = BadrequestResponse
+
+func (response MultipartExample400Response) VisitMultipartExampleResponse(ctx iris.Context) error {
+	ctx.StatusCode(400)
+	return nil
+}
+
+type MultipartExampledefaultResponse struct {
+	StatusCode int
+}
+
+func (response MultipartExampledefaultResponse) VisitMultipartExampleResponse(ctx iris.Context) error {
+	ctx.StatusCode(response.StatusCode)
+	return nil
+}
+
+type MultipleRequestAndResponseTypesRequestObject struct {
+	JSONBody      *MultipleRequestAndResponseTypesJSONRequestBody
+	FormdataBody  *MultipleRequestAndResponseTypesFormdataRequestBody
+	Body          io.Reader
+	MultipartBody *multipart.Reader
+	TextBody      *MultipleRequestAndResponseTypesTextRequestBody
+}
+
+type MultipleRequestAndResponseTypesResponseObject interface {
+	VisitMultipleRequestAndResponseTypesResponse(ctx iris.Context) error
+}
+
+type MultipleRequestAndResponseTypes200JSONResponse Example
+
+func (response MultipleRequestAndResponseTypes200JSONResponse) VisitMultipleRequestAndResponseTypesResponse(ctx iris.Context) error {
+	ctx.ResponseWriter().Header().Set("Content-Type", "application/json")
+	ctx.StatusCode(200)
+
+	return ctx.JSON(&response)
+}
+
+type MultipleRequestAndResponseTypes200FormdataResponse Example
+
+func (response MultipleRequestAndResponseTypes200FormdataResponse) VisitMultipleRequestAndResponseTypesResponse(ctx iris.Context) error {
+	ctx.ResponseWriter().Header().Set("Content-Type", "application/x-www-form-urlencoded")
+	ctx.StatusCode(200)
+
+	if form, err := runtime.MarshalForm(response, nil); err != nil {
+		return err
+	} else {
+		_, err := ctx.WriteString(form.Encode())
+		return err
+	}
+}
+
+type MultipleRequestAndResponseTypes200ImagepngResponse struct {
+	Body          io.Reader
+	ContentLength int64
+}
+
+func (response MultipleRequestAndResponseTypes200ImagepngResponse) VisitMultipleRequestAndResponseTypesResponse(ctx iris.Context) error {
+	ctx.ResponseWriter().Header().Set("Content-Type", "image/png")
+	if response.ContentLength != 0 {
+		ctx.ResponseWriter().Header().Set("Content-Length", fmt.Sprint(response.ContentLength))
+	}
+	ctx.StatusCode(200)
+
+	if closer, ok := response.Body.(io.ReadCloser); ok {
+		defer closer.Close()
+	}
+	_, err := io.Copy(ctx.ResponseWriter(), response.Body)
+	return err
+}
+
+type MultipleRequestAndResponseTypes200MultipartResponse func(writer *multipart.Writer) error
+
+func (response MultipleRequestAndResponseTypes200MultipartResponse) VisitMultipleRequestAndResponseTypesResponse(ctx iris.Context) error {
+	writer := multipart.NewWriter(ctx.ResponseWriter())
+	ctx.ResponseWriter().Header().Set("Content-Type", writer.FormDataContentType())
+	ctx.StatusCode(200)
+
+	defer writer.Close()
+	return response(writer)
+}
+
+type MultipleRequestAndResponseTypes200TextResponse string
+
+func (response MultipleRequestAndResponseTypes200TextResponse) VisitMultipleRequestAndResponseTypesResponse(ctx iris.Context) error {
+	ctx.ResponseWriter().Header().Set("Content-Type", "text/plain")
+	ctx.StatusCode(200)
+
+	_, err := ctx.WriteString(string(response))
+	return err
+}
+
+type MultipleRequestAndResponseTypes400Response = BadrequestResponse
+
+func (response MultipleRequestAndResponseTypes400Response) VisitMultipleRequestAndResponseTypesResponse(ctx iris.Context) error {
+	ctx.StatusCode(400)
+	return nil
+}
+
+type ReservedGoKeywordParametersRequestObject struct {
+	Type string `json:"type"`
+}
+
+type ReservedGoKeywordParametersResponseObject interface {
+	VisitReservedGoKeywordParametersResponse(ctx iris.Context) error
+}
+
+type ReservedGoKeywordParameters200TextResponse string
+
+func (response ReservedGoKeywordParameters200TextResponse) VisitReservedGoKeywordParametersResponse(ctx iris.Context) error {
+	ctx.ResponseWriter().Header().Set("Content-Type", "text/plain")
+	ctx.StatusCode(200)
+
+	_, err := ctx.WriteString(string(response))
+	return err
+}
+
+type ReusableResponsesRequestObject struct {
+	Body *ReusableResponsesJSONRequestBody
+}
+
+type ReusableResponsesResponseObject interface {
+	VisitReusableResponsesResponse(ctx iris.Context) error
+}
+
+type ReusableResponses200JSONResponse struct{ ReusableresponseJSONResponse }
+
+func (response ReusableResponses200JSONResponse) VisitReusableResponsesResponse(ctx iris.Context) error {
+	ctx.ResponseWriter().Header().Set("header1", fmt.Sprint(response.Headers.Header1))
+	ctx.ResponseWriter().Header().Set("header2", fmt.Sprint(response.Headers.Header2))
+	ctx.ResponseWriter().Header().Set("Content-Type", "application/json")
+	ctx.StatusCode(200)
+
+	return ctx.JSON(&response.Body)
+}
+
+type ReusableResponses400Response = BadrequestResponse
+
+func (response ReusableResponses400Response) VisitReusableResponsesResponse(ctx iris.Context) error {
+	ctx.StatusCode(400)
+	return nil
+}
+
+type ReusableResponsesdefaultResponse struct {
+	StatusCode int
+}
+
+func (response ReusableResponsesdefaultResponse) VisitReusableResponsesResponse(ctx iris.Context) error {
+	ctx.StatusCode(response.StatusCode)
+	return nil
+}
+
+type TextExampleRequestObject struct {
+	Body *TextExampleTextRequestBody
+}
+
+type TextExampleResponseObject interface {
+	VisitTextExampleResponse(ctx iris.Context) error
+}
+
+type TextExample200TextResponse string
+
+func (response TextExample200TextResponse) VisitTextExampleResponse(ctx iris.Context) error {
+	ctx.ResponseWriter().Header().Set("Content-Type", "text/plain")
+	ctx.StatusCode(200)
+
+	_, err := ctx.WriteString(string(response))
+	return err
+}
+
+type TextExample400Response = BadrequestResponse
+
+func (response TextExample400Response) VisitTextExampleResponse(ctx iris.Context) error {
+	ctx.StatusCode(400)
+	return nil
+}
+
+type TextExampledefaultResponse struct {
+	StatusCode int
+}
+
+func (response TextExampledefaultResponse) VisitTextExampleResponse(ctx iris.Context) error {
+	ctx.StatusCode(response.StatusCode)
+	return nil
+}
+
+type UnknownExampleRequestObject struct {
+	Body io.Reader
+}
+
+type UnknownExampleResponseObject interface {
+	VisitUnknownExampleResponse(ctx iris.Context) error
+}
+
+type UnknownExample200Videomp4Response struct {
+	Body          io.Reader
+	ContentLength int64
+}
+
+func (response UnknownExample200Videomp4Response) VisitUnknownExampleResponse(ctx iris.Context) error {
+	ctx.ResponseWriter().Header().Set("Content-Type", "video/mp4")
+	if response.ContentLength != 0 {
+		ctx.ResponseWriter().Header().Set("Content-Length", fmt.Sprint(response.ContentLength))
+	}
+	ctx.StatusCode(200)
+
+	if closer, ok := response.Body.(io.ReadCloser); ok {
+		defer closer.Close()
+	}
+	_, err := io.Copy(ctx.ResponseWriter(), response.Body)
+	return err
+}
+
+type UnknownExample400Response = BadrequestResponse
+
+func (response UnknownExample400Response) VisitUnknownExampleResponse(ctx iris.Context) error {
+	ctx.StatusCode(400)
+	return nil
+}
+
+type UnknownExampledefaultResponse struct {
+	StatusCode int
+}
+
+func (response UnknownExampledefaultResponse) VisitUnknownExampleResponse(ctx iris.Context) error {
+	ctx.StatusCode(response.StatusCode)
+	return nil
+}
+
+type UnspecifiedContentTypeRequestObject struct {
+	ContentType string
+	Body        io.Reader
+}
+
+type UnspecifiedContentTypeResponseObject interface {
+	VisitUnspecifiedContentTypeResponse(ctx iris.Context) error
+}
+
+type UnspecifiedContentType200VideoResponse struct {
+	Body          io.Reader
+	ContentType   string
+	ContentLength int64
+}
+
+func (response UnspecifiedContentType200VideoResponse) VisitUnspecifiedContentTypeResponse(ctx iris.Context) error {
+	ctx.ResponseWriter().Header().Set("Content-Type", response.ContentType)
+	if response.ContentLength != 0 {
+		ctx.ResponseWriter().Header().Set("Content-Length", fmt.Sprint(response.ContentLength))
+	}
+	ctx.StatusCode(200)
+
+	if closer, ok := response.Body.(io.ReadCloser); ok {
+		defer closer.Close()
+	}
+	_, err := io.Copy(ctx.ResponseWriter(), response.Body)
+	return err
+}
+
+type UnspecifiedContentType400Response = BadrequestResponse
+
+func (response UnspecifiedContentType400Response) VisitUnspecifiedContentTypeResponse(ctx iris.Context) error {
+	ctx.StatusCode(400)
+	return nil
+}
+
+type UnspecifiedContentType401Response struct {
+}
+
+func (response UnspecifiedContentType401Response) VisitUnspecifiedContentTypeResponse(ctx iris.Context) error {
+	ctx.StatusCode(401)
+	return nil
+}
+
+type UnspecifiedContentType403Response struct {
+}
+
+func (response UnspecifiedContentType403Response) VisitUnspecifiedContentTypeResponse(ctx iris.Context) error {
+	ctx.StatusCode(403)
+	return nil
+}
+
+type UnspecifiedContentTypedefaultResponse struct {
+	StatusCode int
+}
+
+func (response UnspecifiedContentTypedefaultResponse) VisitUnspecifiedContentTypeResponse(ctx iris.Context) error {
+	ctx.StatusCode(response.StatusCode)
+	return nil
+}
+
+type URLEncodedExampleRequestObject struct {
+	Body *URLEncodedExampleFormdataRequestBody
+}
+
+type URLEncodedExampleResponseObject interface {
+	VisitURLEncodedExampleResponse(ctx iris.Context) error
+}
+
+type URLEncodedExample200FormdataResponse Example
+
+func (response URLEncodedExample200FormdataResponse) VisitURLEncodedExampleResponse(ctx iris.Context) error {
+	ctx.ResponseWriter().Header().Set("Content-Type", "application/x-www-form-urlencoded")
+	ctx.StatusCode(200)
+
+	if form, err := runtime.MarshalForm(response, nil); err != nil {
+		return err
+	} else {
+		_, err := ctx.WriteString(form.Encode())
+		return err
+	}
+}
+
+type URLEncodedExample400Response = BadrequestResponse
+
+func (response URLEncodedExample400Response) VisitURLEncodedExampleResponse(ctx iris.Context) error {
+	ctx.StatusCode(400)
+	return nil
+}
+
+type URLEncodedExampledefaultResponse struct {
+	StatusCode int
+}
+
+func (response URLEncodedExampledefaultResponse) VisitURLEncodedExampleResponse(ctx iris.Context) error {
+	ctx.StatusCode(response.StatusCode)
+	return nil
+}
+
+type HeadersExampleRequestObject struct {
+	Params HeadersExampleParams
+	Body   *HeadersExampleJSONRequestBody
+}
+
+type HeadersExampleResponseObject interface {
+	VisitHeadersExampleResponse(ctx iris.Context) error
+}
+
+type HeadersExample200ResponseHeaders struct {
+	Header1 string
+	Header2 int
+}
+
+type HeadersExample200JSONResponse struct {
+	Body    Example
+	Headers HeadersExample200ResponseHeaders
+}
+
+func (response HeadersExample200JSONResponse) VisitHeadersExampleResponse(ctx iris.Context) error {
+	ctx.ResponseWriter().Header().Set("header1", fmt.Sprint(response.Headers.Header1))
+	ctx.ResponseWriter().Header().Set("header2", fmt.Sprint(response.Headers.Header2))
+	ctx.ResponseWriter().Header().Set("Content-Type", "application/json")
+	ctx.StatusCode(200)
+
+	return ctx.JSON(&response.Body)
+}
+
+type HeadersExample400Response = BadrequestResponse
+
+func (response HeadersExample400Response) VisitHeadersExampleResponse(ctx iris.Context) error {
+	ctx.StatusCode(400)
+	return nil
+}
+
+type HeadersExampledefaultResponse struct {
+	StatusCode int
+}
+
+func (response HeadersExampledefaultResponse) VisitHeadersExampleResponse(ctx iris.Context) error {
+	ctx.StatusCode(response.StatusCode)
+	return nil
+}
+
+type UnionExampleRequestObject struct {
+	Body *UnionExampleJSONRequestBody
+}
+
+type UnionExampleResponseObject interface {
+	VisitUnionExampleResponse(ctx iris.Context) error
+}
+
+type UnionExample200ResponseHeaders struct {
+	Header1 string
+	Header2 int
+}
+
+type UnionExample200ApplicationAlternativePlusJSONResponse struct {
+	Body    Example
+	Headers UnionExample200ResponseHeaders
+}
+
+func (response UnionExample200ApplicationAlternativePlusJSONResponse) VisitUnionExampleResponse(ctx iris.Context) error {
+	ctx.ResponseWriter().Header().Set("header1", fmt.Sprint(response.Headers.Header1))
+	ctx.ResponseWriter().Header().Set("header2", fmt.Sprint(response.Headers.Header2))
+	ctx.ResponseWriter().Header().Set("Content-Type", "application/alternative+json")
+	ctx.StatusCode(200)
+
+	return ctx.JSON(&response.Body)
+}
+
+type UnionExample200JSONResponse struct {
+	Body struct {
+		union json.RawMessage
+	}
+	Headers UnionExample200ResponseHeaders
+}
+
+func (response UnionExample200JSONResponse) VisitUnionExampleResponse(ctx iris.Context) error {
+	ctx.ResponseWriter().Header().Set("header1", fmt.Sprint(response.Headers.Header1))
+	ctx.ResponseWriter().Header().Set("header2", fmt.Sprint(response.Headers.Header2))
+	ctx.ResponseWriter().Header().Set("Content-Type", "application/json")
+	ctx.StatusCode(200)
+
+	return ctx.JSON(&response.Body.union)
+}
+
+type UnionExample400Response = BadrequestResponse
+
+func (response UnionExample400Response) VisitUnionExampleResponse(ctx iris.Context) error {
+	ctx.StatusCode(400)
+	return nil
+}
+
+type UnionExampledefaultResponse struct {
+	StatusCode int
+}
+
+func (response UnionExampledefaultResponse) VisitUnionExampleResponse(ctx iris.Context) error {
+	ctx.StatusCode(response.StatusCode)
+	return nil
+}
+
+// StrictServerInterface represents all server handlers.
+type StrictServerInterface interface {
+
+	// (POST /json)
+	JSONExample(ctx context.Context, request JSONExampleRequestObject) (JSONExampleResponseObject, error)
+
+	// (POST /multipart)
+	MultipartExample(ctx context.Context, request MultipartExampleRequestObject) (MultipartExampleResponseObject, error)
+
+	// (POST /multiple)
+	MultipleRequestAndResponseTypes(ctx context.Context, request MultipleRequestAndResponseTypesRequestObject) (MultipleRequestAndResponseTypesResponseObject, error)
+
+	// (GET /reserved-go-keyword-parameters/{type})
+	ReservedGoKeywordParameters(ctx context.Context, request ReservedGoKeywordParametersRequestObject) (ReservedGoKeywordParametersResponseObject, error)
+
+	// (POST /reusable-responses)
+	ReusableResponses(ctx context.Context, request ReusableResponsesRequestObject) (ReusableResponsesResponseObject, error)
+
+	// (POST /text)
+	TextExample(ctx context.Context, request TextExampleRequestObject) (TextExampleResponseObject, error)
+
+	// (POST /unknown)
+	UnknownExample(ctx context.Context, request UnknownExampleRequestObject) (UnknownExampleResponseObject, error)
+
+	// (POST /unspecified-content-type)
+	UnspecifiedContentType(ctx context.Context, request UnspecifiedContentTypeRequestObject) (UnspecifiedContentTypeResponseObject, error)
+
+	// (POST /urlencoded)
+	URLEncodedExample(ctx context.Context, request URLEncodedExampleRequestObject) (URLEncodedExampleResponseObject, error)
+
+	// (POST /with-headers)
+	HeadersExample(ctx context.Context, request HeadersExampleRequestObject) (HeadersExampleResponseObject, error)
+
+	// (POST /with-union)
+	UnionExample(ctx context.Context, request UnionExampleRequestObject) (UnionExampleResponseObject, error)
+}
+
+type StrictHandlerFunc = runtime.StrictIrisHandlerFunc
+type StrictMiddlewareFunc = runtime.StrictIrisMiddlewareFunc
+
+func NewStrictHandler(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc) ServerInterface {
+	return &strictHandler{ssi: ssi, middlewares: middlewares}
+}
+
+type strictHandler struct {
+	ssi         StrictServerInterface
+	middlewares []StrictMiddlewareFunc
+}
+
+// JSONExample operation middleware
+func (sh *strictHandler) JSONExample(ctx iris.Context) {
+	var request JSONExampleRequestObject
+
+	var body JSONExampleJSONRequestBody
+	if err := ctx.ReadJSON(&body); err != nil {
+		ctx.StopWithError(http.StatusBadRequest, err)
+		return
+	}
+	request.Body = &body
+
+	handler := func(ctx iris.Context, request interface{}) (interface{}, error) {
+		return sh.ssi.JSONExample(ctx, request.(JSONExampleRequestObject))
+	}
+	for _, middleware := range sh.middlewares {
+		handler = middleware(handler, "JSONExample")
+	}
+
+	response, err := handler(ctx, request)
+
+	if err != nil {
+		ctx.StopWithError(http.StatusBadRequest, err)
+		return
+	} else if validResponse, ok := response.(JSONExampleResponseObject); ok {
+		if err := validResponse.VisitJSONExampleResponse(ctx); err != nil {
+			ctx.StopWithError(http.StatusBadRequest, err)
+			return
+		}
+	} else if response != nil {
+		ctx.Writef("Unexpected response type: %T", response)
+		return
+	}
+}
+
+// MultipartExample operation middleware
+func (sh *strictHandler) MultipartExample(ctx iris.Context) {
+	var request MultipartExampleRequestObject
+
+	if reader, err := ctx.Request().MultipartReader(); err == nil {
+		request.Body = reader
+	} else {
+		ctx.StopWithError(http.StatusBadRequest, err)
+		return
+	}
+
+	handler := func(ctx iris.Context, request interface{}) (interface{}, error) {
+		return sh.ssi.MultipartExample(ctx, request.(MultipartExampleRequestObject))
+	}
+	for _, middleware := range sh.middlewares {
+		handler = middleware(handler, "MultipartExample")
+	}
+
+	response, err := handler(ctx, request)
+
+	if err != nil {
+		ctx.StopWithError(http.StatusBadRequest, err)
+		return
+	} else if validResponse, ok := response.(MultipartExampleResponseObject); ok {
+		if err := validResponse.VisitMultipartExampleResponse(ctx); err != nil {
+			ctx.StopWithError(http.StatusBadRequest, err)
+			return
+		}
+	} else if response != nil {
+		ctx.Writef("Unexpected response type: %T", response)
+		return
+	}
+}
+
+// MultipleRequestAndResponseTypes operation middleware
+func (sh *strictHandler) MultipleRequestAndResponseTypes(ctx iris.Context) {
+	var request MultipleRequestAndResponseTypesRequestObject
+
+	if strings.HasPrefix(ctx.GetHeader("Content-Type"), "application/json") {
+
+		var body MultipleRequestAndResponseTypesJSONRequestBody
+		if err := ctx.ReadJSON(&body); err != nil {
+			ctx.StopWithError(http.StatusBadRequest, err)
+			return
+		}
+		request.JSONBody = &body
+	}
+	if strings.HasPrefix(ctx.GetHeader("Content-Type"), "application/x-www-form-urlencoded") {
+		if err := ctx.Request().ParseForm(); err != nil {
+			ctx.StopWithError(http.StatusBadRequest, err)
+			return
+		}
+		var body MultipleRequestAndResponseTypesFormdataRequestBody
+		if err := runtime.BindForm(&body, ctx.Request().Form, nil, nil); err != nil {
+			ctx.StopWithError(http.StatusBadRequest, err)
+			return
+		}
+		request.FormdataBody = &body
+	}
+	if strings.HasPrefix(ctx.GetHeader("Content-Type"), "image/png") {
+		request.Body = ctx.Request().Body
+	}
+	if strings.HasPrefix(ctx.GetHeader("Content-Type"), "multipart/form-data") {
+		if reader, err := ctx.Request().MultipartReader(); err == nil {
+			request.MultipartBody = reader
+		} else {
+			ctx.StopWithError(http.StatusBadRequest, err)
+			return
+		}
+	}
+	if strings.HasPrefix(ctx.GetHeader("Content-Type"), "text/plain") {
+		data, err := io.ReadAll(ctx.Request().Body)
+		if err != nil {
+			ctx.StopWithError(http.StatusBadRequest, err)
+			return
+		}
+		body := MultipleRequestAndResponseTypesTextRequestBody(data)
+		request.TextBody = &body
+	}
+
+	handler := func(ctx iris.Context, request interface{}) (interface{}, error) {
+		return sh.ssi.MultipleRequestAndResponseTypes(ctx, request.(MultipleRequestAndResponseTypesRequestObject))
+	}
+	for _, middleware := range sh.middlewares {
+		handler = middleware(handler, "MultipleRequestAndResponseTypes")
+	}
+
+	response, err := handler(ctx, request)
+
+	if err != nil {
+		ctx.StopWithError(http.StatusBadRequest, err)
+		return
+	} else if validResponse, ok := response.(MultipleRequestAndResponseTypesResponseObject); ok {
+		if err := validResponse.VisitMultipleRequestAndResponseTypesResponse(ctx); err != nil {
+			ctx.StopWithError(http.StatusBadRequest, err)
+			return
+		}
+	} else if response != nil {
+		ctx.Writef("Unexpected response type: %T", response)
+		return
+	}
+}
+
+// ReservedGoKeywordParameters operation middleware
+func (sh *strictHandler) ReservedGoKeywordParameters(ctx iris.Context, pType string) {
+	var request ReservedGoKeywordParametersRequestObject
+
+	request.Type = pType
+
+	handler := func(ctx iris.Context, request interface{}) (interface{}, error) {
+		return sh.ssi.ReservedGoKeywordParameters(ctx, request.(ReservedGoKeywordParametersRequestObject))
+	}
+	for _, middleware := range sh.middlewares {
+		handler = middleware(handler, "ReservedGoKeywordParameters")
+	}
+
+	response, err := handler(ctx, request)
+
+	if err != nil {
+		ctx.StopWithError(http.StatusBadRequest, err)
+		return
+	} else if validResponse, ok := response.(ReservedGoKeywordParametersResponseObject); ok {
+		if err := validResponse.VisitReservedGoKeywordParametersResponse(ctx); err != nil {
+			ctx.StopWithError(http.StatusBadRequest, err)
+			return
+		}
+	} else if response != nil {
+		ctx.Writef("Unexpected response type: %T", response)
+		return
+	}
+}
+
+// ReusableResponses operation middleware
+func (sh *strictHandler) ReusableResponses(ctx iris.Context) {
+	var request ReusableResponsesRequestObject
+
+	var body ReusableResponsesJSONRequestBody
+	if err := ctx.ReadJSON(&body); err != nil {
+		ctx.StopWithError(http.StatusBadRequest, err)
+		return
+	}
+	request.Body = &body
+
+	handler := func(ctx iris.Context, request interface{}) (interface{}, error) {
+		return sh.ssi.ReusableResponses(ctx, request.(ReusableResponsesRequestObject))
+	}
+	for _, middleware := range sh.middlewares {
+		handler = middleware(handler, "ReusableResponses")
+	}
+
+	response, err := handler(ctx, request)
+
+	if err != nil {
+		ctx.StopWithError(http.StatusBadRequest, err)
+		return
+	} else if validResponse, ok := response.(ReusableResponsesResponseObject); ok {
+		if err := validResponse.VisitReusableResponsesResponse(ctx); err != nil {
+			ctx.StopWithError(http.StatusBadRequest, err)
+			return
+		}
+	} else if response != nil {
+		ctx.Writef("Unexpected response type: %T", response)
+		return
+	}
+}
+
+// TextExample operation middleware
+func (sh *strictHandler) TextExample(ctx iris.Context) {
+	var request TextExampleRequestObject
+
+	data, err := io.ReadAll(ctx.Request().Body)
+	if err != nil {
+		ctx.StopWithError(http.StatusBadRequest, err)
+		return
+	}
+	body := TextExampleTextRequestBody(data)
+	request.Body = &body
+
+	handler := func(ctx iris.Context, request interface{}) (interface{}, error) {
+		return sh.ssi.TextExample(ctx, request.(TextExampleRequestObject))
+	}
+	for _, middleware := range sh.middlewares {
+		handler = middleware(handler, "TextExample")
+	}
+
+	response, err := handler(ctx, request)
+
+	if err != nil {
+		ctx.StopWithError(http.StatusBadRequest, err)
+		return
+	} else if validResponse, ok := response.(TextExampleResponseObject); ok {
+		if err := validResponse.VisitTextExampleResponse(ctx); err != nil {
+			ctx.StopWithError(http.StatusBadRequest, err)
+			return
+		}
+	} else if response != nil {
+		ctx.Writef("Unexpected response type: %T", response)
+		return
+	}
+}
+
+// UnknownExample operation middleware
+func (sh *strictHandler) UnknownExample(ctx iris.Context) {
+	var request UnknownExampleRequestObject
+
+	request.Body = ctx.Request().Body
+
+	handler := func(ctx iris.Context, request interface{}) (interface{}, error) {
+		return sh.ssi.UnknownExample(ctx, request.(UnknownExampleRequestObject))
+	}
+	for _, middleware := range sh.middlewares {
+		handler = middleware(handler, "UnknownExample")
+	}
+
+	response, err := handler(ctx, request)
+
+	if err != nil {
+		ctx.StopWithError(http.StatusBadRequest, err)
+		return
+	} else if validResponse, ok := response.(UnknownExampleResponseObject); ok {
+		if err := validResponse.VisitUnknownExampleResponse(ctx); err != nil {
+			ctx.StopWithError(http.StatusBadRequest, err)
+			return
+		}
+	} else if response != nil {
+		ctx.Writef("Unexpected response type: %T", response)
+		return
+	}
+}
+
+// UnspecifiedContentType operation middleware
+func (sh *strictHandler) UnspecifiedContentType(ctx iris.Context) {
+	var request UnspecifiedContentTypeRequestObject
+
+	request.ContentType = ctx.GetContentTypeRequested()
+
+	request.Body = ctx.Request().Body
+
+	handler := func(ctx iris.Context, request interface{}) (interface{}, error) {
+		return sh.ssi.UnspecifiedContentType(ctx, request.(UnspecifiedContentTypeRequestObject))
+	}
+	for _, middleware := range sh.middlewares {
+		handler = middleware(handler, "UnspecifiedContentType")
+	}
+
+	response, err := handler(ctx, request)
+
+	if err != nil {
+		ctx.StopWithError(http.StatusBadRequest, err)
+		return
+	} else if validResponse, ok := response.(UnspecifiedContentTypeResponseObject); ok {
+		if err := validResponse.VisitUnspecifiedContentTypeResponse(ctx); err != nil {
+			ctx.StopWithError(http.StatusBadRequest, err)
+			return
+		}
+	} else if response != nil {
+		ctx.Writef("Unexpected response type: %T", response)
+		return
+	}
+}
+
+// URLEncodedExample operation middleware
+func (sh *strictHandler) URLEncodedExample(ctx iris.Context) {
+	var request URLEncodedExampleRequestObject
+
+	if err := ctx.Request().ParseForm(); err != nil {
+		ctx.StopWithError(http.StatusBadRequest, err)
+		return
+	}
+	var body URLEncodedExampleFormdataRequestBody
+	if err := runtime.BindForm(&body, ctx.Request().Form, nil, nil); err != nil {
+		ctx.StopWithError(http.StatusBadRequest, err)
+		return
+	}
+	request.Body = &body
+
+	handler := func(ctx iris.Context, request interface{}) (interface{}, error) {
+		return sh.ssi.URLEncodedExample(ctx, request.(URLEncodedExampleRequestObject))
+	}
+	for _, middleware := range sh.middlewares {
+		handler = middleware(handler, "URLEncodedExample")
+	}
+
+	response, err := handler(ctx, request)
+
+	if err != nil {
+		ctx.StopWithError(http.StatusBadRequest, err)
+		return
+	} else if validResponse, ok := response.(URLEncodedExampleResponseObject); ok {
+		if err := validResponse.VisitURLEncodedExampleResponse(ctx); err != nil {
+			ctx.StopWithError(http.StatusBadRequest, err)
+			return
+		}
+	} else if response != nil {
+		ctx.Writef("Unexpected response type: %T", response)
+		return
+	}
+}
+
+// HeadersExample operation middleware
+func (sh *strictHandler) HeadersExample(ctx iris.Context, params HeadersExampleParams) {
+	var request HeadersExampleRequestObject
+
+	request.Params = params
+
+	var body HeadersExampleJSONRequestBody
+	if err := ctx.ReadJSON(&body); err != nil {
+		ctx.StopWithError(http.StatusBadRequest, err)
+		return
+	}
+	request.Body = &body
+
+	handler := func(ctx iris.Context, request interface{}) (interface{}, error) {
+		return sh.ssi.HeadersExample(ctx, request.(HeadersExampleRequestObject))
+	}
+	for _, middleware := range sh.middlewares {
+		handler = middleware(handler, "HeadersExample")
+	}
+
+	response, err := handler(ctx, request)
+
+	if err != nil {
+		ctx.StopWithError(http.StatusBadRequest, err)
+		return
+	} else if validResponse, ok := response.(HeadersExampleResponseObject); ok {
+		if err := validResponse.VisitHeadersExampleResponse(ctx); err != nil {
+			ctx.StopWithError(http.StatusBadRequest, err)
+			return
+		}
+	} else if response != nil {
+		ctx.Writef("Unexpected response type: %T", response)
+		return
+	}
+}
+
+// UnionExample operation middleware
+func (sh *strictHandler) UnionExample(ctx iris.Context) {
+	var request UnionExampleRequestObject
+
+	var body UnionExampleJSONRequestBody
+	if err := ctx.ReadJSON(&body); err != nil {
+		ctx.StopWithError(http.StatusBadRequest, err)
+		return
+	}
+	request.Body = &body
+
+	handler := func(ctx iris.Context, request interface{}) (interface{}, error) {
+		return sh.ssi.UnionExample(ctx, request.(UnionExampleRequestObject))
+	}
+	for _, middleware := range sh.middlewares {
+		handler = middleware(handler, "UnionExample")
+	}
+
+	response, err := handler(ctx, request)
+
+	if err != nil {
+		ctx.StopWithError(http.StatusBadRequest, err)
+		return
+	} else if validResponse, ok := response.(UnionExampleResponseObject); ok {
+		if err := validResponse.VisitUnionExampleResponse(ctx); err != nil {
+			ctx.StopWithError(http.StatusBadRequest, err)
+			return
+		}
+	} else if response != nil {
+		ctx.Writef("Unexpected response type: %T", response)
+		return
+	}
+}
+
+// Base64 encoded, gzipped, json marshaled Swagger object
+var swaggerSpec = []string{
+
+	"H4sIAAAAAAAC/+xYS2/jNhD+KwTb01aynGxOunWDxbbdtimc5FTkQIsjm7sSyQ5HVgzD/72gKL9ixbW3",
+	"fhRBb3oMvxl+8+BwZjwzpTUaNDmezjiCs0Y7aF6GQiL8VYEj/ybBZagsKaN5yj8IOWj/zSOOUDkxLGCx",
+	"3MtnRhPoZqmwtlCZ8EuTL86vn3GXjaEU/ul7hJyn/LtkZUoS/roEnkVpC+Dz+Tx6YcHdZx7xMQgJ2Fgb",
+	"Hq82sWlqgafcESo94h4kiF13iilNMAL02rxoa4QXWNiRzrhFYwFJBY4moqigW1P7xQy/QEZhB0rnZpvL",
+	"W6NJKO2YVHkOCJpYSx7zGI65ylqDBJINp8xryIg5wAkgjzgp8obx+/XvrDXY8YhPAF1QdNXr9/reX8aC",
+	"FlbxlL9vPkXcCho3G1o6yJouv/9yf/c7U46JikwpSGWiKKasFOjGogDJlCbjTawycj3eaMLG8T/LdvXH",
+	"lkofNU0AfTByeoqAaeJyLZyv+/0zxeU84jdBWRfG0qhkLcEamFxURQfnj/qrNrVmgGiw3VlSVgUpK5DW",
+	"fbXJ9m8LkX0oX+IlucEyloLEiVg/lqaLEt/Wgs4cuR+b2rGxqRkZJkEUrFY0ZouFL5JbaSaYU3pUAFsY",
+	"FXV6soC25P6o5aDdy4PHOHkuRRsoz3Fd13HjvAoL0JmRIL8NVpViBInVo83lHlsQT/lwSj5st4vrkYIo",
+	"4gTPlNhCKL375DhTOfmf6aMldkhXhOZElPHIxF9hWhuUsRUoSiBAl8y89rkHHkFHKv+xlGSZ0GwITIsS",
+	"JBM5AbJPhrWQbitlB63eT+ZzEFlBNcft8iX9c8Y9Jc0RzCPuFfA0sBLyWqF3OmEF0Q7anv4xPv+VAxZs",
+	"hkYv3lDVXQYXJWpJHULufEns8lwHf0HTYE3iMg3D7ojban3PcQZ5T75+7j/A815H/hFL37lz+1DCqvDx",
+	"dc7aVfvQ9o2VdA8WJ0qCSUp7cyDyxUh1FjKVK5Bxu4s42PZaSbg1OkOgzRbIXye0IbYE87ccGgMLDETM",
+	"GVYDKytHzArnmKKmihQq3JQkbBWPx5Vlt0HTw6qc7vLquxP59N2lPHrTvzp8yfsTx81GK/NKPg5+/Rhk",
+	"Dr0vHq1nOrDjO57eC6Wzv6TEawOV7hT+KQiszvQM1MR3RFoyBKpQg2QTJRZDgK3cbAFWbu3qhYIZq25o",
+	"Mdw5pCGKdmJd82jXAOjpDY8nTjk2O1ecVlrtGlM9+t+s7aFfng3K6P/oEEoUBKgFqQn8cJwb5DaK0XCX",
+	"N5n2wsvRnhqe3l5UzSMe5qahBFVY+DpBZNMkCfPWnqvFaATYUyYRVnkW/g4AAP//Pk3lbjwXAAA=",
+}
+
+// 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: %w", err)
+	}
+	zr, err := gzip.NewReader(bytes.NewReader(zipped))
+	if err != nil {
+		return nil, fmt.Errorf("error decompressing spec: %w", err)
+	}
+	var buf bytes.Buffer
+	_, err = buf.ReadFrom(zr)
+	if err != nil {
+		return nil, fmt.Errorf("error decompressing spec: %w", 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) {
+	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) {
+	resolvePath := PathToRawSpec("")
+
+	loader := openapi3.NewLoader()
+	loader.IsExternalRefsAllowed = true
+	loader.ReadFromURIFunc = func(loader *openapi3.Loader, url *url.URL) ([]byte, error) {
+		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/strict-server/iris/server.go b/internal/test/strict-server/iris/server.go
new file mode 100644
index 0000000..c59b796
--- /dev/null
+++ b/internal/test/strict-server/iris/server.go
@@ -0,0 +1,119 @@
+//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen --config=server.cfg.yaml ../strict-schema.yaml
+//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen --config=types.cfg.yaml ../strict-schema.yaml
+
+package api
+
+import (
+	"context"
+	"encoding/json"
+	"io"
+	"mime/multipart"
+)
+
+type StrictServer struct{}
+
+func (s StrictServer) JSONExample(ctx context.Context, request JSONExampleRequestObject) (JSONExampleResponseObject, error) {
+	return JSONExample200JSONResponse(*request.Body), nil
+}
+
+func (s StrictServer) MultipartExample(ctx context.Context, request MultipartExampleRequestObject) (MultipartExampleResponseObject, error) {
+	return MultipartExample200MultipartResponse(func(writer *multipart.Writer) error {
+		for {
+			part, err := request.Body.NextPart()
+			if err == io.EOF {
+				return nil
+			} else if err != nil {
+				return err
+			}
+			w, err := writer.CreatePart(part.Header)
+			if err != nil {
+				return err
+			}
+			_, err = io.Copy(w, part)
+			if err != nil {
+				return err
+			}
+			if err = part.Close(); err != nil {
+				return err
+			}
+		}
+	}), nil
+}
+
+func (s StrictServer) MultipleRequestAndResponseTypes(ctx context.Context, request MultipleRequestAndResponseTypesRequestObject) (MultipleRequestAndResponseTypesResponseObject, error) {
+	switch {
+	case request.Body != nil:
+		return MultipleRequestAndResponseTypes200ImagepngResponse{Body: request.Body}, nil
+	case request.JSONBody != nil:
+		return MultipleRequestAndResponseTypes200JSONResponse(*request.JSONBody), nil
+	case request.FormdataBody != nil:
+		return MultipleRequestAndResponseTypes200FormdataResponse(*request.FormdataBody), nil
+	case request.TextBody != nil:
+		return MultipleRequestAndResponseTypes200TextResponse(*request.TextBody), nil
+	case request.MultipartBody != nil:
+		return MultipleRequestAndResponseTypes200MultipartResponse(func(writer *multipart.Writer) error {
+			for {
+				part, err := request.MultipartBody.NextPart()
+				if err == io.EOF {
+					return nil
+				} else if err != nil {
+					return err
+				}
+				w, err := writer.CreatePart(part.Header)
+				if err != nil {
+					return err
+				}
+				_, err = io.Copy(w, part)
+				if err != nil {
+					return err
+				}
+				if err = part.Close(); err != nil {
+					return err
+				}
+			}
+		}), nil
+	default:
+		return MultipleRequestAndResponseTypes400Response{}, nil
+	}
+}
+
+func (s StrictServer) TextExample(ctx context.Context, request TextExampleRequestObject) (TextExampleResponseObject, error) {
+	return TextExample200TextResponse(*request.Body), nil
+}
+
+func (s StrictServer) UnknownExample(ctx context.Context, request UnknownExampleRequestObject) (UnknownExampleResponseObject, error) {
+	return UnknownExample200Videomp4Response{Body: request.Body}, nil
+}
+
+func (s StrictServer) UnspecifiedContentType(ctx context.Context, request UnspecifiedContentTypeRequestObject) (UnspecifiedContentTypeResponseObject, error) {
+	return UnspecifiedContentType200VideoResponse{Body: request.Body, ContentType: request.ContentType}, nil
+}
+
+func (s StrictServer) URLEncodedExample(ctx context.Context, request URLEncodedExampleRequestObject) (URLEncodedExampleResponseObject, error) {
+	return URLEncodedExample200FormdataResponse(*request.Body), nil
+}
+
+func (s StrictServer) HeadersExample(ctx context.Context, request HeadersExampleRequestObject) (HeadersExampleResponseObject, error) {
+	return HeadersExample200JSONResponse{Body: *request.Body, Headers: HeadersExample200ResponseHeaders{Header1: request.Params.Header1, Header2: *request.Params.Header2}}, nil
+}
+
+func (s StrictServer) ReusableResponses(ctx context.Context, request ReusableResponsesRequestObject) (ReusableResponsesResponseObject, error) {
+	return ReusableResponses200JSONResponse{ReusableresponseJSONResponse: ReusableresponseJSONResponse{Body: *request.Body}}, nil
+}
+
+func (s StrictServer) ReservedGoKeywordParameters(ctx context.Context, request ReservedGoKeywordParametersRequestObject) (ReservedGoKeywordParametersResponseObject, error) {
+	return ReservedGoKeywordParameters200TextResponse(""), nil
+}
+
+func (s StrictServer) UnionExample(ctx context.Context, request UnionExampleRequestObject) (UnionExampleResponseObject, error) {
+	union, err := json.Marshal(*request.Body)
+	if err != nil {
+		return nil, err
+	}
+
+	return UnionExample200JSONResponse{
+		Body: struct{ union json.RawMessage }{
+			union: union,
+		},
+	}, nil
+}
diff --git a/internal/test/strict-server/iris/types.cfg.yaml b/internal/test/strict-server/iris/types.cfg.yaml
new file mode 100644
index 0000000..4ea1d8a
--- /dev/null
+++ b/internal/test/strict-server/iris/types.cfg.yaml
@@ -0,0 +1,4 @@
+package: api
+generate:
+  models: true
+output: types.gen.go
diff --git a/internal/test/strict-server/iris/types.gen.go b/internal/test/strict-server/iris/types.gen.go
new file mode 100644
index 0000000..023ebed
--- /dev/null
+++ b/internal/test/strict-server/iris/types.gen.go
@@ -0,0 +1,57 @@
+// Package api provides primitives to interact with the openapi HTTP API.
+//
+// Code generated by github.com/deepmap/oapi-codegen version (devel) DO NOT EDIT.
+package api
+
+// Example defines model for example.
+type Example struct {
+	Value *string `json:"value,omitempty"`
+}
+
+// Reusableresponse defines model for reusableresponse.
+type Reusableresponse = Example
+
+// MultipleRequestAndResponseTypesTextBody defines parameters for MultipleRequestAndResponseTypes.
+type MultipleRequestAndResponseTypesTextBody = string
+
+// TextExampleTextBody defines parameters for TextExample.
+type TextExampleTextBody = string
+
+// HeadersExampleParams defines parameters for HeadersExample.
+type HeadersExampleParams struct {
+	Header1 string `json:"header1"`
+	Header2 *int   `json:"header2,omitempty"`
+}
+
+// JSONExampleJSONRequestBody defines body for JSONExample for application/json ContentType.
+type JSONExampleJSONRequestBody = Example
+
+// MultipartExampleMultipartRequestBody defines body for MultipartExample for multipart/form-data ContentType.
+type MultipartExampleMultipartRequestBody = Example
+
+// MultipleRequestAndResponseTypesJSONRequestBody defines body for MultipleRequestAndResponseTypes for application/json ContentType.
+type MultipleRequestAndResponseTypesJSONRequestBody = Example
+
+// MultipleRequestAndResponseTypesFormdataRequestBody defines body for MultipleRequestAndResponseTypes for application/x-www-form-urlencoded ContentType.
+type MultipleRequestAndResponseTypesFormdataRequestBody = Example
+
+// MultipleRequestAndResponseTypesMultipartRequestBody defines body for MultipleRequestAndResponseTypes for multipart/form-data ContentType.
+type MultipleRequestAndResponseTypesMultipartRequestBody = Example
+
+// MultipleRequestAndResponseTypesTextRequestBody defines body for MultipleRequestAndResponseTypes for text/plain ContentType.
+type MultipleRequestAndResponseTypesTextRequestBody = MultipleRequestAndResponseTypesTextBody
+
+// ReusableResponsesJSONRequestBody defines body for ReusableResponses for application/json ContentType.
+type ReusableResponsesJSONRequestBody = Example
+
+// TextExampleTextRequestBody defines body for TextExample for text/plain ContentType.
+type TextExampleTextRequestBody = TextExampleTextBody
+
+// URLEncodedExampleFormdataRequestBody defines body for URLEncodedExample for application/x-www-form-urlencoded ContentType.
+type URLEncodedExampleFormdataRequestBody = Example
+
+// HeadersExampleJSONRequestBody defines body for HeadersExample for application/json ContentType.
+type HeadersExampleJSONRequestBody = Example
+
+// UnionExampleJSONRequestBody defines body for UnionExample for application/json ContentType.
+type UnionExampleJSONRequestBody = Example
diff --git a/internal/test/strict-server/strict_test.go b/internal/test/strict-server/strict_test.go
index 65385ad..04bd951 100644
--- a/internal/test/strict-server/strict_test.go
+++ b/internal/test/strict-server/strict_test.go
@@ -15,6 +15,7 @@ import (
 	"github.com/go-chi/chi/v5"
 	"github.com/gofiber/fiber/v2"
 	"github.com/gofiber/fiber/v2/middleware/adaptor"
+	"github.com/kataras/iris/v12"
 	"github.com/labstack/echo/v4"
 	"github.com/stretchr/testify/assert"
 
@@ -23,10 +24,21 @@ import (
 	echoAPI "github.com/deepmap/oapi-codegen/internal/test/strict-server/echo"
 	fiberAPI "github.com/deepmap/oapi-codegen/internal/test/strict-server/fiber"
 	ginAPI "github.com/deepmap/oapi-codegen/internal/test/strict-server/gin"
+	irisAPI "github.com/deepmap/oapi-codegen/internal/test/strict-server/iris"
+
+	// "github.com/deepmap/oapi-codegen/pkg/runtime"
 	"github.com/deepmap/oapi-codegen/pkg/runtime"
 	"github.com/deepmap/oapi-codegen/pkg/testutil"
 )
 
+func TestIrisServer(t *testing.T) {
+	server := irisAPI.StrictServer{}
+	strictHandler := irisAPI.NewStrictHandler(server, nil)
+	i := iris.New()
+	irisAPI.RegisterHandlers(i, strictHandler)
+	testImpl(t, i)
+}
+
 func TestChiServer(t *testing.T) {
 	server := chiAPI.StrictServer{}
 	strictHandler := chiAPI.NewStrictHandler(server, nil)
diff --git a/pkg/codegen/utils_test.go b/pkg/codegen/utils_test.go
index 6776526..8806c6a 100644
--- a/pkg/codegen/utils_test.go
+++ b/pkg/codegen/utils_test.go
@@ -229,6 +229,23 @@ func TestIsGoTypeReference(t *testing.T) {
 	assert.Equal(t, false, IsGoTypeReference("http://deepmap.com/doc.json"))
 }
 
+func TestSwaggerUriToIrisUri(t *testing.T) {
+	assert.Equal(t, "/path", SwaggerUriToIrisUri("/path"))
+	assert.Equal(t, "/path/:arg", SwaggerUriToIrisUri("/path/{arg}"))
+	assert.Equal(t, "/path/:arg1/:arg2", SwaggerUriToIrisUri("/path/{arg1}/{arg2}"))
+	assert.Equal(t, "/path/:arg1/:arg2/foo", SwaggerUriToIrisUri("/path/{arg1}/{arg2}/foo"))
+
+	// Make sure all the exploded and alternate formats match too
+	assert.Equal(t, "/path/:arg/foo", SwaggerUriToIrisUri("/path/{arg}/foo"))
+	assert.Equal(t, "/path/:arg/foo", SwaggerUriToIrisUri("/path/{arg*}/foo"))
+	assert.Equal(t, "/path/:arg/foo", SwaggerUriToIrisUri("/path/{.arg}/foo"))
+	assert.Equal(t, "/path/:arg/foo", SwaggerUriToIrisUri("/path/{.arg*}/foo"))
+	assert.Equal(t, "/path/:arg/foo", SwaggerUriToIrisUri("/path/{;arg}/foo"))
+	assert.Equal(t, "/path/:arg/foo", SwaggerUriToIrisUri("/path/{;arg*}/foo"))
+	assert.Equal(t, "/path/:arg/foo", SwaggerUriToIrisUri("/path/{?arg}/foo"))
+	assert.Equal(t, "/path/:arg/foo", SwaggerUriToIrisUri("/path/{?arg*}/foo"))
+}
+
 func TestSwaggerUriToEchoUri(t *testing.T) {
 	assert.Equal(t, "/path", SwaggerUriToEchoUri("/path"))
 	assert.Equal(t, "/path/:arg", SwaggerUriToEchoUri("/path/{arg}"))
@@ -450,7 +467,6 @@ func TestSchemaNameToTypeName(t *testing.T) {
 	}
 }
 
-
 func TestTypeDefinitionsEquivalent(t *testing.T) {
 	def1 := TypeDefinition{TypeName: "name", Schema: Schema{
 		OAPISchema: &openapi3.Schema{},
@@ -461,7 +477,6 @@ func TestTypeDefinitionsEquivalent(t *testing.T) {
 	assert.True(t, TypeDefinitionsEquivalent(def1, def2))
 }
 
-
 func TestRefPathToObjName(t *testing.T) {
 	t.Parallel()
 
diff --git a/pkg/iris-middleware/oapi_validate_test.go b/pkg/iris-middleware/oapi_validate_test.go
new file mode 100644
index 0000000..4035fb1
--- /dev/null
+++ b/pkg/iris-middleware/oapi_validate_test.go
@@ -0,0 +1,418 @@
+package middleware
+
+import (
+	"context"
+	_ "embed"
+	"errors"
+	"fmt"
+	"io"
+	"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/kataras/iris/v12"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+//go:embed test_spec.yaml
+var testSchema []byte
+
+func doGet(t *testing.T, i *iris.Application, rawURL string) *httptest.ResponseRecorder {
+	u, err := url.Parse(rawURL)
+	if err != nil {
+		t.Fatalf("Invalid url: %s", rawURL)
+	}
+
+	response := testutil.NewRequest().Get(u.RequestURI()).WithHost(u.Host).WithAcceptJson().GoWithHTTPHandler(t, i)
+	return response.Recorder
+}
+
+func doPost(t *testing.T, i *iris.Application, rawURL string, jsonBody interface{}) *httptest.ResponseRecorder {
+	u, err := url.Parse(rawURL)
+	if err != nil {
+		t.Fatalf("Invalid url: %s", rawURL)
+	}
+
+	response := testutil.NewRequest().Post(u.RequestURI()).WithHost(u.Host).WithJsonBody(jsonBody).GoWithHTTPHandler(t, i)
+	return response.Recorder
+}
+
+func TestOapiRequestValidator(t *testing.T) {
+	swagger, err := openapi3.NewLoader().LoadFromData(testSchema)
+	require.NoError(t, err, "Error initializing swagger")
+
+	// Create a new iris router
+	i := iris.New()
+
+	// Set up an authenticator to check authenticated function. It will allow
+	// access to "someScope", but disallow others.
+	options := Options{
+		ErrorHandler: func(ctx iris.Context, message string, statusCode int) {
+			ctx.StopWithText(statusCode, "test: "+message)
+		},
+		Options: openapi3filter.Options{
+			AuthenticationFunc: func(ctx context.Context, input *openapi3filter.AuthenticationInput) error {
+				// The iris context should be propagated into here.
+				iCtx := GetIrisContext(ctx)
+				assert.NotNil(t, iCtx)
+				// As should user data
+				assert.EqualValues(t, "hi!", GetUserData(ctx))
+
+				for _, s := range input.Scopes {
+					if s == "someScope" {
+						return nil
+					}
+					if s == "unauthorized" {
+						return errors.New("unauthorized")
+					}
+				}
+				return errors.New("forbidden")
+			},
+		},
+		UserData: "hi!",
+	}
+
+	// Install our OpenApi based request validator
+	i.Use(OapiRequestValidatorWithOptions(swagger, &options))
+
+	called := false
+
+	// Install a request handler for /resource. We want to make sure it doesn't
+	// get called.
+	i.Get("/resource", func(ctx iris.Context) {
+		called = true
+	})
+
+	// Add a handler for the POST message
+	i.Post("/resource", func(ctx iris.Context) {
+		called = true
+		ctx.StatusCode(http.StatusNoContent)
+	})
+
+	i.Get("/protected_resource", func(ctx iris.Context) {
+		called = true
+		ctx.StatusCode(http.StatusNoContent)
+	})
+
+	i.Get("/protected_resource2", func(ctx iris.Context) {
+		called = true
+		ctx.StatusCode(http.StatusNoContent)
+	})
+
+	i.Get("/protected_resource_401", func(ctx iris.Context) {
+		called = true
+		ctx.StatusCode(http.StatusNoContent)
+	})
+
+	if err := i.Build(); err != nil {
+		t.Fatalf("Error building iris: %s", err)
+	}
+
+	// Let's send the request to the wrong server, this should fail validation
+	{
+		res := doGet(t, i, "https://not.deepmap.ai/resource")
+		assert.Equal(t, http.StatusBadRequest, res.Code)
+		assert.False(t, called, "Handler should not have been called")
+	}
+
+	// Let's send a good request, it should pass
+	{
+		res := doGet(t, i, "https://deepmap.ai/resource")
+		assert.Equal(t, http.StatusOK, res.Code)
+		assert.True(t, called, "Handler should have been called")
+		called = false
+	}
+
+	// Send an out-of-spec parameter
+	{
+		res := doGet(t, i, "https://deepmap.ai/resource?id=500")
+		assert.Equal(t, http.StatusBadRequest, res.Code)
+		assert.False(t, called, "Handler should not have been called")
+		called = false
+	}
+
+	// Send a bad parameter type
+	{
+		res := doGet(t, i, "https://deepmap.ai/resource?id=foo")
+		assert.Equal(t, http.StatusBadRequest, res.Code)
+		assert.False(t, called, "Handler should not have been called")
+		called = false
+	}
+
+	called = false
+	// Send a good request body
+	{
+		body := struct {
+			Name string `json:"name"`
+		}{
+			Name: "Marcin",
+		}
+		res := doPost(t, i, "https://deepmap.ai/resource", body)
+		assert.Equal(t, http.StatusNoContent, res.Code)
+		assert.True(t, called, "Handler should have been called")
+		called = false
+	}
+
+	// Send a malformed body
+	{
+		body := struct {
+			Name int `json:"name"`
+		}{
+			Name: 7,
+		}
+		res := doPost(t, i, "https://deepmap.ai/resource", body)
+		assert.Equal(t, http.StatusBadRequest, res.Code)
+		assert.False(t, called, "Handler should not have been called")
+		called = false
+	}
+
+	// Call a protected function to which we have access
+	{
+		res := doGet(t, i, "https://deepmap.ai/protected_resource")
+		assert.Equal(t, http.StatusNoContent, res.Code)
+		assert.True(t, called, "Handler should have been called")
+		called = false
+	}
+
+	// Call a protected function to which we don't have access
+	{
+		res := doGet(t, i, "https://deepmap.ai/protected_resource2")
+		assert.Equal(t, http.StatusBadRequest, res.Code)
+		assert.False(t, called, "Handler should not have been called")
+		called = false
+	}
+
+	// Call a protected function without credentials
+	{
+		res := doGet(t, i, "https://deepmap.ai/protected_resource_401")
+		assert.Equal(t, http.StatusBadRequest, res.Code)
+		body, err := io.ReadAll(res.Body)
+		if assert.NoError(t, err) {
+			assert.Equal(t, "test: error in openapi3filter.SecurityRequirementsError: security requirements failed: unauthorized", string(body))
+		}
+		assert.False(t, called, "Handler should not have been called")
+		called = false
+	}
+}
+
+func TestOapiRequestValidatorWithOptionsMultiError(t *testing.T) {
+	swagger, err := openapi3.NewLoader().LoadFromData(testSchema)
+	require.NoError(t, err, "Error initializing swagger")
+
+	i := iris.New()
+
+	// Set up an authenticator to check authenticated function. It will allow
+	// access to "someScope", but disallow others.
+	options := Options{
+		Options: openapi3filter.Options{
+			ExcludeRequestBody:    false,
+			ExcludeResponseBody:   false,
+			IncludeResponseStatus: true,
+			MultiError:            true,
+		},
+	}
+
+	// register middleware
+	i.Use(OapiRequestValidatorWithOptions(swagger, &options))
+
+	called := false
+
+	// Install a request handler for /resource. We want to make sure it doesn't
+	// get called.
+	i.Get("/multiparamresource", func(ctx iris.Context) {
+		called = true
+	})
+
+	if err := i.Build(); err != nil {
+		t.Fatalf("Error building iris: %s", err)
+	}
+
+	// Let's send a good request, it should pass
+	{
+		res := doGet(t, i, "https://deepmap.ai/multiparamresource?id=50&id2=50")
+		assert.Equal(t, http.StatusOK, res.Code)
+		assert.True(t, called, "Handler should have been called")
+		called = false
+	}
+
+	// Let's send a request with a missing parameter, it should return
+	// a bad status
+	{
+		res := doGet(t, i, "https://deepmap.ai/multiparamresource?id=50")
+		assert.Equal(t, http.StatusBadRequest, res.Code)
+		body, err := io.ReadAll(res.Body)
+		if assert.NoError(t, err) {
+			assert.Contains(t, string(body), "multiple errors encountered")
+			assert.Contains(t, string(body), "parameter \"id2\"")
+			assert.Contains(t, string(body), "value is required but missing")
+		}
+		assert.False(t, called, "Handler should not have been called")
+		called = false
+	}
+
+	// Let's send a request with a 2 missing parameters, it should return
+	// a bad status
+	{
+		res := doGet(t, i, "https://deepmap.ai/multiparamresource")
+		assert.Equal(t, http.StatusBadRequest, res.Code)
+		body, err := io.ReadAll(res.Body)
+		if assert.NoError(t, err) {
+			assert.Contains(t, string(body), "multiple errors encountered")
+			assert.Contains(t, string(body), "parameter \"id\"")
+			assert.Contains(t, string(body), "value is required but missing")
+			assert.Contains(t, string(body), "parameter \"id2\"")
+			assert.Contains(t, string(body), "value is required but missing")
+		}
+		assert.False(t, called, "Handler should not have been called")
+		called = false
+	}
+
+	// Let's send a request with a 1 missing parameter, and another outside
+	// or the parameters. It should return a bad status
+	{
+		res := doGet(t, i, "https://deepmap.ai/multiparamresource?id=500")
+		assert.Equal(t, http.StatusBadRequest, res.Code)
+		body, err := io.ReadAll(res.Body)
+		if assert.NoError(t, err) {
+			assert.Contains(t, string(body), "multiple errors encountered")
+			assert.Contains(t, string(body), "parameter \"id\"")
+			assert.Contains(t, string(body), "number must be at most 100")
+			assert.Contains(t, string(body), "parameter \"id2\"")
+			assert.Contains(t, string(body), "value is required but missing")
+		}
+		assert.False(t, called, "Handler should not have been called")
+		called = false
+	}
+
+	// Let's send a request with a parameters that do not meet spec. It should
+	// return a bad status
+	{
+		res := doGet(t, i, "https://deepmap.ai/multiparamresource?id=abc&id2=1")
+		assert.Equal(t, http.StatusBadRequest, res.Code)
+		body, err := io.ReadAll(res.Body)
+		if assert.NoError(t, err) {
+			assert.Contains(t, string(body), "multiple errors encountered")
+			assert.Contains(t, string(body), "parameter \"id\"")
+			assert.Contains(t, string(body), "value abc: an invalid integer: invalid syntax")
+			assert.Contains(t, string(body), "parameter \"id2\"")
+			assert.Contains(t, string(body), "number must be at least 10")
+		}
+		assert.False(t, called, "Handler should not have been called")
+		called = false
+	}
+}
+
+func TestOapiRequestValidatorWithOptionsMultiErrorAndCustomHandler(t *testing.T) {
+	swagger, err := openapi3.NewLoader().LoadFromData(testSchema)
+	require.NoError(t, err, "Error initializing swagger")
+
+	i := iris.New()
+
+	// Set up an authenticator to check authenticated function. It will allow
+	// access to "someScope", but disallow others.
+	options := Options{
+		Options: openapi3filter.Options{
+			ExcludeRequestBody:    false,
+			ExcludeResponseBody:   false,
+			IncludeResponseStatus: true,
+			MultiError:            true,
+		},
+		MultiErrorHandler: func(me openapi3.MultiError) error {
+			return fmt.Errorf("Bad stuff -  %s", me.Error())
+		},
+	}
+
+	// register middleware
+	i.Use(OapiRequestValidatorWithOptions(swagger, &options))
+
+	called := false
+
+	// Install a request handler for /resource. We want to make sure it doesn't
+	// get called.
+	i.Get("/multiparamresource", func(ctx iris.Context) {
+		called = true
+	})
+
+	if err := i.Build(); err != nil {
+		t.Fatalf("Error building iris: %s", err)
+	}
+
+	// Let's send a good request, it should pass
+	{
+		res := doGet(t, i, "https://deepmap.ai/multiparamresource?id=50&id2=50")
+		assert.Equal(t, http.StatusOK, res.Code)
+		assert.True(t, called, "Handler should have been called")
+		called = false
+	}
+
+	// Let's send a request with a missing parameter, it should return
+	// a bad status
+	{
+		res := doGet(t, i, "https://deepmap.ai/multiparamresource?id=50")
+		assert.Equal(t, http.StatusBadRequest, res.Code)
+		body, err := io.ReadAll(res.Body)
+		if assert.NoError(t, err) {
+			assert.Contains(t, string(body), "Bad stuff")
+			assert.Contains(t, string(body), "parameter \"id2\"")
+			assert.Contains(t, string(body), "value is required but missing")
+		}
+		assert.False(t, called, "Handler should not have been called")
+		called = false
+	}
+
+	// Let's send a request with a 2 missing parameters, it should return
+	// a bad status
+	{
+		res := doGet(t, i, "https://deepmap.ai/multiparamresource")
+		assert.Equal(t, http.StatusBadRequest, res.Code)
+		body, err := io.ReadAll(res.Body)
+		if assert.NoError(t, err) {
+			assert.Contains(t, string(body), "Bad stuff")
+			assert.Contains(t, string(body), "parameter \"id\"")
+			assert.Contains(t, string(body), "value is required but missing")
+			assert.Contains(t, string(body), "parameter \"id2\"")
+			assert.Contains(t, string(body), "value is required but missing")
+		}
+		assert.False(t, called, "Handler should not have been called")
+		called = false
+	}
+
+	// Let's send a request with a 1 missing parameter, and another outside
+	// or the parameters. It should return a bad status
+	{
+		res := doGet(t, i, "https://deepmap.ai/multiparamresource?id=500")
+		assert.Equal(t, http.StatusBadRequest, res.Code)
+		body, err := io.ReadAll(res.Body)
+		if assert.NoError(t, err) {
+			assert.Contains(t, string(body), "Bad stuff")
+			assert.Contains(t, string(body), "parameter \"id\"")
+			assert.Contains(t, string(body), "number must be at most 100")
+			assert.Contains(t, string(body), "parameter \"id2\"")
+			assert.Contains(t, string(body), "value is required but missing")
+		}
+		assert.False(t, called, "Handler should not have been called")
+		called = false
+	}
+
+	// Let's send a request with a parameters that do not meet spec. It should
+	// return a bad status
+	{
+		res := doGet(t, i, "https://deepmap.ai/multiparamresource?id=abc&id2=1")
+		assert.Equal(t, http.StatusBadRequest, res.Code)
+		body, err := io.ReadAll(res.Body)
+		if assert.NoError(t, err) {
+			assert.Contains(t, string(body), "Bad stuff")
+			assert.Contains(t, string(body), "parameter \"id\"")
+			assert.Contains(t, string(body), "value abc: an invalid integer: invalid syntax")
+			assert.Contains(t, string(body), "parameter \"id2\"")
+			assert.Contains(t, string(body), "number must be at least 10")
+		}
+		assert.False(t, called, "Handler should not have been called")
+		called = false
+	}
+}
diff --git a/pkg/iris-middleware/test_spec.yaml b/pkg/iris-middleware/test_spec.yaml
new file mode 100644
index 0000000..1f847d7
--- /dev/null
+++ b/pkg/iris-middleware/test_spec.yaml
@@ -0,0 +1,103 @@
+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
+  /multiparamresource:
+    get:
+      operationId: getResource
+      parameters:
+        - name: id
+          in: query
+          required: true
+          schema:
+            type: integer
+            minimum: 10
+            maximum: 100
+        - name: id2
+          required: true
+          in: query
+          schema:
+            type: integer
+            minimum: 10
+            maximum: 100
+      responses:
+        '200':
+          description: success
+          content:
+            application/json:
+              schema:
+                properties:
+                  name:
+                    type: string
+                  id:
+                    type: integer
+components:
+  securitySchemes:
+    BearerAuth:
+      type: http
+      scheme: bearer
+      bearerFormat: JWT