Documentation
¶
Overview ¶
Package httpvalidator validates HTTP requests and responses against OpenAPI specifications.
This package enables runtime validation of HTTP traffic in API gateways, middleware, and testing scenarios. It supports both OAS 2.0 (Swagger) and OAS 3.x specifications.
Features ¶
- Request validation: path, query, header, cookie parameters and request body
- Response validation: status codes, headers, and response body
- Parameter deserialization: all OAS serialization styles (simple, form, matrix, label, etc.)
- Schema validation: type checking, constraints, enum, composition (allOf/anyOf/oneOf)
- Middleware-friendly: works with standard net/http patterns
- Strict mode: reject unknown parameters and undocumented responses
Basic Usage ¶
Create a validator from a parsed OpenAPI specification:
parsed, _ := parser.ParseWithOptions(parser.WithFilePath("openapi.yaml"))
v, err := httpvalidator.New(parsed)
if err != nil {
log.Fatal(err)
}
// Validate incoming request
result, err := v.ValidateRequest(req)
if !result.Valid {
for _, e := range result.Errors {
log.Printf("Validation error: %s: %s", e.Path, e.Message)
}
}
// Access validated and deserialized parameters
userID := result.PathParams["userId"]
page := result.QueryParams["page"]
Middleware Pattern ¶
The validator integrates naturally with HTTP middleware:
func ValidateMiddleware(v *httpvalidator.Validator) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
result, _ := v.ValidateRequest(r)
if !result.Valid {
http.Error(w, "Invalid request", http.StatusBadRequest)
return
}
next.ServeHTTP(w, r)
})
}
}
For response validation in middleware, use ValidateResponseData which accepts captured response parts instead of *http.Response:
result, _ := v.ValidateResponseData(req, recorder.Code, recorder.Header(), recorder.Body.Bytes())
Functional Options ¶
For one-off validations, use the functional options API:
result, err := httpvalidator.ValidateRequestWithOptions(
req,
httpvalidator.WithFilePath("openapi.yaml"),
httpvalidator.WithStrictMode(true),
)
Strict Mode ¶
Enable strict mode for stricter validation:
v.StrictMode = true
In strict mode:
- Unknown query parameters cause validation errors
- Unknown headers (except standard HTTP headers) cause errors
- Unknown cookies cause errors
- Undocumented response status codes cause errors
Parameter Deserialization ¶
The validator automatically deserializes parameters according to their OAS style:
- path: simple (default), label, matrix
- query: form (default), spaceDelimited, pipeDelimited, deepObject
- header: simple (default)
- cookie: form (default)
Deserialized values are available in the result:
result.PathParams["id"] // Deserialized path parameter result.QueryParams["page"] // Deserialized query parameter result.HeaderParams["X-API"] // Deserialized header parameter result.CookieParams["token"] // Deserialized cookie parameter
Schema Validation ¶
The validator performs JSON Schema validation on request/response bodies including:
- Type checking (string, number, integer, boolean, array, object, null)
- String constraints (minLength, maxLength, pattern, format, enum)
- Number constraints (minimum, maximum, exclusiveMin/Max, multipleOf)
- Array constraints (minItems, maxItems, uniqueItems)
- Object constraints (required, properties, additionalProperties)
- Composition (allOf, anyOf, oneOf)
- Nullable fields (OAS 3.0 nullable, OAS 3.1 type arrays)
Index ¶
- Constants
- type Option
- func WithFilePath(path string) Option
- func WithIncludeWarnings(include bool) Option
- func WithParsed(result *parser.ParseResult) Option
- func WithSkipBodyValidation(skip bool) Option
- func WithSkipCookieValidation(skip bool) Option
- func WithSkipHeaderValidation(skip bool) Option
- func WithSkipQueryValidation(skip bool) Option
- func WithStrictMode(strict bool) Option
- type ParamDeserializer
- func (d *ParamDeserializer) DeserializeCookieParam(value string, param *parser.Parameter) any
- func (d *ParamDeserializer) DeserializeHeaderParam(value string, param *parser.Parameter) any
- func (d *ParamDeserializer) DeserializePathParam(value string, param *parser.Parameter) any
- func (d *ParamDeserializer) DeserializeQueryParam(values []string, param *parser.Parameter) any
- func (d *ParamDeserializer) DeserializeQueryParamsDeepObject(queryValues url.Values, paramName string, schema *parser.Schema) map[string]any
- type PathMatcher
- type PathMatcherSet
- type RequestValidationResult
- type ResponseValidationResult
- type SchemaValidator
- type Severity
- type ValidationError
- type ValidationLocation
- type Validator
- func (v *Validator) IsOAS2() bool
- func (v *Validator) IsOAS3() bool
- func (v *Validator) ValidateRequest(req *http.Request) (*RequestValidationResult, error)
- func (v *Validator) ValidateResponse(req *http.Request, resp *http.Response) (*ResponseValidationResult, error)
- func (v *Validator) ValidateResponseData(req *http.Request, statusCode int, headers http.Header, body []byte) (*ResponseValidationResult, error)
Examples ¶
Constants ¶
const ( SeverityError = severity.SeverityError SeverityWarning = severity.SeverityWarning SeverityInfo = severity.SeverityInfo SeverityCritical = severity.SeverityCritical )
Severity constants re-exported for convenience.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Option ¶
type Option func(*config) error
Option is a functional option for configuring validation.
func WithFilePath ¶
WithFilePath sets the path to the OpenAPI specification file. The file will be parsed automatically.
func WithIncludeWarnings ¶
WithIncludeWarnings sets whether to include best-practice warnings. Default is true.
func WithParsed ¶
func WithParsed(result *parser.ParseResult) Option
WithParsed uses a pre-parsed OpenAPI specification. This is more efficient when validating multiple requests.
func WithSkipBodyValidation ¶
WithSkipBodyValidation skips request/response body validation. Useful when body validation is too expensive or handled elsewhere.
func WithSkipCookieValidation ¶
WithSkipCookieValidation skips cookie parameter validation.
func WithSkipHeaderValidation ¶
WithSkipHeaderValidation skips header parameter validation.
func WithSkipQueryValidation ¶
WithSkipQueryValidation skips query parameter validation.
func WithStrictMode ¶
WithStrictMode enables stricter validation:
- Rejects requests with unknown query parameters
- Rejects requests with unknown headers (except standard HTTP headers)
- Rejects requests with unknown cookies
- Rejects responses with undocumented status codes
Default is false.
type ParamDeserializer ¶
type ParamDeserializer struct{}
ParamDeserializer handles deserialization of HTTP parameters according to OpenAPI serialization styles. Each parameter location has default styles:
| Location | Default Style | Default Explode | |----------|---------------|-----------------| | path | simple | false | | query | form | true | | header | simple | false | | cookie | form | false |
func NewParamDeserializer ¶
func NewParamDeserializer() *ParamDeserializer
NewParamDeserializer creates a new parameter deserializer.
func (*ParamDeserializer) DeserializeCookieParam ¶
func (d *ParamDeserializer) DeserializeCookieParam(value string, param *parser.Parameter) any
DeserializeCookieParam deserializes a cookie parameter value. Cookie parameters default to style "form" with explode=false.
func (*ParamDeserializer) DeserializeHeaderParam ¶
func (d *ParamDeserializer) DeserializeHeaderParam(value string, param *parser.Parameter) any
DeserializeHeaderParam deserializes a header parameter value. Header parameters default to style "simple" with explode=false.
func (*ParamDeserializer) DeserializePathParam ¶
func (d *ParamDeserializer) DeserializePathParam(value string, param *parser.Parameter) any
DeserializePathParam deserializes a path parameter value according to its style. Path parameters default to style "simple" with explode=false.
Styles supported:
- simple (default): comma-separated values, e.g., "a,b,c"
- label: dot-prefixed values, e.g., ".a.b.c"
- matrix: semicolon-prefixed key=value, e.g., ";id=5"
func (*ParamDeserializer) DeserializeQueryParam ¶
func (d *ParamDeserializer) DeserializeQueryParam(values []string, param *parser.Parameter) any
DeserializeQueryParam deserializes query parameter values according to their style. Query parameters default to style "form" with explode=true.
Styles supported:
- form (default): standard query string format
- spaceDelimited: space-separated values
- pipeDelimited: pipe-separated values
- deepObject: nested object notation, e.g., "filter[status]=active"
func (*ParamDeserializer) DeserializeQueryParamsDeepObject ¶
func (d *ParamDeserializer) DeserializeQueryParamsDeepObject(queryValues url.Values, paramName string, schema *parser.Schema) map[string]any
DeserializeQueryParamsDeepObject deserializes query parameters using deepObject style. This handles nested object notation like "filter[status]=active&filter[type]=user".
Returns a map representing the nested object structure.
type PathMatcher ¶
type PathMatcher struct {
// contains filtered or unexported fields
}
PathMatcher handles matching request paths against OpenAPI path templates. It converts path templates like "/pets/{petId}" into regex patterns and extracts parameter values from actual request paths.
Example ¶
package main
import (
"fmt"
"github.com/erraggy/oastools/httpvalidator"
)
func main() {
// Create a path matcher from a template
matcher, err := httpvalidator.NewPathMatcher("/users/{userId}/posts/{postId}")
if err != nil {
fmt.Println("Error:", err)
return
}
// Test matching
matched, params := matcher.Match("/users/42/posts/101")
fmt.Println("Matched:", matched)
fmt.Println("userId:", params["userId"])
fmt.Println("postId:", params["postId"])
}
Output: Matched: true userId: 42 postId: 101
func NewPathMatcher ¶
func NewPathMatcher(template string) (*PathMatcher, error)
NewPathMatcher creates a PathMatcher from an OpenAPI path template. The template should be in the format "/path/{param}/more/{param2}".
Returns an error if the template is malformed (e.g., unclosed braces).
func (*PathMatcher) Match ¶
func (pm *PathMatcher) Match(path string) (bool, map[string]string)
Match checks if the given path matches this template and extracts parameters. Returns true and a map of parameter names to values if the path matches. Returns false and nil if the path does not match.
func (*PathMatcher) ParamNames ¶
func (pm *PathMatcher) ParamNames() []string
ParamNames returns the list of parameter names in order of appearance.
func (*PathMatcher) Template ¶
func (pm *PathMatcher) Template() string
Template returns the original path template.
type PathMatcherSet ¶
type PathMatcherSet struct {
// contains filtered or unexported fields
}
PathMatcherSet manages a collection of path matchers and finds the best match for a given request path according to OpenAPI specification precedence rules.
Example ¶
package main
import (
"fmt"
"github.com/erraggy/oastools/httpvalidator"
)
func main() {
// Create matchers for multiple paths
templates := []string{
"/pets",
"/pets/{petId}",
"/pets/{petId}/owner",
"/users/{userId}",
}
set, _ := httpvalidator.NewPathMatcherSet(templates)
// Match a request path
template, params, found := set.Match("/pets/123/owner")
fmt.Println("Found:", found)
fmt.Println("Template:", template)
fmt.Println("petId:", params["petId"])
}
Output: Found: true Template: /pets/{petId}/owner petId: 123
func NewPathMatcherSet ¶
func NewPathMatcherSet(templates []string) (*PathMatcherSet, error)
NewPathMatcherSet creates a new PathMatcherSet from a list of path templates. The matchers are sorted by specificity so that more specific paths match first.
func (*PathMatcherSet) Match ¶
func (pms *PathMatcherSet) Match(path string) (template string, params map[string]string, found bool)
Match finds the best matching path template for the given request path. Returns the matched template, extracted parameters, and whether a match was found.
According to OpenAPI spec, paths are matched in order of specificity: 1. Exact matches before parameterized paths 2. More specific templates before less specific ones 3. Longer paths before shorter paths
func (*PathMatcherSet) Templates ¶
func (pms *PathMatcherSet) Templates() []string
Templates returns all path templates in the set.
type RequestValidationResult ¶
type RequestValidationResult struct {
// Valid is true if the request passes all validation checks.
Valid bool
// Errors contains all validation errors found.
Errors []ValidationError
// Warnings contains best-practice warnings (if IncludeWarnings is enabled).
Warnings []ValidationError
// MatchedPath is the OpenAPI path template that matched the request
// (e.g., "/pets/{petId}"). Empty if no path matched.
MatchedPath string
// MatchedMethod is the HTTP method of the request (e.g., "GET", "POST").
MatchedMethod string
// PathParams contains the extracted and validated path parameters.
// Keys are parameter names, values are the deserialized values.
PathParams map[string]any
// QueryParams contains the extracted and validated query parameters.
QueryParams map[string]any
// HeaderParams contains the extracted and validated header parameters.
HeaderParams map[string]any
// CookieParams contains the extracted and validated cookie parameters.
CookieParams map[string]any
}
RequestValidationResult contains the results of validating an HTTP request against an OpenAPI specification.
func ValidateRequestWithOptions ¶
func ValidateRequestWithOptions(req *http.Request, opts ...Option) (*RequestValidationResult, error)
ValidateRequestWithOptions validates an HTTP request against an OpenAPI specification using functional options.
This is a convenience function for one-off validations. For validating multiple requests, use New() to create a reusable Validator instance.
Example:
result, err := httpvalidator.ValidateRequestWithOptions(
req,
httpvalidator.WithFilePath("openapi.yaml"),
httpvalidator.WithStrictMode(true),
)
Example ¶
package main
import (
"fmt"
"net/http/httptest"
"strings"
"github.com/erraggy/oastools/httpvalidator"
"github.com/erraggy/oastools/parser"
)
func main() {
specYAML := `
openapi: "3.0.0"
info:
title: API
version: "1.0"
paths:
/users:
post:
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [email]
properties:
email:
type: string
format: email
responses:
"201":
description: Created
`
parsed, _ := parser.ParseWithOptions(parser.WithBytes([]byte(specYAML)))
// Create request with JSON body
body := strings.NewReader(`{"email": "user@example.com"}`)
req := httptest.NewRequest("POST", "/users", body)
req.Header.Set("Content-Type", "application/json")
// Validate using functional options
result, _ := httpvalidator.ValidateRequestWithOptions(
req,
httpvalidator.WithParsed(parsed),
httpvalidator.WithStrictMode(true),
)
fmt.Println("Valid:", result.Valid)
fmt.Println("Matched path:", result.MatchedPath)
}
Output: Valid: true Matched path: /users
type ResponseValidationResult ¶
type ResponseValidationResult struct {
// Valid is true if the response passes all validation checks.
Valid bool
// Errors contains all validation errors found.
Errors []ValidationError
// Warnings contains best-practice warnings (if IncludeWarnings is enabled).
Warnings []ValidationError
// StatusCode is the HTTP status code of the response.
StatusCode int
// ContentType is the Content-Type of the response.
ContentType string
// MatchedPath is the OpenAPI path template that matched the original request.
MatchedPath string
// MatchedMethod is the HTTP method of the original request.
MatchedMethod string
}
ResponseValidationResult contains the results of validating an HTTP response against an OpenAPI specification.
func ValidateResponseDataWithOptions ¶
func ValidateResponseDataWithOptions(req *http.Request, statusCode int, headers http.Header, body []byte, opts ...Option) (*ResponseValidationResult, error)
ValidateResponseDataWithOptions validates response data (for middleware use) using functional options.
This is useful in middleware where you've captured response parts in a ResponseRecorder but don't have an *http.Response.
Example:
result, err := httpvalidator.ValidateResponseDataWithOptions(
req, recorder.Code, recorder.Header(), recorder.Body.Bytes(),
httpvalidator.WithFilePath("openapi.yaml"),
)
func ValidateResponseWithOptions ¶
func ValidateResponseWithOptions(req *http.Request, resp *http.Response, opts ...Option) (*ResponseValidationResult, error)
ValidateResponseWithOptions validates an HTTP response against an OpenAPI specification using functional options.
This is a convenience function for one-off validations. For validating multiple responses, use New() to create a reusable Validator instance.
Example:
result, err := httpvalidator.ValidateResponseWithOptions(
req, resp,
httpvalidator.WithFilePath("openapi.yaml"),
httpvalidator.WithIncludeWarnings(false),
)
type SchemaValidator ¶
type SchemaValidator struct {
// contains filtered or unexported fields
}
SchemaValidator validates data values against OpenAPI schemas. It implements a minimal subset of JSON Schema validation suitable for validating HTTP request and response bodies.
func NewSchemaValidator ¶
func NewSchemaValidator() *SchemaValidator
NewSchemaValidator creates a new SchemaValidator.
func (*SchemaValidator) Validate ¶
func (v *SchemaValidator) Validate(data any, schema *parser.Schema, path string) []ValidationError
Validate validates data against an OpenAPI schema. Returns a slice of validation errors (empty if valid).
type ValidationError ¶
ValidationError represents a single HTTP validation issue. This is an alias to issues.Issue for consistency with other oastools packages.
type ValidationLocation ¶
type ValidationLocation string
ValidationLocation indicates where in the HTTP message the error occurred.
const ( LocationPath ValidationLocation = "path" LocationQuery ValidationLocation = "query" LocationHeader ValidationLocation = "header" LocationCookie ValidationLocation = "cookie" LocationRequestBody ValidationLocation = "requestBody" LocationResponse ValidationLocation = "response" )
Validation location constants.
type Validator ¶
type Validator struct {
// IncludeWarnings determines whether to include best practice warnings
// in validation results. Default is true.
IncludeWarnings bool
// StrictMode enables stricter validation behavior:
// - Rejects requests with unknown query parameters
// - Rejects requests with unknown headers
// - Rejects responses with undocumented status codes
StrictMode bool
// contains filtered or unexported fields
}
Validator validates HTTP requests and responses against an OpenAPI specification. It supports both OAS 2.0 (Swagger) and OAS 3.x specifications.
Create a Validator using the New function:
parsed, _ := parser.ParseWithOptions(parser.WithFilePath("openapi.yaml"))
v, err := httpvalidator.New(parsed)
if err != nil {
log.Fatal(err)
}
result, err := v.ValidateRequest(req)
if !result.Valid {
// Handle validation errors
}
func New ¶
func New(parsed *parser.ParseResult) (*Validator, error)
New creates a new HTTP Validator from a parsed OpenAPI specification. The validator pre-compiles path matchers for efficient matching.
Returns an error if the parsed result is nil or contains invalid path templates.
Example ¶
package main
import (
"fmt"
"github.com/erraggy/oastools/httpvalidator"
"github.com/erraggy/oastools/parser"
)
func main() {
// Create a minimal spec inline for the example
specYAML := `
openapi: "3.0.0"
info:
title: Pet Store
version: "1.0"
paths:
/pets:
get:
responses:
"200":
description: Success
`
// Parse an OpenAPI specification
parsed, err := parser.ParseWithOptions(parser.WithBytes([]byte(specYAML)))
if err != nil {
fmt.Println("Parse error:", err)
return
}
// Create a validator
v, err := httpvalidator.New(parsed)
if err != nil {
fmt.Println("Validator error:", err)
return
}
// The validator is ready to validate requests and responses
fmt.Println("Validator created, strict mode:", v.StrictMode)
}
Output: Validator created, strict mode: false
func (*Validator) ValidateRequest ¶
func (v *Validator) ValidateRequest(req *http.Request) (*RequestValidationResult, error)
ValidateRequest validates an HTTP request against the OpenAPI specification. It checks path parameters, query parameters, headers, cookies, and request body.
Returns a RequestValidationResult containing validation errors and extracted parameters. The error return is reserved for internal errors (e.g., body reading failures), not validation errors which are captured in the result.
Example ¶
package main
import (
"fmt"
"net/http/httptest"
"github.com/erraggy/oastools/httpvalidator"
"github.com/erraggy/oastools/parser"
)
func main() {
// Create a minimal OAS 3.0 spec for the example
specYAML := `
openapi: "3.0.0"
info:
title: Pet Store
version: "1.0"
paths:
/pets/{petId}:
get:
parameters:
- name: petId
in: path
required: true
schema:
type: integer
- name: include
in: query
schema:
type: string
enum: [owner, vaccinations, all]
responses:
"200":
description: Success
`
parsed, _ := parser.ParseWithOptions(parser.WithBytes([]byte(specYAML)))
v, _ := httpvalidator.New(parsed)
// Create a test request
req := httptest.NewRequest("GET", "/pets/123?include=owner", nil)
// Validate the request
result, err := v.ValidateRequest(req)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("Valid:", result.Valid)
fmt.Println("Matched path:", result.MatchedPath)
fmt.Println("Pet ID:", result.PathParams["petId"])
}
Output: Valid: true Matched path: /pets/{petId} Pet ID: 123
Example (Invalid) ¶
package main
import (
"fmt"
"net/http/httptest"
"github.com/erraggy/oastools/httpvalidator"
"github.com/erraggy/oastools/parser"
)
func main() {
specYAML := `
openapi: "3.0.0"
info:
title: Pet Store
version: "1.0"
paths:
/pets/{petId}:
get:
parameters:
- name: petId
in: path
required: true
schema:
type: integer
minimum: 1
responses:
"200":
description: Success
`
parsed, _ := parser.ParseWithOptions(parser.WithBytes([]byte(specYAML)))
v, _ := httpvalidator.New(parsed)
// Request with invalid petId (not an integer)
req := httptest.NewRequest("GET", "/pets/abc", nil)
result, _ := v.ValidateRequest(req)
fmt.Println("Valid:", result.Valid)
if len(result.Errors) > 0 {
fmt.Println("First error:", result.Errors[0].Message)
}
}
Output: Valid: false First error: expected type integer but got string
func (*Validator) ValidateResponse ¶
func (v *Validator) ValidateResponse(req *http.Request, resp *http.Response) (*ResponseValidationResult, error)
ValidateResponse validates an HTTP response against the OpenAPI specification. It checks the status code, response headers, and response body.
The original request is needed to determine which operation's response to validate against.
Returns a ResponseValidationResult containing validation errors. The error return is reserved for internal errors (e.g., body reading failures), not validation errors which are captured in the result.
func (*Validator) ValidateResponseData ¶
func (v *Validator) ValidateResponseData(req *http.Request, statusCode int, headers http.Header, body []byte) (*ResponseValidationResult, error)
ValidateResponseData validates response data without requiring an *http.Response. This is useful for middleware scenarios where you've captured response parts but don't have an *http.Response object.
Parameters:
- req: The original HTTP request (to determine the operation)
- statusCode: The HTTP status code of the response
- headers: Response headers
- body: Response body bytes (can be nil for bodyless responses)
Example middleware usage:
result, err := v.ValidateResponseData(req, rec.Code, rec.Header(), rec.Body.Bytes())
Example ¶
package main
import (
"fmt"
"net/http"
"net/http/httptest"
"github.com/erraggy/oastools/httpvalidator"
"github.com/erraggy/oastools/parser"
)
func main() {
specYAML := `
openapi: "3.0.0"
info:
title: Pet Store
version: "1.0"
paths:
/pets/{petId}:
get:
responses:
"200":
description: Success
content:
application/json:
schema:
type: object
required: [id, name]
properties:
id:
type: integer
name:
type: string
`
parsed, _ := parser.ParseWithOptions(parser.WithBytes([]byte(specYAML)))
v, _ := httpvalidator.New(parsed)
// Original request
req := httptest.NewRequest("GET", "/pets/123", nil)
// Captured response data (simulating middleware capture)
statusCode := 200
headers := http.Header{"Content-Type": []string{"application/json"}}
body := []byte(`{"id": 123, "name": "Fluffy"}`)
// Validate the response
result, _ := v.ValidateResponseData(req, statusCode, headers, body)
fmt.Println("Valid:", result.Valid)
fmt.Println("Status code:", result.StatusCode)
}
Output: Valid: true Status code: 200