Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 88 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,97 @@
# `net/http` Middleware
# OpenAPI Validation Middleware for `net/http`-compatible servers

An HTTP middleware to perform validation of incoming requests via an OpenAPI specification.

This project is a lightweight wrapper over the excellent [kin-openapi](https://github.com/getkin/kin-openapi) library's [`openapi3filter` package](https://pkg.go.dev/github.com/getkin/kin-openapi/openapi3filter).

This is _intended_ to be used with code that's generated through [`oapi-codegen`](https://github.com/oapi-codegen/oapi-codegen), but should work otherwise.

⚠️ This README may be for the latest development version, which may contain unreleased changes. Please ensure you're looking at the README for the latest release version.

Middleware for servers that implement `net/http` handlers, for use with [deepmap/oapi-codegen](https://github.com/deepmap/oapi-codegen), which has been tested to work with:
## Usage

You can add the middleware to your project with:

```sh
go get github.com/oapi-codegen/nethttp-middleware
```

There is a full example of usage in [the Go doc for this project](https://pkg.go.dev/github.com/oapi-codegen/nethttp-middleware#pkg-examples).

A simplified version of this code is as follows:

```go
rawSpec := `
openapi: "3.0.0"
# ...
`
spec, _ := openapi3.NewLoader().LoadFromData([]byte(rawSpec))

// NOTE that we need to make sure that the `Servers` aren't set, otherwise the OpenAPI validation middleware will validate that the `Host` header (of incoming requests) are targeting known `Servers` in the OpenAPI spec
// See also: Options#SilenceServersWarning
spec.Servers = nil

router := http.NewServeMux()

router.HandleFunc("/resource", func(w http.ResponseWriter, r *http.Request) {
fmt.Printf("%s /resource was called\n", r.Method)

if r.Method == http.MethodPost {
w.WriteHeader(http.StatusNoContent)
return
}

w.WriteHeader(http.StatusMethodNotAllowed)
})

use := func(r *http.ServeMux, middlewares ...func(next http.Handler) http.Handler) http.Handler {
var s http.Handler
s = r

for _, mw := range middlewares {
s = mw(s)
}

return s
}

// create middleware
mw := middleware.OapiRequestValidatorWithOptions(spec, &middleware.Options{
Options: openapi3filter.Options{
AuthenticationFunc: authenticationFunc,
},
})

// then wire it in
server := use(router, mw)

// now all HTTP routes will be handled by the middleware, and any requests that are invalid will be rejected
```

## FAQs

### Which HTTP servers should this work with?

If you're using something that's compliant with `net/http` (which should be all Go web frameworks / routers / HTTP servers) it should work as-is.

We explicitly test with the following servers, as they correspond with versions used by users of [oapi-codegen/oapi-codegen](https://github.com/oapi-codegen/oapi-codegen):

- [Chi](https://github.com/go-chi/chi)
- [gorilla/mux](https://github.com/gorilla/mux)
- [net/http](https://pkg.go.dev/net/http)

But if you're using something that's compliant with `net/http` it should work as-is.
### "This doesn't support ..." / "I think it's a bug that ..."

As this project is a lightweight wrapper over [kin-openapi](https://github.com/getkin/kin-openapi)'s [`openapi3filter` package](https://pkg.go.dev/github.com/getkin/kin-openapi/openapi3filter), it's _likely_ that any bugs/features are better sent upstream.

However, it's worth raising an issue here instead, as it'll allow us to triage it before it goes to the kin-openapi maintainers.

Additionally, as `oapi-codegen` contains [a number of middleware modules](https://github.com/search?q=org%3Aoapi-codegen+middleware&type=repositories), we'll very likely want to implement the same functionality across all the middlewares, so it may take a bit more coordination to get the changes in across our middlewares.

### I've just updated my version of `kin-openapi`, and now I can't build my code 😠

The [kin-openapi](https://github.com/getkin/kin-openapi) project - which we 💜 for providing a great library and set of tooling for interacting with OpenAPI - is a pre-v1 release, which means that they're within their rights to push breaking changes.

This may lead to breakage in your consuming code, and if so, sorry that's happened!

Licensed under the Apache-2.0.
We'll be aware of the issue, and will work to update both the core `oapi-codegen` and the middlewares accordingly.
31 changes: 22 additions & 9 deletions oapi_validate.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
// Package middleware implements middleware function for net/http compatible router
// 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.
// Provide HTTP middleware functionality to validate that incoming requests conform to a given OpenAPI 3.x specification.
//
// This provides middleware for any `net/http` conforming HTTP Server.
//
// This package is a lightweight wrapper over https://pkg.go.dev/github.com/getkin/kin-openapi/openapi3filter from https://pkg.go.dev/github.com/getkin/kin-openapi.
//
// This is _intended_ to be used with code that's generated through https://pkg.go.dev/github.com/oapi-codegen/oapi-codegen, but should work otherwise.
package nethttpmiddleware

import (
Expand All @@ -19,24 +23,33 @@ import (
// ErrorHandler is called when there is an error in validation
type ErrorHandler func(w http.ResponseWriter, message string, statusCode int)

// MultiErrorHandler is called when oapi returns a MultiError type
// MultiErrorHandler is called when the OpenAPI filter returns an openapi3.MultiError (https://pkg.go.dev/github.com/getkin/kin-openapi/openapi3#MultiError)
type MultiErrorHandler func(openapi3.MultiError) (int, error)

// Options to customize request validation, openapi3filter specified options will be passed through.
// Options allows configuring the OapiRequestValidator.
type Options struct {
Options openapi3filter.Options
ErrorHandler ErrorHandler
// Options contains any configuration for the underlying `openapi3filter`
Options openapi3filter.Options
// ErrorHandler is called when a validation error occurs.
//
// If not provided, `http.Error` will be called
ErrorHandler ErrorHandler
// MultiErrorHandler is called when there is an openapi3.MultiError (https://pkg.go.dev/github.com/getkin/kin-openapi/openapi3#MultiError) returned by the `openapi3filter`.
//
// If not provided `defaultMultiErrorHandler` will be used.
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
}

// OapiRequestValidator Creates middleware to validate request by OpenAPI spec.
// OapiRequestValidator Creates the middleware to validate that incoming requests match the given OpenAPI 3.x spec, with a default set of configuration.
func OapiRequestValidator(spec *openapi3.T) func(next http.Handler) http.Handler {
return OapiRequestValidatorWithOptions(spec, nil)
}

// OapiRequestValidatorWithOptions Creates middleware to validate request by OpenAPI spec.
// OapiRequestValidatorWithOptions Creates the middleware to validate that incoming requests match the given OpenAPI 3.x spec, allowing explicit configuration.
//
// NOTE that this may panic if the OpenAPI spec isn't valid, or if it cannot be used to create the middleware
func OapiRequestValidatorWithOptions(spec *openapi3.T, options *Options) func(next http.Handler) http.Handler {
if spec.Servers != nil && (options == nil || !options.SilenceServersWarning) {
log.Println("WARN: OapiRequestValidatorWithOptions called with an OpenAPI spec that has `Servers` set. This may lead to an HTTP 400 with `no matching operation was found` when sending a valid request, as the validator performs `Host` header validation. If you're expecting `Host` header validation, you can silence this warning by setting `Options.SilenceServersWarning = true`. See https://github.com/deepmap/oapi-codegen/issues/882 for more information.")
Expand Down
Loading