I'm writing a RESTful Web-Service but CXF doesn't match the right target method when the URI paths are identical but only #QueryParam parameters are different...
I know that #QueryParam parameters are optional and as it's said in the answer of this post: "both methods are ambiguous [at URI paths level] and seems like the first one wins"...
ex: http://mydomain/property?key=sthA or http://mydomain/property?value=sthB, CXF will match the same method unfortunately.
#Path("/property")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public interface PropertyService {
#GET
#Path("/{id}")
AppSetting findPropertyById(#PathParam("id") String id); /* /property/123 */
#GET
Property findPropertyByKey(#QueryParam("key") String key); /* /property?key=sth */
#GET
List<Property> getPropertiesByValue(#QueryParam("value") String value); /* /property?value=sth */
}
So, what is the best solution between:
having one method that will handle any possible #QueryParam parameters for a specific URI path (here "/property?[key=sthA][&value=sthB][...]")
or set some RegExp directly in the value of the #Path annotation to "help" CXF to match URI path with the right method (here: "/property?key=sthA" should match the method with "#Path({key:.*})" annotation)
or any other possible solution
?
Thks
Related
I'm facing the following issue and have been able to find a proper fix.
As a use case example, let's imagine a Rest client that fetches a json object from a server, where the request is the path to the object. This api does not accept query parameters nor bodies, and no catalogue is available.
say that I have the following RestClient:
#Path("/json")
#RegisterRestClient(configKey="json-api")
public interface JsonService {
#GET
#Path("/{MyVariableLengthEndpoint}")
Response getJson(#PathParam("MyVariableLengthEndpoint") ????? endpoint);
}
Examples of requests could be :
/json/employees/Dwight/jobs/assistantRegionalManager/salary
/json/games/theLastOfUs/rating
Passing a string with / characters gets encoded with %. To bypass this, I've tried:
Using the #Encoded annotation
Adding a regex in the pathParameter {MyVariableLengthEndpoint: .*}
Passing a List<PathSegment>
None of those worked.
Is there a proper way to do this ?
You should use Regex in your #Path definition, something like this:
#GET
#Path(“/{varPath : .+}”)
Response getJson(#PathParam(“varPath”) String endpoint);
This will match anything that comes after /json.
For more info search: “JAXRS path Regex”, on Google.
I'm using a javascript library called tabulator to display data inside tables on the client side.
The Tabulator js library provides a feature to encode a representation of filters in the query parameters of an ajax request. For example, here's what the query params look like:
https://host/myEndpoint?size=10&page=1&filters%5B0%5D%5Bfield%5D=username&filters%5B0%5D%5Btype%5D=like&filters%5B0%5D%5Bvalue%5D=filteredBy
Here's the same url decoded:
https://host/myEndpoint?size=10&page=1&filters[0][field]=username&filters[0][type]=like&filters[0][value]=filteredBy
If possible, I'd like to have a Resteasy endpoint like this:
#GET
#Path("/myEndpoint")
#Consumes("application/json")
#Produces("application/json")
public Response myEndpoint(#QueryParam("page") Integer page,
#QueryParam("size") Integer size,
#QueryParam("filters") List<Filter> filters) {
resteasy interprets page and size no problem, but filters is always a list of size 0.
My Filter bean has 3 fields named field, type, and value with a constructor with single String argument as described here.
But it doesn't seem like resteasy is recognizing and parsing the filters query param? Is it possible to parse this type of nested array structure query parameters in resteasy?
filters[0][field]=username&filters[0][type]=like&filters[0][value]=filteredB
I'm still hoping that there's a better way, but here's a possible solution that works for me for now at least:
#GET
#Path("/myEndpoint")
#Consumes("application/json")
#Produces("application/json")
public Response myEndpoint(#QueryParam("page") Integer page,
#QueryParam("size") Integer size,
#Context UriInfo uriInfo) {
for(String key : uriInfo.getQueryParameters().keySet()) {
// check if key starts with something like `filters[0]`
// and then parse it however you need.
}
}
I'm trying to create a GET request, where I have two different requests. Since both resources are related with each other, I'm trying to put them under one 'sub-resource'.
The first one is:
#QueryParam("{customerId}")
public List<customerModel> getCustomer(#QueryParam("customerId") Long customerId) {....}
this fetches the customer's name depending on the customerId
#QueryParam("{customerId}/details")
public List<customerDetailModel> getCustomerDetail(#QueryParam("customerId") Long customerId) {....}
this fetches the detailed information of the customer (phone number, address, etc.)
I'm running the first one with the following (works fine) :
......?customerId=7453112
but I can't reach the second request when I'm hitting the following URL:
......?customerId=7453112/details
Any suggestions?
Resource methods must be annotated with #Path and with a request method designator such as #GET, #POST, #PUT,
#DELETE, #HEAD or #OPTIONS. Your #QueryParam annotation is misplaced.
By the way, for this situation, you should consider using a path parameter instead of a query parameter. Hence you should use #PathParam instead of #QueryParam in the method parameter. For more details on when to use query or path parameters, have a look at this answer
Also, not sure why you are returning a List if you are requesting a resource by its unique identifier. In this situation, you are supposed to return a representation of a single resource (and not a represention of multiple resources).
So your resource class would be as following:
#Path("customers")
#Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public class CustomerResource {
#GET
#Path("{customerId}")
public CustomerModel getCustomer(#PathParam("customerId") Long id) {
...
}
#GET
#Path("{customerId}/details")
public CustomerDetailModel getCustomerDetail(#PathParam("customerId") Long id) {
...
}
}
The first endpoint can be requested as following:
GET http://example.com/api/customers/1
And the second endpoint can be requested as following:
GET http://example.com/api/customers/1/details
You need to go with #RequestMapping.
Something like:
#RequestMapping("/{customerId}") and #RequestMapping("/{customerId}/details").
Your URL becomes 7453112/details instead of query params.
You can specify the /details in an #Path annotation, and then use the same query parameter, like this:
#Path("/details/{customerId}")
public List<customerDetailModel> getCustomerDetail(#PathParam("customerId") Long customerId) {....}
Then your URL would look like this:
.../details/7453112
Or if you want to keep using it as a query parameter you can do something like this:
#Path("/details")
public List<customerDetailModel> getCustomerDetail(#QueryParam("customerId") Long customerId) {....}
using this url:
.../details?customerId=xxx
I am using rest-easy and I want my #Path annotation to obtain it's value from a variable (mayabe a system parameter).
Eg:
#Path(someVar)
#GET
#Produces(MediaType.TEXT_XML)
public String retrieve() {
}
I tried reading and got to know that Path must be a constant value.
Is something like above possible in rest easy ??
Annotations are processed on compile time. You cannot use a variable as an annotation parameter.
You can pass variable value in URL, something like below example
so your URL will be /book/some_isbn_code and the getBook method will receive isbn value in id field.
#Path("/book/{isbn}")
public String getBook(#PathParam("isbn") String id) {
I've started working with RestEasy and I've come across a problem that I can't seem to find an answer to. If I have 2 methods that both resolve to the same path (in this case /path1/path2/path3), but they both have a different number of query parameters, will RestEasy be able to determine which method to use?
#GET
#NoCache
#Produces({
MediaType.APPLICATION_JSON
})
#Path("/path1/path2/{path3}")
public String getResults1(
#PathParam("path3") String path3,
#QueryParam("query1") #DefaultValue("") String query1,
#QueryParam("query2") String query2,
#QueryParam("query3") #DefaultValue("25") int query3) {
...
}
#GET
#NoCache
#Produces({
MediaType.APPLICATION_JSON
})
#Path("/path1/path2/{path3}")
public String getResults2(
#PathParam("path3") String path3,
#QueryParam("query1") #DefaultValue("") String query1,
#QueryParam("query2") #DefaultValue("5") Integer query2) {
...
}
I've done some testing and yesterday it seemed that everything was working perfectly and that it could choose the right path, but now today I'm starting to see it take the wrong path each time.
Is this something that should be handled, or should I just suck it up and put it in 1 method and do the check myself?
No, you should be handling this in the method. If conflicting resources are found it is implementation independent which method will be matched.
Take a look at your example again:
If you submitted query1 and query2 how would it know if you wanted the method with 2 query parameters or the method with 3 query parameters and you wanted it to default the 3rd to it's default value?