Skip to content
This repository was archived by the owner on Mar 3, 2026. It is now read-only.

Commit 723677b

Browse files
committed
Add support for multiple verbs in mvc methods
1 parent df68570 commit 723677b

2 files changed

Lines changed: 98 additions & 43 deletions

File tree

jooby-core/src/main/java/jooby/internal/mvc/Routes.java

Lines changed: 40 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.lang.annotation.Annotation;
55
import java.lang.reflect.AnnotatedElement;
66
import java.lang.reflect.Method;
7+
import java.lang.reflect.Modifier;
78
import java.util.ArrayList;
89
import java.util.Arrays;
910
import java.util.HashMap;
@@ -12,12 +13,11 @@
1213
import java.util.Optional;
1314
import java.util.Set;
1415
import java.util.function.Function;
15-
import java.util.stream.Collectors;
1616

1717
import jooby.MediaType;
1818
import jooby.Mode;
1919
import jooby.Route;
20-
import jooby.internal.Reflection;
20+
import jooby.Route.Definition;
2121
import jooby.mvc.Consumes;
2222
import jooby.mvc.DELETE;
2323
import jooby.mvc.GET;
@@ -66,45 +66,52 @@ public static List<Route.Definition> routes(final Mode mode, final Class<?> rout
6666

6767
String rootPath = path(routeClass);
6868

69-
return Reflection
70-
.methods(routeClass)
69+
Map<Method, List<Class<?>>> methods = new HashMap<>();
70+
for (Method method : routeClass.getDeclaredMethods()) {
71+
List<Class<?>> annotations = new ArrayList<>();
72+
for (Class annotationType : VERBS) {
73+
Annotation annotation = method.getAnnotation(annotationType);
74+
if (annotation != null) {
75+
annotations.add(annotationType);
76+
}
77+
}
78+
if (annotations.size() > 0) {
79+
if (!Modifier.isPublic(method.getModifiers())) {
80+
throw new IllegalArgumentException("Not a public method: " + method);
81+
}
82+
methods.put(method, annotations);
83+
}
84+
}
85+
86+
List<Definition> definitions = new ArrayList<>();
87+
88+
methods
89+
.keySet()
7190
.stream()
72-
.filter(
73-
m -> {
74-
List<Annotation> annotations = new ArrayList<>();
75-
for (Class annotationType : VERBS) {
76-
Annotation annotation = m.getAnnotation(annotationType);
77-
if (annotation != null) {
78-
annotations.add(annotation);
79-
}
80-
}
81-
if (annotations.size() == 0) {
82-
return false;
83-
}
84-
if (annotations.size() > 1) {
85-
throw new IllegalStateException("A resource method: " + m
86-
+ " should have only one HTTP verb. Found: "
87-
+ annotations);
88-
}
89-
return true;
90-
})
9191
.sorted((m1, m2) -> {
9292
String k1 = key(m1);
9393
String k2 = key(m2);
9494
int l1 = lines.getOrDefault(k1, 0);
9595
int l2 = lines.getOrDefault(k2, 0);
9696
return l1 - l2;
9797
})
98-
.map(
99-
m -> {
100-
String verb = verb(m);
101-
String path = rootPath + "/" + path(m);
102-
return new Route.Definition(verb, path, new MvcRoute(m, provider))
103-
.produces(produces(m))
104-
.consumes(consumes(m))
105-
.name(routeClass.getSimpleName() + "." + m.getName());
106-
})
107-
.collect(Collectors.toList());
98+
.forEach(
99+
method -> {
100+
List<Class<?>> verbs = methods.get(method);
101+
String path = rootPath + "/" + path(method);
102+
String name = routeClass.getSimpleName() + "." + method.getName();
103+
for (Class<?> verb : verbs) {
104+
Definition definition = new Route.Definition(
105+
verb.getSimpleName(), path, new MvcRoute(method, provider))
106+
.produces(produces(method))
107+
.consumes(consumes(method))
108+
.name(name);
109+
110+
definitions.add(definition);
111+
}
112+
});
113+
114+
return definitions;
108115
}
109116

110117
private static String key(final Method method) {
@@ -154,16 +161,6 @@ private static List<MediaType> consumes(final Method method) {
154161
.orElse(ALL));
155162
}
156163

157-
@SuppressWarnings("unchecked")
158-
private static String verb(final Method method) {
159-
for (Class<?> annotation : VERBS) {
160-
if (method.isAnnotationPresent((Class<? extends Annotation>) annotation)) {
161-
return annotation.getSimpleName();
162-
}
163-
}
164-
throw new IllegalStateException("Couldn't find a HTTP annotation");
165-
}
166-
167164
private static String path(final AnnotatedElement owner) {
168165
Path annotation = owner.getAnnotation(Path.class);
169166
if (annotation == null) {
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package jooby;
2+
3+
import static org.junit.Assert.assertEquals;
4+
import jooby.FilterFeature.HttpResponseValidator;
5+
import jooby.mvc.GET;
6+
import jooby.mvc.POST;
7+
import jooby.mvc.Path;
8+
9+
import org.apache.http.HttpResponse;
10+
import org.apache.http.client.fluent.Request;
11+
import org.apache.http.client.utils.URIBuilder;
12+
import org.apache.http.util.EntityUtils;
13+
import org.junit.Test;
14+
15+
public class MvcMethodWithMultipleVerbsFeature extends ServerFeature {
16+
17+
public static class Resource {
18+
@GET
19+
@POST
20+
@Path("/")
21+
public String getOrPost(final jooby.Request req) {
22+
return req.route().verb().toString();
23+
}
24+
}
25+
26+
{
27+
route(Resource.class);
28+
}
29+
30+
@Test
31+
public void get() throws Exception {
32+
assertEquals("GET", execute(GET(uri("/")), (response) -> {
33+
assertEquals(200, response.getStatusLine().getStatusCode());
34+
}));
35+
}
36+
37+
@Test
38+
public void post() throws Exception {
39+
assertEquals("POST", execute(POST(uri("/")), (response) -> {
40+
assertEquals(200, response.getStatusLine().getStatusCode());
41+
}));
42+
}
43+
44+
private static Request GET(final URIBuilder uri) throws Exception {
45+
return Request.Get(uri.build());
46+
}
47+
48+
private static Request POST(final URIBuilder uri) throws Exception {
49+
return Request.Post(uri.build());
50+
}
51+
52+
private static Object execute(final Request request, final HttpResponseValidator validator)
53+
throws Exception {
54+
HttpResponse resp = request.execute().returnResponse();
55+
validator.validate(resp);
56+
return EntityUtils.toString(resp.getEntity());
57+
}
58+
}

0 commit comments

Comments
 (0)