Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions source/core-developers/action-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
5 changes: 5 additions & 0 deletions source/plugins/bean-validation/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
35 changes: 35 additions & 0 deletions source/plugins/rest/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down Expand Up @@ -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<Order> {

@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)
Expand Down