Skip to content

Commit c843e47

Browse files
authored
Merge pull request #46918 from Postremus/issues/23990-locator-path-params
Return locators path parameters in UriInfo
2 parents f31843d + de0a9bb commit c843e47

File tree

3 files changed

+166
-9
lines changed

3 files changed

+166
-9
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
package io.quarkus.resteasy.reactive.server.test.resource.basic;
2+
3+
import static org.hamcrest.Matchers.equalTo;
4+
5+
import java.util.function.Supplier;
6+
7+
import jakarta.enterprise.context.RequestScoped;
8+
import jakarta.inject.Inject;
9+
import jakarta.ws.rs.GET;
10+
import jakarta.ws.rs.Path;
11+
import jakarta.ws.rs.container.ResourceContext;
12+
import jakarta.ws.rs.core.UriInfo;
13+
14+
import org.jboss.resteasy.reactive.RestPath;
15+
import org.jboss.shrinkwrap.api.ShrinkWrap;
16+
import org.jboss.shrinkwrap.api.spec.JavaArchive;
17+
import org.junit.jupiter.api.Test;
18+
import org.junit.jupiter.api.extension.RegisterExtension;
19+
20+
import io.quarkus.resteasy.reactive.server.test.simple.PortProviderUtil;
21+
import io.quarkus.test.QuarkusUnitTest;
22+
import io.restassured.RestAssured;
23+
24+
public class SubResourceUriInfoTest {
25+
@RegisterExtension
26+
static QuarkusUnitTest testExtension = new QuarkusUnitTest()
27+
.setArchiveProducer(new Supplier<>() {
28+
@Override
29+
public JavaArchive get() {
30+
JavaArchive war = ShrinkWrap.create(JavaArchive.class);
31+
war.addClasses(PortProviderUtil.class);
32+
war.addClasses(UsersResource.class);
33+
war.addClasses(UserResource.class);
34+
war.addClasses(ContactResource.class);
35+
war.addClasses(ResponseHolder.class);
36+
return war;
37+
}
38+
});
39+
40+
@Test
41+
public void basicTest() {
42+
RestAssured.given()
43+
.get("/users/userId/contacts/contactId")
44+
.then()
45+
.statusCode(200)
46+
.body(equalTo("{id=[userId]}{id=[contactId, userId]}{id=[contactId, userId]}"));
47+
}
48+
49+
@RequestScoped
50+
@Path("users")
51+
public static class UsersResource {
52+
53+
@Inject
54+
ResponseHolder responseHolder;
55+
56+
@Inject
57+
UriInfo uriInfo;
58+
59+
@Inject
60+
ResourceContext resourceContext;
61+
62+
@Path("{id}")
63+
public UserResource get(@RestPath String id) {
64+
responseHolder.setResponse(responseHolder.getResponse() + uriInfo.getPathParameters().toString());
65+
return resourceContext.getResource(UserResource.class);
66+
}
67+
}
68+
69+
@RequestScoped
70+
public static class UserResource {
71+
72+
@Inject
73+
ResponseHolder responseHolder;
74+
@Inject
75+
UriInfo uriInfo;
76+
77+
@Inject
78+
ResourceContext resourceContext;
79+
80+
@Path("contacts/{id}")
81+
public ContactResource get(@RestPath String id) {
82+
responseHolder.setResponse(responseHolder.getResponse() + uriInfo.getPathParameters().toString());
83+
return resourceContext.getResource(ContactResource.class);
84+
}
85+
}
86+
87+
@RequestScoped
88+
public static class ContactResource {
89+
90+
@Inject
91+
ResponseHolder responseHolder;
92+
93+
@Inject
94+
UriInfo uriInfo;
95+
96+
@GET
97+
public String getName(@RestPath String id) {
98+
responseHolder.setResponse(responseHolder.getResponse() + uriInfo.getPathParameters().toString());
99+
return responseHolder.getResponse();
100+
}
101+
}
102+
103+
@RequestScoped
104+
public static class ResponseHolder {
105+
String response = "";
106+
107+
public String getResponse() {
108+
return response;
109+
}
110+
111+
public void setResponse(String response) {
112+
this.response = response;
113+
}
114+
}
115+
}

independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ResteasyReactiveRequestContext.java

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import java.util.Deque;
1515
import java.util.LinkedList;
1616
import java.util.List;
17+
import java.util.Map;
1718
import java.util.concurrent.Executor;
1819
import java.util.regex.Matcher;
1920

@@ -29,6 +30,7 @@
2930
import jakarta.ws.rs.core.GenericEntity;
3031
import jakarta.ws.rs.core.HttpHeaders;
3132
import jakarta.ws.rs.core.MediaType;
33+
import jakarta.ws.rs.core.MultivaluedMap;
3234
import jakarta.ws.rs.core.PathSegment;
3335
import jakarta.ws.rs.core.Request;
3436
import jakarta.ws.rs.core.Response;
@@ -45,6 +47,7 @@
4547
import org.jboss.resteasy.reactive.common.util.Encode;
4648
import org.jboss.resteasy.reactive.common.util.PathHelper;
4749
import org.jboss.resteasy.reactive.common.util.PathSegmentImpl;
50+
import org.jboss.resteasy.reactive.common.util.QuarkusMultivaluedHashMap;
4851
import org.jboss.resteasy.reactive.server.SimpleResourceInfo;
4952
import org.jboss.resteasy.reactive.server.core.multipart.FormData;
5053
import org.jboss.resteasy.reactive.server.core.serialization.EntityWriter;
@@ -1130,6 +1133,44 @@ public String getResourceLocatorPathParam(String name, boolean encoded) {
11301133
return getResourceLocatorPathParam(name, (PreviousResource) getProperty(PreviousResource.PROPERTY_KEY), encoded);
11311134
}
11321135

1136+
/**
1137+
* Collects all path parameters, first from the current RuntimeResource, also known as target, and then from the previous
1138+
* RuntimeResources, including path parameters from sub resource locators in the process.
1139+
*
1140+
* @param encoded
1141+
* @return MultivaluedMap with path parameters. May be empty, but is never null
1142+
*/
1143+
public MultivaluedMap<String, String> getAllPathParameters(boolean encoded) {
1144+
MultivaluedMap<String, String> pathParams = new QuarkusMultivaluedHashMap<>();
1145+
// a target can be null if this happens in a filter that runs before the target is set
1146+
if (target == null) {
1147+
return pathParams;
1148+
}
1149+
1150+
PreviousResource previousResource = null;
1151+
Object paramValues = this.pathParamValues;
1152+
do {
1153+
for (Map.Entry<String, Integer> pathParam : target.getPathParameterIndexes().entrySet()) {
1154+
pathParams.add(pathParam.getKey(), doGetPathParam(pathParam.getValue(), paramValues, encoded));
1155+
}
1156+
1157+
if (previousResource != null) {
1158+
previousResource = previousResource.prev;
1159+
} else {
1160+
previousResource = (PreviousResource) getProperty(PreviousResource.PROPERTY_KEY);
1161+
}
1162+
if (previousResource == null) {
1163+
break;
1164+
}
1165+
1166+
target = previousResource.locatorTarget;
1167+
paramValues = previousResource.locatorPathParamValues;
1168+
1169+
} while (true);
1170+
1171+
return pathParams;
1172+
}
1173+
11331174
public FormData getFormData() {
11341175
return formData;
11351176
}

independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/jaxrs/UriInfoImpl.java

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import java.util.Collection;
77
import java.util.Collections;
88
import java.util.List;
9-
import java.util.Map.Entry;
109

1110
import jakarta.ws.rs.core.MultivaluedMap;
1211
import jakarta.ws.rs.core.PathSegment;
@@ -32,7 +31,11 @@ public class UriInfoImpl implements UriInfo {
3231

3332
private final ResteasyReactiveRequestContext currentRequest;
3433
private MultivaluedMap<String, String> queryParams;
34+
35+
// marker for which target the pathParams where created, may be null when getPathParams was never called
36+
private RuntimeResource pathParamsTargetMarker;
3537
private MultivaluedMap<String, String> pathParams;
38+
3639
private URI requestUri;
3740

3841
public UriInfoImpl(ResteasyReactiveRequestContext currentRequest) {
@@ -150,14 +153,12 @@ public MultivaluedMap<String, String> getPathParameters() {
150153
public MultivaluedMap<String, String> getPathParameters(boolean decode) {
151154
if (!decode)
152155
throw encodedNotSupported();
153-
if (pathParams == null) {
154-
pathParams = new QuarkusMultivaluedHashMap<>();
155-
RuntimeResource target = currentRequest.getTarget();
156-
if (target != null) { // a target can be null if this happens in a filter that runs before the target is set
157-
for (Entry<String, Integer> pathParam : target.getPathParameterIndexes().entrySet()) {
158-
pathParams.add(pathParam.getKey(), currentRequest.getPathParam(pathParam.getValue(), false));
159-
}
160-
}
156+
// pathParams have to be recreated when the target changes.
157+
// this happens e.g. when the ResteasyReactiveRequestContext#restart is called for sub resources
158+
// The sub resource, can have additional path params that are not present on the locator
159+
if (pathParams == null && pathParamsTargetMarker == null || pathParamsTargetMarker != currentRequest.getTarget()) {
160+
pathParams = currentRequest.getAllPathParameters(false);
161+
pathParamsTargetMarker = currentRequest.getTarget();
161162
}
162163
return new UnmodifiableMultivaluedMap<>(pathParams);
163164
}

0 commit comments

Comments
 (0)