diff --git a/core/src/main/java/org/kohsuke/stapler/verb/AnyVerb.java b/core/src/main/java/org/kohsuke/stapler/verb/AnyVerb.java new file mode 100644 index 0000000000..7a0fc2dc7d --- /dev/null +++ b/core/src/main/java/org/kohsuke/stapler/verb/AnyVerb.java @@ -0,0 +1,25 @@ +package org.kohsuke.stapler.verb; + +import org.kohsuke.stapler.WebMethod; +import org.kohsuke.stapler.interceptor.InterceptorAnnotation; +import org.kohsuke.stapler.interceptor.Stage; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Explicitly allows any HTTP verb for {@link WebMethod}. + * While conceptually similar to not have any annotation, it's friendlier for static analysis. + * This isn't a no-op only in case it would be combined with a more restrictive verb annotation, as it will continue to allow any verb. + * + */ +@Target(METHOD) +@Retention(RUNTIME) +@Documented +@InterceptorAnnotation(value = HttpVerbInterceptor.class, stage = Stage.SELECTION) +public @interface AnyVerb { +} diff --git a/core/src/main/java/org/kohsuke/stapler/verb/HttpVerbInterceptor.java b/core/src/main/java/org/kohsuke/stapler/verb/HttpVerbInterceptor.java index 2a1e7aaed4..278c1f8076 100644 --- a/core/src/main/java/org/kohsuke/stapler/verb/HttpVerbInterceptor.java +++ b/core/src/main/java/org/kohsuke/stapler/verb/HttpVerbInterceptor.java @@ -40,6 +40,7 @@ * @see POST * @see PUT * @see DELETE + * @see AnyVerb */ public class HttpVerbInterceptor extends Interceptor { @Override @@ -57,7 +58,7 @@ private boolean matches(StaplerRequest request) { Class t = a.annotationType(); InterceptorAnnotation ia = t.getAnnotation(InterceptorAnnotation.class); if (ia != null && ia.value() == HttpVerbInterceptor.class) { - if (t.getSimpleName().equals(method)) { + if (t.getSimpleName().equals(method) || t == AnyVerb.class) { return true; } } diff --git a/core/src/test/java/org/kohsuke/stapler/DispatcherTest.java b/core/src/test/java/org/kohsuke/stapler/DispatcherTest.java index b87dd44c48..5ad2f6594e 100644 --- a/core/src/test/java/org/kohsuke/stapler/DispatcherTest.java +++ b/core/src/test/java/org/kohsuke/stapler/DispatcherTest.java @@ -10,6 +10,8 @@ import org.kohsuke.stapler.interceptor.RequirePOST; import org.kohsuke.stapler.json.JsonBody; import org.kohsuke.stapler.test.JettyTestCase; +import org.kohsuke.stapler.verb.AnyVerb; +import org.kohsuke.stapler.verb.DELETE; import org.kohsuke.stapler.verb.GET; import org.kohsuke.stapler.verb.POST; import org.kohsuke.stapler.verb.PUT; @@ -87,6 +89,44 @@ private void check(WebClient wc, HttpMethod m) throws java.io.IOException { assertEquals("Got " + m.name(), p.getContent()); } + //=================================================================== + + public final AnyVerbMatch anyVerbMatch = new AnyVerbMatch(); + + public class AnyVerbMatch { + @WebMethod(name = "") + @GET + public HttpResponse get() { + return HttpResponses.text("get()"); + } + + @WebMethod(name = "") + @AnyVerb + @DELETE + public HttpResponse routeAny(StaplerRequest req) { // name sorts after 'get' + return HttpResponses.text("Got " + req.getMethod()); + } + + @WebMethod(name = "") + @POST + public HttpResponse zzzz(StaplerRequest req) { // name sorts last, unused + return HttpResponses.text("never"); + } + + } + + public void testAnyVerbMatch() throws Exception { + WebClient wc = new WebClient(); + + checkText(wc, HttpMethod.GET, "get()"); + checkText(wc, HttpMethod.POST, "Got POST"); + checkText(wc, HttpMethod.DELETE, "Got DELETE"); + } + + private void checkText(WebClient wc, HttpMethod m, String text) throws java.io.IOException { + TextPage p = wc.getPage(new WebRequestSettings(new URL(url, "anyVerbMatch/"), m)); + assertEquals(text, p.getContent()); + } //===================================================================