users@jersey.java.net

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

From: 王子剑 <nerther_at_gmail.com>
Date: Thu, 17 Nov 2011 18:31:08 +0800

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>写道:

> 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>写 道:
>
>> 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>>
>> >
>> > Hi 王子剑,
>> >
>> > please use 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> * Get people information
>> >>
>> >> *GET : http://test.com/{people}?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>*, 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?
>> >> *
>> >> *
>> >> *
>> >> *
>> >
>> >
>>
>>
>