From a79e2f18c24b36271978138403bec4181aac7d67 Mon Sep 17 00:00:00 2001 From: Lukasz Lenart Date: Sun, 8 Nov 2026 15:34:26 +0100 Subject: [PATCH] WW-5844 docs: describe httpMethod enforcement in the plugin stacks and annotation resolution restDefaultStack and beanValidationDefaultStack now contain the httpMethod interceptor, so the action-configuration page lists them with the core stacks and both plugin pages carry a "Since 7.5.0" paragraph. The HTTP Methods section gains the resolution rules for method-level and class-level annotations across the class and interface hierarchy, and the REST page explains that the interceptor checks the request method itself, so update() and destroy() reached from a POST form with _method need @HttpPost as well. Co-Authored-By: Claude Mythos 5.1 --- .../core-developers/action-configuration.md | 21 +++++++++-- source/plugins/bean-validation/index.md | 5 +++ source/plugins/rest/index.md | 35 +++++++++++++++++++ 3 files changed, 59 insertions(+), 2 deletions(-) diff --git a/source/core-developers/action-configuration.md b/source/core-developers/action-configuration.md index 9ae376b4a..f2a150dc5 100644 --- a/source/core-developers/action-configuration.md +++ b/source/core-developers/action-configuration.md @@ -368,13 +368,30 @@ The attribute requires the 7.5 DTD. An unknown method name, e.g. `http-method="f Actions matched by a wildcard or regex pattern keep the restriction declared on the pattern. The restriction is enforced by the `httpMethod` interceptor, which is part of `basicStack`, `defaultStack`, -`paramsPrepareParamsStack` and the stacks built on them. If an action declares `http-method` but its interceptors do -not include `httpMethod`, a warning is logged at startup and the restriction is not enforced. +`paramsPrepareParamsStack` and the stacks built on them, as well as the [REST Plugin](../plugins/rest)'s +`restDefaultStack` and the [Bean Validation Plugin](../plugins/bean-validation)'s `beanValidationDefaultStack`. +If an action declares `http-method` but its interceptors do not include `httpMethod`, a warning is logged at startup +and the restriction is not enforced. The same interceptor enforces the `@AllowedHttpMethod`, `@HttpGet`, `@HttpPost`, `@HttpPut`, `@HttpDelete` and `@HttpGetOrPost` annotations on the action class or method. When both the attribute and an annotation apply, the request method must be allowed by both. +Annotations are resolved as follows: + +- A method-level annotation applies to the action method the request invokes, including the implicit `execute()` + when the mapping names no method. +- An annotation on the invoked method itself takes precedence. Otherwise the interceptor looks for the same method + declared on a superclass or on an implemented interface, including interfaces those extend, and uses the first + annotated declaration it finds. Only public instance methods count, so a private, static or package-private + method of the same name on a parent class carries no restriction. +- Class-level annotations are consulted only when no method-level annotation applies. They are looked up on the + action class, its superclasses and their interfaces in the same way. + +The interceptor compares the HTTP method of the request as the container reports it. The `_method` parameter the +REST plugin uses to route a POST form to `update` or `destroy` does not change it, see +[HTTP method restrictions in restDefaultStack](../plugins/rest#http-method-restrictions-in-restdefaultstack). + ### Rejected requests When the request method is not allowed, the interceptor returns the `bad-request` result if it can be resolved: diff --git a/source/plugins/bean-validation/index.md b/source/plugins/bean-validation/index.md index 872687a9b..669f04439 100644 --- a/source/plugins/bean-validation/index.md +++ b/source/plugins/bean-validation/index.md @@ -69,6 +69,11 @@ returns the result code `403`, which needs a matching (global) result to render `fetchMetadata.disabled` to `true` on the stack for actions that must accept such requests. {:.alert .alert-warning} +Since Struts 7.5.0 `beanValidationDefaultStack` also contains the `httpMethod` interceptor, placed after +`servletConfig` as in core's `defaultStack` ([WW-5844](https://issues.apache.org/jira/browse/WW-5844)). The +`http-method` attribute and the `@HttpGet`, `@HttpPost` and related annotations are enforced for every package +extending `struts-bean-validation`, see [HTTP Methods](/core-developers/action-configuration#http-methods). + Here is another example that shows how you can combine bean-validation with other plugins by configuring your own Interceptor-Stack (note: this is just a very short example. In a real app you should take more care about your stack). You can combine bean validation with classic struts validation (or disable either) by putting the according interceptors diff --git a/source/plugins/rest/index.md b/source/plugins/rest/index.md index 4da7eb1a0..741cb93c1 100644 --- a/source/plugins/rest/index.md +++ b/source/plugins/rest/index.md @@ -82,6 +82,8 @@ The following URL's will invoke its methods: - `DELETE: /movies/Thrillers` => method=**destroy**, id=**Thrillers** To simulate the HTTP methods PUT and DELETE, since they aren't supported by HTML, the HTTP parameter `_method` will be used. +It changes only which method the mapper selects; the request is still a POST for the `httpMethod` interceptor, see +[HTTP method restrictions in restDefaultStack](#http-method-restrictions-in-restdefaultstack). Or, expressed as a table: @@ -394,6 +396,39 @@ check for the stack serving it: Requests sent by non-browser clients carry no `Sec-Fetch-Site` header and are not affected. +### HTTP method restrictions in restDefaultStack + +Since Struts 7.5.0 `restDefaultStack` contains the `httpMethod` interceptor, placed after `servletConfig` as in core's +`defaultStack` ([WW-5844](https://issues.apache.org/jira/browse/WW-5844)). The `http-method` attribute and the +`@AllowedHttpMethod`, `@HttpGet`, `@HttpPost`, `@HttpPut`, `@HttpDelete` and `@HttpGetOrPost` annotations are enforced +for every package extending `rest-default`, see [HTTP Methods](/core-developers/action-configuration#http-methods). + +The interceptor checks the HTTP method of the request itself, not the `_method` parameter. A POST form carrying +`_method=put` or `_method=delete` is routed to `update` or `destroy`, but the request stays a POST, so those methods +must allow POST. `@HttpPut` and `@HttpDelete` alone accept only real PUT and DELETE requests, as sent by `fetch()`/XHR +or non-browser clients; combine them with `@HttpPost` when the same method also serves HTML forms: +{:.alert .alert-warning} + +```java +public class OrdersController implements ModelDriven { + + @HttpPost + @HttpPut + public HttpHeaders update() { + ... + } + + @HttpPost + @HttpDelete + public HttpHeaders destroy() { + ... + } +} +``` + +An XML `http-method` restriction on such an action must likewise include `post`. A request whose method is not +allowed receives `405 Method Not Allowed` with an `Allow` header, or the `bad-request` result when one is mapped. + ## Resources + [http://www.b-simple.de/documents](http://www.b-simple.de/documents) - Short RESTful Rails tutorial (PDF, multiple languages)