feat: implement response body validation with strict mode and improve…#2
feat: implement response body validation with strict mode and improve…#2amirHdev wants to merge 2 commits intophumberdroz:mainfrom
Conversation
…d errors
- Capture outgoing response body using custom responseBodyWriter
- Validate responses against OpenAPI spec using openapi3filter.ValidateResponse
- Introduce ValidatorOptions{StrictResponse: bool} → return 500 on invalid responses when enabled
- Add custom, user-friendly error messages for common schema violations
- Modernize OpenAPI document loading with openapi3.NewLoader
- Log validation failures with structured error context
- Add/update tests covering response validation, strict mode behavior, and error formatting
Signed-off-by: Amirhossein Akhlaghpour <m9.akhlaghpoor@gmail.com>
📝 WalkthroughWalkthroughAdds a CI workflow, updates module dependencies and Go toolchain, and implements enhanced OpenAPI request/response validation middleware (response capture, strict-response option, router switch) with README usage examples. Changes
Sequence Diagram(s)sequenceDiagram
participant Client as Client
participant Gin as Gin Handler
participant Validator as Validator Middleware
participant Router as OpenAPI Router
participant RespWriter as Response Body Writer
Client->>Gin: Send HTTP request
Gin->>Validator: Enter middleware (Validator)
Validator->>Router: Match route & validate request
alt Request validation fails
Validator->>Gin: Return validation error response
Gin->>Client: Error response
else Request valid
Validator->>RespWriter: Wrap original response writer (capture headers/body/status)
Gin->>RespWriter: Invoke handler (writes to wrapped writer)
RespWriter->>Validator: Provide captured status, headers, body
Validator->>Router: Validate response against OpenAPI schema
alt StrictResponse true and validation fails
Validator->>Client: Return 500 / strict-failure response
else Validation passes or non-strict
RespWriter->>Client: Flush captured response
end
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@pkg/gin-openapi-validator/validator.go`:
- Around line 92-122: The response is currently written to the client inside
responseBodyWriter.Write, so ValidateResponse runs after the response is already
sent and AbortWithStatusJSON cannot replace it; change the flow so
responseBodyWriter.Write only buffers into its body buffer (do not write to the
original ResponseWriter), collect status and headers into
responseValidationInput, call openapi3filter.ValidateResponse, and only if
validation succeeds flush the buffered headers, status and body to the original
ResponseWriter (write header values, call WriteHeader with the captured status,
then write the buffered bytes); if validation fails and options.StrictResponse
is true, send the 500 JSON via the original ResponseWriter instead of attempting
AbortWithStatusJSON. Ensure you update the responseBodyWriter.Write
implementation and the post-c.Next logic around ValidateResponse, referencing
responseBodyWriter.Write, responseValidationInput, ValidateResponse, c.Next, and
AbortWithStatusJSON.
🧹 Nitpick comments (3)
.github/workflows/ci.yml (1)
26-29: Consider pinning golangci-lint version for reproducibility.Using
version: latestcan lead to inconsistent CI results when new linter versions introduce breaking changes or new rules. Pinning to a specific version (e.g.,v1.61.0) improves build reproducibility.pkg/gin-openapi-validator/validator.go (1)
40-40: Format validator registration on every call.
DefineStringFormatValidatoris called each timeValidator()is invoked. While likely idempotent, consider moving this to async.Onceor packageinit()to ensure it runs exactly once.♻️ Suggested improvement
+var formatValidatorOnce sync.Once + func Validator(doc []byte, opts ...ValidatorOptions) gin.HandlerFunc { var options ValidatorOptions if len(opts) > 0 { options = opts[0] } - openapi3.DefineStringFormatValidator("uuid", openapi3.NewRegexpFormatValidator(openapi3.FormatOfStringForUUIDOfRFC4122)) + formatValidatorOnce.Do(func() { + openapi3.DefineStringFormatValidator("uuid", openapi3.NewRegexpFormatValidator(openapi3.FormatOfStringForUUIDOfRFC4122)) + })README.md (1)
8-14: Consider adding brief explanation of each mode's behavior.The examples are correct, but users would benefit from knowing what happens in each mode:
- Basic mode: Logs violations but allows the response through
- Strict mode: Returns HTTP 500 when response violates the spec
This helps users choose the appropriate mode for their use case.
📝 Suggested documentation enhancement
```go import "github.com/phumberdroz/gin-openapi-validator" -// Basic (logs violations only) +// Basic mode: validates requests and responses, logs violations but allows invalid responses through r.Use(ginopenapivalidator.Validator(openapiYAMLBytes)) -// Strict mode: return 500 when response is invalid +// Strict mode: returns HTTP 500 when the response body violates the OpenAPI specification +// Use this in development/staging to catch API contract violations early r.Use(ginopenapivalidator.Validator(openapiYAMLBytes, ginopenapivalidator.ValidatorOptions{ StrictResponse: true, })) ```
Signed-off-by: Amirhossein Akhlaghpour <m9.akhlaghpoor@gmail.com>
Features
responseBodyWriteropenapi3filter.ValidateResponseValidatorOptions.StrictResponse bool(returns 500 on violation)openapi3.NewLoader(external refs allowed)Tests
Documentation
Motivation
Improves API contract enforcement and debugging experience without breaking.
Summary by CodeRabbit
New Features
Bug Fixes
Documentation
Chores