davsclaus commented on code in PR #26203:
URL: https://github.com/apache/camel/pull/26203#discussion_r4112510909


##########
components/camel-rest-openapi/src/main/docs/rest-openapi-component.adoc:
##########
@@ -202,6 +202,126 @@ If any of the validation checks fail, then a 
`RestOpenApiValidationException` is
 has a `getValidationErrors` method that returns the error messages from the 
validator.
 
 
+== Unmatched requests
+
+By default, an incoming request that does not match any operation in the 
OpenAPI specification is answered by the
+HTTP layer of the runtime, with HTTP 404, and a request that matches a path 
but not the HTTP method is answered
+with HTTP 405 and an `Allow` header listing the allowed methods.
+
+A request that matches an operation but whose `Content-Type` or `Accept` 
header does not match the `consumes` or
+`produces` of the operation is answered by the HTTP layer, with HTTP 415 or 
406 (see the `serverRequestValidation`
+option of the platform-http component, enabled by default). When 
`unmatchedRequestHandling` is set to `camel`,
+some runtimes also route these requests to Camel, so the unmatched request 
handler answers them with the same

Review Comment:
   This (correctly) describes the `camel`-mode-only behaviour, but the code 
currently applies it in every mode — see the comment on `RestOpenApiProcessor`. 
Code and docs should agree once that's settled.



##########
components/camel-rest-openapi/src/main/docs/rest-openapi-component.adoc:
##########
@@ -202,6 +202,126 @@ If any of the validation checks fail, then a 
`RestOpenApiValidationException` is
 has a `getValidationErrors` method that returns the error messages from the 
validator.
 
 
+== Unmatched requests
+
+By default, an incoming request that does not match any operation in the 
OpenAPI specification is answered by the
+HTTP layer of the runtime, with HTTP 404, and a request that matches a path 
but not the HTTP method is answered
+with HTTP 405 and an `Allow` header listing the allowed methods.
+
+A request that matches an operation but whose `Content-Type` or `Accept` 
header does not match the `consumes` or
+`produces` of the operation is answered by the HTTP layer, with HTTP 415 or 
406 (see the `serverRequestValidation`
+option of the platform-http component, enabled by default). When 
`unmatchedRequestHandling` is set to `camel`,
+some runtimes also route these requests to Camel, so the unmatched request 
handler answers them with the same
+415 or 406 status code.
+
+To let Camel answer these requests instead, set the `unmatchedRequestHandling` 
option to `camel` on the
+rest-openapi consumer endpoint or via the rest DSL `openApi` section. The 
rest-openapi component then registers a
+catch-all for the API base path on the HTTP layer so requests that match no 
operation are routed to Camel, where
+they are answered by the unmatched request handler.
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("rest-openapi:petstore-v3.json?missingOperation=ignore&unmatchedRequestHandling=camel")
+    .to("direct:businessLogic");
+
+// ... or using the rest DSL
+
+rest().openApi()
+    .specification("petstore-v3.json")
+    .missingOperation("ignore")
+    .unmatchedRequestHandling("camel");
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+    from:
+      uri: rest-openapi:petstore-v3.json
+      parameters:
+        missingOperation: ignore
+        unmatchedRequestHandling: camel
+      steps:
+        - to:
+            uri: direct:businessLogic
+
+# ... or using the rest DSL
+
+- rest:
+    openApi:
+      specification: petstore-v3.json
+      missingOperation: ignore
+      unmatchedRequestHandling: camel
+----
+====
+
+The option is supported by the built-in `platform-http` consumer component: 
Camel Main when using
+xref:platform-http-component.adoc[Platform HTTP], and Spring Boot when using 
the platform-http starter
+(`camel-platform-http-starter`). Other consumer components are not tested and 
can decide to handle, ignore or
+reject the parameter.
+
+On Camel Main and Quarkus, the catch-all route is evaluated last on the HTTP 
server, so it never shadows the
+operation routes of other APIs served by the same server, even when their base 
paths are nested under this API.
+
+[NOTE]
+====
+With nested base paths (for example `/api` and `/api/v3`), the order of the 
catch-all follows the order in
+which the consumers start: `PUT /api/v3/pet/123` may get a 404 from `/api` 
instead of 405 with an `Allow`
+header from `/api/v3`. To work around this, avoid nesting the base paths of 
several APIs in `camel`
+mode, or inspect the request in a custom `RestOpenApiUnmatchedRequestHandler` 
and determine the proper response.
+====
+
+On Spring Boot, the catch-all is served by a Spring MVC handler mapping that 
is evaluated before the
+application's own controller mappings and the static resources. With a base 
path of `/`, in `camel` mode
+every request unmatched by the API will be answered by the unmatched request 
handler. Controllers and static 
+resources will be shadowed. When the application also serves MVC contet it is 
prefered to use a base path other 
+than `/` (for example by setting `servers` in the OpenAPI specification or 
`contextPath` in the rest
+configuration), so the catch-all only claims requests under that path.
+
+If the API and Spring MVC controllers must keep responding to their own paths 
at a base path of `/`, you can 
+set register your own `RequestMappingHandlerMapping` bean with a order set to 
`-50`. This will still not work for 
+serving static resources.

Review Comment:
   A few typos and trailing spaces here (please regenerate the catalog copy 
after):
   
   ```suggestion
   every request unmatched by the API is answered by the unmatched request 
handler, so controllers and static
   resources are shadowed. When the application also serves MVC content, prefer 
a base path other
   than `/` (for example by setting `servers` in the OpenAPI specification or 
`contextPath` in the rest
   configuration), so the catch-all only claims requests under that path.
   
   If the API and Spring MVC controllers must keep responding to their own 
paths at a base path of `/`, you can
   register your own `RequestMappingHandlerMapping` bean with its order set to 
`-50`. This still does not work for
   serving static resources.
   ```



##########
components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiProcessor.java:
##########
@@ -100,6 +114,22 @@ public boolean process(Exchange exchange, AsyncCallback 
callback) {
         RestConsumerContextPathMatcher.ConsumerPath<Operation> m
                 = RestConsumerContextPathMatcher.matchBestPath(verb, path, 
paths);
         if (m instanceof RestOpenApiConsumerPath rcp) {
+            // when server request validation is enabled, the HTTP layer 
rejects requests whose Content-Type
+            // or Accept header does not match the consumes/produces of the 
operation with 415/406. However,
+            // depending on the runtime, such requests may still be routed to 
Camel (via an unconstrained
+            // catch-all route or a matchOnUriPrefix endpoint), so the 
rejected requests must be answered
+            // here instead of being processed as if they were valid
+            if (serverRequestValidation) {

Review Comment:
   This is gated only on `serverRequestValidation`, which defaults to `true` on 
platform-http, so it also runs in the default `platform` mode (your unit tests 
use `createProcessor(..., null)`, i.e. default mode, and get 415/406). That's a 
behaviour change for existing users and contradicts the docs, which say this 
only happens in `camel` mode.
   
   It also double-checks requests Vert.x already negotiated, using a cruder 
matcher: `RestUtil.isValidOrAcceptedContentType` does 
`StringHelper.before(target, ";")` before splitting on `,`, so `Accept: 
application/xml;q=0.9, application/json` against `produces=application/json` 
becomes `application/xml` → 406, and `application/*` never matches. Could this 
be `if (serverRequestValidation && unmatchedRequestCatchAllRegistered)`, with 
Accept params stripped per comma-separated part (and a test for a q-valued 
Accept)?



##########
core/camel-api/src/main/java/org/apache/camel/spi/RestOpenApiUnmatchedRequestHandler.java:
##########
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.spi;
+
+import java.util.List;
+
+import org.apache.camel.Exchange;
+
+/**
+ * Used for customizing the HTTP responses for incoming requests that Camel 
must not process. Exemplary these are
+ * requests that do not match any operation defined in the OpenAPI 
specification (404/405) or, when server request
+ * validation is enabled, requests whose Content-Type/Accept header does not 
match the consumes/produces of the matched
+ * operation (415/406).
+ * <p>
+ * This allows to plugin different handlers to produce custom error response 
bodies.
+ *
+ * @since 4.23
+ */
+public interface RestOpenApiUnmatchedRequestHandler {

Review Comment:
   Thanks for moving this. Since the reason for putting it in camel-api is 
reuse by `camel-rest-postman`, should the name drop the OpenAPI part (e.g. 
`RestUnmatchedRequestHandler`, with the factory key to match)? Once 4.23 is 
released the name is frozen. Minor nit on line 24: "Exemplary these are" → "For 
example, these are".



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to