Skip to content

Commit 3b6cba0

Browse files
committed
sq
1 parent 124a344 commit 3b6cba0

File tree

2 files changed

+130
-0
lines changed

2 files changed

+130
-0
lines changed

oapi_validate.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,9 @@ type Options struct {
103103
SilenceServersWarning bool
104104
// DoNotValidateServers ensures that there is no Host validation performed (see `SilenceServersWarning` and https://github.com/deepmap/oapi-codegen/issues/882 for more details)
105105
DoNotValidateServers bool
106+
// Prefix allows (optionally) trimming a prefix from the API path.
107+
// This may be useful if your API is routed to an internal path that is different from the OpenAPI specification.
108+
Prefix string
106109
}
107110

108111
// OapiRequestValidator Creates the middleware to validate that incoming requests match the given OpenAPI 3.x spec, with a default set of configuration.
@@ -159,6 +162,11 @@ func performRequestValidationForErrorHandler(next http.Handler, w http.ResponseW
159162
// Note that this is an inline-and-modified version of `validateRequest`, with a simplified control flow and providing full access to the `error` for the `ErrorHandlerWithOpts` function.
160163
func performRequestValidationForErrorHandlerWithOpts(next http.Handler, w http.ResponseWriter, r *http.Request, router routers.Router, options *Options) {
161164
// Find route
165+
166+
r.RequestURI = strings.TrimPrefix(r.RequestURI, options.Prefix)
167+
r.URL.Path = strings.TrimPrefix(r.URL.Path, options.Prefix)
168+
r.URL.RawPath = strings.TrimPrefix(r.URL.RawPath, options.Prefix)
169+
162170
route, pathParams, err := router.FindRoute(r)
163171
if err != nil {
164172
errOpts := ErrorHandlerOpts{
@@ -223,6 +231,9 @@ func performRequestValidationForErrorHandlerWithOpts(next http.Handler, w http.R
223231
// of validating a request.
224232
func validateRequest(r *http.Request, router routers.Router, options *Options) (int, error) {
225233

234+
r.RequestURI = strings.TrimPrefix(r.RequestURI, options.Prefix)
235+
r.URL.Path = strings.TrimPrefix(r.URL.Path, options.Prefix)
236+
226237
// Find route
227238
route, pathParams, err := router.FindRoute(r)
228239
if err != nil {

oapi_validate_example_test.go

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -843,3 +843,122 @@ paths:
843843
// Received an HTTP 400 response. Expected HTTP 400
844844
// Response body: There was a bad request
845845
}
846+
847+
// In the case that your public OpenAPI spec documents an API which does /not/ match your internal API endpoint setup, you may want to set the `Prefix` option to allow rewriting paths
848+
func ExampleOapiRequestValidatorWithOptions_withPrefix() {
849+
rawSpec := `
850+
openapi: "3.0.0"
851+
info:
852+
version: 1.0.0
853+
title: TestServer
854+
servers:
855+
- url: http://example.com/
856+
paths:
857+
/resource:
858+
post:
859+
operationId: createResource
860+
responses:
861+
'204':
862+
description: No content
863+
requestBody:
864+
required: true
865+
content:
866+
application/json:
867+
schema:
868+
properties:
869+
name:
870+
type: string
871+
additionalProperties: false
872+
`
873+
874+
must := func(err error) {
875+
if err != nil {
876+
panic(err)
877+
}
878+
}
879+
880+
use := func(r *http.ServeMux, middlewares ...func(next http.Handler) http.Handler) http.Handler {
881+
var s http.Handler
882+
s = r
883+
884+
for _, mw := range middlewares {
885+
s = mw(s)
886+
}
887+
888+
return s
889+
}
890+
891+
logResponseBody := func(rr *httptest.ResponseRecorder) {
892+
if rr.Result().Body != nil {
893+
data, _ := io.ReadAll(rr.Result().Body)
894+
if len(data) > 0 {
895+
fmt.Printf("Response body: %s", data)
896+
}
897+
}
898+
}
899+
900+
spec, err := openapi3.NewLoader().LoadFromData([]byte(rawSpec))
901+
must(err)
902+
903+
// 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
904+
// See also: Options#SilenceServersWarning
905+
spec.Servers = nil
906+
907+
router := http.NewServeMux()
908+
909+
router.HandleFunc("/public-api/v1/resource", func(w http.ResponseWriter, r *http.Request) {
910+
fmt.Printf("%s /public-api/v1/resource was called\n", r.Method)
911+
912+
if r.Method == http.MethodPost {
913+
w.WriteHeader(http.StatusNoContent)
914+
return
915+
}
916+
917+
w.WriteHeader(http.StatusMethodNotAllowed)
918+
})
919+
920+
router.HandleFunc("/internal-api/v2/resource", func(w http.ResponseWriter, r *http.Request) {
921+
fmt.Printf("%s /internal-api/v2/resource was called\n", r.Method)
922+
923+
w.WriteHeader(http.StatusMethodNotAllowed)
924+
})
925+
926+
// create middleware
927+
mw := middleware.OapiRequestValidatorWithOptions(spec, &middleware.Options{
928+
Options: openapi3filter.Options{
929+
// make sure that multiple errors in a given request are returned
930+
MultiError: true,
931+
},
932+
Prefix: "/public-api/v1/",
933+
})
934+
935+
// then wire it in
936+
server := use(router, mw)
937+
938+
// ================================================================================
939+
fmt.Println("# A request that is well-formed is passed through to the Handler")
940+
body := map[string]string{
941+
"name": "Jamie",
942+
}
943+
944+
data, err := json.Marshal(body)
945+
must(err)
946+
947+
req, err := http.NewRequest(http.MethodPost, "/public-api/resource", bytes.NewReader(data))
948+
must(err)
949+
req.Header.Set("Content-Type", "application/json")
950+
951+
rr := httptest.NewRecorder()
952+
953+
server.ServeHTTP(rr, req)
954+
955+
fmt.Printf("Received an HTTP %d response. Expected HTTP 204\n", rr.Code)
956+
logResponseBody(rr)
957+
fmt.Println()
958+
959+
// Output:
960+
// # A request that is well-formed is passed through to the Handler
961+
// POST /public-api/resource was called
962+
// Received an HTTP 204 response. Expected HTTP 204
963+
//
964+
}

0 commit comments

Comments
 (0)