From b332bcb780a801664d605975f6cd75711d98e4ea Mon Sep 17 00:00:00 2001 From: Daniel Beck Date: Thu, 21 Jan 2021 16:17:13 +0100 Subject: [PATCH 1/2] @AnyVerb annotation makes web methods explicitly accept any HTTP verb --- .../org/kohsuke/stapler/verb/AnyVerb.java | 26 +++++++++++++++++++ .../stapler/verb/HttpVerbInterceptor.java | 3 ++- 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 core/src/main/java/org/kohsuke/stapler/verb/AnyVerb.java 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..a023c144ce --- /dev/null +++ b/core/src/main/java/org/kohsuke/stapler/verb/AnyVerb.java @@ -0,0 +1,26 @@ +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}. This isn't just a + * no-op in case it would be combined with a more restrictive verb, but + * conceptually identical to not have any annotation, but friendlier for static + * analysis. + * + */ +@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; } } From a29ffd7fc140b276f6e601caeca6fee2d9363f9f Mon Sep 17 00:00:00 2001 From: Daniel Beck Date: Wed, 27 Jan 2021 15:58:37 +0100 Subject: [PATCH 2/2] Add test, clearer Javadoc --- .../org/kohsuke/stapler/verb/AnyVerb.java | 7 ++-- .../org/kohsuke/stapler/DispatcherTest.java | 40 +++++++++++++++++++ 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/org/kohsuke/stapler/verb/AnyVerb.java b/core/src/main/java/org/kohsuke/stapler/verb/AnyVerb.java index a023c144ce..7a0fc2dc7d 100644 --- a/core/src/main/java/org/kohsuke/stapler/verb/AnyVerb.java +++ b/core/src/main/java/org/kohsuke/stapler/verb/AnyVerb.java @@ -12,10 +12,9 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME; /** - * Explicitly allows any HTTP verb for {@link WebMethod}. This isn't just a - * no-op in case it would be combined with a more restrictive verb, but - * conceptually identical to not have any annotation, but friendlier for static - * analysis. + * 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) 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()); + } //===================================================================