users@jersey.java.net

[Jersey] Re: How Jersey dispatch request based on query parameter rather than path?

From: Jakub Podlesak <jakub.podlesak_at_oracle.com>
Date: Fri, 18 Nov 2011 11:43:58 +0100

Hi Zijian,

It seems your legacy application uses the request query part for content
negotiation.
You do not write anything about your exchanged media types, so i might
be wrong on this.

But still, the following approach could work for you, as you can
introduce a custom media types
for sort of an internal content negotiation. Then you need to also have
a response filter
to map these back to what the external client expects.

The idea is to update the request filter as follows:

1) remove the query part from the request uri (not 100 % necessary)
2) change the accept header to contain the required media type (external
or internal one)

Then you will have the following resource methods:

@Path({image: ..*})
@Produces("image/*")
@GET
public Image getImage(@PathParam("image" String image) {
...
}

@Path({image: ..*})
@Produces("text/plain") // or @Produces("custom/location-type"), then
another filter is needed for responses
@GET
public Location getImage(@PathParam("image" String image) {
...
}


~Jakub


On 17.11.2011 11:31, 王子剑 wrote:
> Hi Martin,
>
> Thanks for your kind reply, The question of Stackoverflow was posted
> by me, : )
>
> These REST-API use question mark as request dispatch, because the path
> parameter contains multi-slash.
>
> Example:
>
> @Path("{image : ..*}") // match multi-slash
> public People getImage(@PathParam String image);
>
> @Path("{image : ..*}?location")
> public People geImage(@PathParam String image);
>
> The path parameter image could be 'books/english/19830001/abc.jpg', it
> contains multi-slash;
>
> API
> GET http://test.com/storage/books/math/008/xyz.jpg get image of
> 'books/math/008/xyz.jpg' ;
> GET http://test.com/storage/books/english/19830001/abc.jpg get image
> of 'books/english/19830001/abc.jpg';
> GET http://test.com/storage/books/english/19830001/abc.jpg?location
> get image location of 'books/english/19830001/abc.jpg';
>
> So the QueryParamToPathSegmentFilter will impact the path parameter.
>
> Regards.
>
> Zijian.
>
> 在 2011年11月17日 上午1:00,Martin Matula <martin.matula_at_oracle.com
> <mailto:martin.matula_at_oracle.com>>写 道:
>
> See
> http://stackoverflow.com/questions/8118969/jersey-path-cannot-process-question-mark
>
> Seems you are using question mark where slash is more appropriate
> - perhaps for backward compatibility reasons.
> Other option to work around this is to write your resource paths
> like this:
>
>
> @Path("{people}")
> public People getPeople(@PathParam String people);
>
> @Path("{people}/location")
> public Location getLocation(@PathParam String people);
>
> @Path("{people}/friends")
> public List<People> getFriends(@PathParam String people);
>
>
> And then have a request filter that replaces the question marks in
> the URI by slashes - Jersey will then dispatch it properly and for
> future clients you will have a clean API (as the one you have now
> - utilizing query parameters where URI path segments are more
> appropriate is not clean).
>
> The filter can be as simple as this:
>
> public class QueryParamToPathSegmentFilter implements
> ContainerRequestFilter {
>
>
> @Override
> public ContainerRequest filter(ContainerRequest request) {
> String requestUri = request.getRequestUri().toString();
> requestUri = requestUri.replace('?', '/');
> request.setUris(request.getBaseUri(),
> UriBuilder.fromUri(requestUri).build());
> return request;
> }
>
> }
>
> Depending on how your URI's look like - you can make it more
> sophisticated.
> Here is more on how you can register filters in your application:
> http://jersey.java.net/nonav/apidocs/latest/jersey/com/sun/jersey/api/container/filter/package-summary.html
> Regards,
> Martin
>
>
>
>
>
> On 16.11.2011 15:20, 王子剑 wrote:
>> Hi Pavel
>>
>> Actually the return types are totally different, example:
>>
>> @Path("{people}")
>> public People getPeople(@PathParam String people);
>>
>> @Path("{people}?location")
>> public Location getLocation(@PathParam String people);
>>
>> @Path("{people}?friends")
>> public List<People> getFriends(@PathParam String people);
>>
>> ......
>>
>> Jersey dispatch incoming request base on path rather on query
>> parameters, but we can encode the expected query parameter to the
>> path to implement it transparently.
>>
>> I write a ContainerRequestFilter to do this,
>>
>> public class SubResourceMatchableRequestFilter implements
>> ContainerRequestFilter {
>>
>> @Override
>> public ContainerRequest filter(ContainerRequest request) {
>> final String subResource = getFirstSubResource(request);
>> if (subResource == null) {
>> return request;
>> }
>> return new AdaptingContainerRequest(request) {
>> @Override
>> public String getPath(boolean decode) {
>> return new StringBuilder(super.getPath(decode))
>> .append(decode ? "?" : "%3F")
>> .append(subResource)
>> .toString();
>> }
>> };
>> }
>>
>> private String getFirstSubResource(ContainerRequest request) {
>> String query = request.getRequestUri().getQuery();
>> if (query == null) {
>> return null;
>> }
>> int indexOfFirstAnd = query.indexOf("&");
>> return indexOfFirstAnd == -1 ? query : query.substring(0,
>> indexOfFirstAnd);
>> }
>> }
>>
>> The requirement can be archived, but I don`t know is there some
>> hidden trouble?
>>
>> Looking forward to your reply, thanks.
>>
>> Regards,
>> Zijian.
>>
>> 在 2011年11月16日 下午6:15,Pavel Bucek <pavel.bucek_at_oracle.com
>> <mailto:pavel.bucek_at_oracle.com>>写 道:
>>
>> Yeah, you are right.
>>
>> It should be achievable by using reqexes, but I've tested this:
>>
>> @GET
>> @Path("{people}")
>> public String getPeople(@PathParam("people") String people,
>> @QueryParam("location") String location) {
>> if(location == null) {
>> return location;
>> } else {
>> return people;
>> }
>> }
>>
>> and looks like it does what you need. location param here can
>> be null
>> ("?location" not present) or "" (empty string), thus you are
>> able to
>> decide what to return (you are using same return data type
>> for both
>> methods in you example so that shouldn't be a problem..
>>
>> Let me know if this works for you.
>>
>> Regards,
>> Pavel
>>
>>
>> On 11/16/11 2:39 AM, 王子剑 wrote:
>> > Hi Pavel
>> >
>> > Thank you for your kind reply.
>> >
>> > The code:
>> webResource.path("test?location").get(String.class); will
>> > encode character *?* to *%3F*(url encode), that means the
>> request url
>> > will be *http://test.com/test%3Flocation* instead of
>> > *http://test.com/test?location*.
>> >
>> > If someone use unencoded url like
>> *http://test.com/test?location*,
>> > Jersey will return 405 'MethodNotAllowed' response.
>> >
>> > Best regards.
>> >
>> > Zijian
>> >
>> > 2011/11/15 Pavel Bucek <pavel.bucek_at_oracle.com
>> <mailto:pavel.bucek_at_oracle.com>
>> > <mailto:pavel.bucek_at_oracle.com
>> <mailto:pavel.bucek_at_oracle.com>>>
>> >
>> > Hi 王子剑,
>> >
>> > please use users_at_jersey.java.net
>> <mailto:users_at_jersey.java.net> <mailto:users_at_jersey.java.net
>> <mailto:users_at_jersey.java.net>>
>> > mailing list, this one is for mailing list administration only.
>> >
>> > And about your issue - it can be achieved, you need to exclude
>> > requests containing question mark from first method. See
>> following:
>> >
>> > @GET
>> > @Path("{people:[^\\?]*}")
>> > public String getPeople(@PathParam("people") String people) {
>> > return people;
>> >
>> > }
>> >
>> > @GET
>> > @Path("{people}?location")
>> > public String getLocation(@PathParam("people") String people) {
>> > return "location:" + people;
>> > }
>> >
>> > I verified this with following requests:
>> >
>> > responseMsg = webResource.path("test").get(String.class); //
>> > matches getPeople
>> > responseMsg =
>> webResource.path("test?location").get(String.class);
>> > // matches getLocation
>> >
>> > Regards,
>> > Pavel
>> >
>> >
>> >
>> >
>> >
>> > On 11/15/11 11:10 AM, 王子剑 wrote:
>> >> Hello All
>> >>
>> >> I am handling a legacy system and I cannot process the
>> request by
>> >> Jersey gracefully.
>> >>
>> >> API:
>> >>
>> >> *GET : http://test.com/{people} <http://test.com/%7Bpeople%7D>
>> >> <http://test.com/%7Bpeople%7D> * Get people information
>> >>
>> >> *GET : http://test.com/{people}?location
>> <http://test.com/%7Bpeople%7D?location>
>> >> <http://test.com/%7Bpeople%7D?location>* Get people location.
>> >>
>> >>
>> >>
>> >> Code
>> >>
>> >> *_at_GET*
>> >> *_at_Path("{people}")*
>> >> *public People getPeopleInformation(String people);*
>> >> *
>> >> *
>> >> *_at_GET*
>> >> *_at_Path("{people}?location")*
>> >> *public People getPeopleLocation(String people);*
>> >> *
>> >> *
>> >>
>> >> Jersey will not invoke the second method
>> *getPeopleLocation *when
>> >> the client request url: *http://test.com/{people}?location
>> <http://test.com/%7Bpeople%7D?location>
>> >> <http://test.com/%7Bpeople%7D?location>*, it always
>> dispatch the
>> >> incoming request which end with '?location' to the first
>> method
>> >> '*getPeopleInformation*'.
>> >>
>> >> Is there some way to let Jersey to dispatch request based on
>> >> query parameters like this situation?
>> >> *
>> >> *
>> >> *
>> >> *
>> >
>> >
>>
>>
>