Overloading HTTP Methods
Through the use of the built-in method GET parameter, you can implement requests beyond the basic REST http method
types.
For example, the URL /sample/foo?method=BAR will cause the following method to be invoked...
@RestOp(method="BAR", read=true)
public void doBar(RestRequest req, RestResponse res) {
// Handle BAR requests
}
To support overloaded methods, the @Rest(allowedMethodParams) setting must be enabled on your servlet.
@Rest(
// Allow &method parameter on BAR requests
allowedMethodParams="BAR"
)
A request can also name its method in a header. The X-Method, X-HTTP-Method-Override, X-HTTP-Method and X-Method-Override headers are all honored, in that order, when
@Rest(allowedMethodHeaders) admits the method they name. They share the one setting, which is empty (off) by default.
@Rest(
// Allow X-Method and the other override headers to name PUT
allowedMethodHeaders="PUT"
)
Override claims must agree. When a channel is enabled (its allow-list admits at least one method), every claim on it counts, whether or not the allow-list admits that claim. More than one distinct method across the method parameter and the override headers, or repeated with different values on one of them, is refused with 400. Identical repeats (PUT and put) are fine. A channel whose allow-list is empty or NONE is ignored.
Overrides on safe requests
A link, a prefetcher or a crawler can send a GET or HEAD without the user asking for it, so an override on a safe request line must not reach an operation that changes state.
On a GET, HEAD, OPTIONS or TRACE request, an override is honored only when it names another safe method, or a custom method whose operation is declared @RestOp(read=true).
Any other override, such as GET /foo?method=DELETE, is refused with 405 Method Not Allowed before the operation, its hooks or its guards run.
The response carries an Allow header listing the methods the path answers, and its message doesn't echo the method that was claimed.
// GET /files/report.txt?method=VIEW reaches this operation.
@RestOp(method="VIEW", path="/files/*", read=true)
public Reader view(@Path("/*") String path) {
...
}
// GET /files/report.txt?method=PURGE is refused with 405. POST /files/report.txt?method=PURGE still reaches it.
@RestOp(method="PURGE", path="/files/*")
public void purge(@Path("/*") String path) {
...
}
Points to know:
- Overrides on
POSTare unchanged.POST /foo?method=PATCHstill works for clients that can't sendPATCH. read=trueis accepted only on custom methods. It fails at startup onPOST,PUT,PATCH,DELETE,CONNECT,method="*",RRPC, or together with@Mutating. An operation declared withmethod="*"is therefore never reached by an override on a safe request.- The allow-lists still apply first. A method that
allowedMethodParamsorallowedMethodHeadersdoesn't admit is ignored, and the request runs as the plainGET. - Every framework
405carries anAllowheader, including the one sent when a path matches only under other methods. - A CSRF boundary still judges any custom-method claim as a write, so a path-based read link (
/view/{file}) is more robust than?method=VIEWbehind a boundary.
Mutating operations and method safety
HTTP defines GET, HEAD, OPTIONS and TRACE as safe methods: clients, caches and prefetchers may issue them freely because they promise not to change state.
The @Mutating annotation lets you declare that a
Java method changes state, so that binding it to a safe method is reported as a startup error instead of going unnoticed.
// Correct: declared mutating and bound to POST.
@Mutating("Arms the pending release")
@RestPost("/{version}/arm")
public ArmResult arm(@Path("version") String version, @Content ArmRequest body) {
...
}
Points to know:
@Mutatingis method-level only (@Target(METHOD)), and is@Inherited.- Its optional
valueis a free-text note on what the operation changes. It is only included in the error message. - The check compares the operation's resolved HTTP method (the same one used for dispatch) against the safe set.
The safe set is available through MethodSafety
.isSafe(String), which is case-insensitive and treatsnulland unrecognized methods as not safe. - An operation declared with
method="*"matchesGETas well, so combining it with@Mutatingis also reported. - If
@RestOphas nomethodand the Java method name does not start withget,put,postordelete, the method defaults toGET. A@Mutatingoperation declared that way therefore fails the check. See Inferred HTTP Methods and Paths. - A violation throws
InternalServerErrorwhen the resource's operation table is assembled: at startup when@Rest(eagerInit)is enabled, otherwise on the first request to the resource, and in either case before any of its operations can be dispatched. - The annotation is purely additive. Operations without it behave exactly as before. The check only finds declared contradictions; it cannot detect a state-changing method that is not annotated.
- Its opposite is
@RestOp(read=true), described above. It applies only to custom methods and cannot declare aPOST,PUT,PATCHorDELETEsafe.