You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@cxf.apache.org by Kevin Schmidt <kt...@gmail.com> on 2015/06/24 02:42:01 UTC

Problem with optional @PathParam

I have a situation where I'd like a @PathParam to be optional.  For example
I'd like for the path /{tenant}/resource/{id} to be matched for both
.../myTenant/resource/12345 and .../resource/12345, the second case passing
in null for the tenant @PathParam.

I did some research and it seemed like using a regex in my @Path would do
the trick, but I've been unable to get it to work.  And in one case, I'm
getting behavior that seems to be a bug?

Here is what I've done:

    @GET
    @Path("/{tenant : [^/]*}/resource/{id}")
    @Produces({MediaType.APPLICATION_JSON})
    public Response getResource(@PathParam("tenant") String tenant,
@PathParam("id") String id) {
...
    }

When I use the above with a URI .../t1/resource/12345 I get tenant=t1 and
id=12345 as expected, so all is well.

If I use .../resource/12345 I get a "No operation matching request path" in
my log.

If I use ...//resource/12345 I oddly get tenant=12345 and id=null.

It is this last case that seems odd, even if my regex in @Path isn't fully
correct, I'd still expect tenant=null and id=12345 in this case.  So is
this a bug?  Or is there an explanation for it?

But more importantly, is there a regex I can use to have tenant=null when I
use .../resource/12345 and tenant=t1 when I use .../t1/resource/12345?

FWIW, if I try this @Path:

@Path("/{tenant : ([^/]*)?}/resource/{id}")

I get tenant=t1 and id=t1 which also doesn't seem right.  Another bug?

I'm using CXF as part of Camel 2.15.1 running in Karaf 3.0.x.

Thanks,

Kevin

Re: Problem with optional @PathParam

Posted by Sergey Beryozkin <sb...@gmail.com>.
Hi

I've updated the code to better handle requests like
"//resource/12345" against "/{tenant : [^/]*}/resource/{id}".

CXF UriTemplate is trying to move to the next group value if the current 
value is empty/null - and there's a test where a regular expression 
matching produces an empty value followed by the actual var value, 
typically happens if a nested group is available.
But it is not correct when a custom regular expression like the one 
occurs. So I tweaked the code a bit:

https://issues.apache.org/jira/browse/CXF-6474

However there's not much we can do with

@Path("/{tenant : ([^/]*)?}/resource/{id}")

It has a nested group and I've debugged it:

m.group(1) = t1
m.group(2) = t1
m.group(3) = 1

we have only two vars and there's no way to know that a non-empty 
m.group(2) is happens to be a duplicate but not the real value of the 
2nd var. If there's a well-known logic there that can let us have a 
reliable code then I might consider updating it but I'd rather stay away 
from it.

Nested groups work a bit better when they are in the end in a case like
"{entitySetName}{optionalParens: (\\(\\))?}", etc, ex, when one needs to 
match /a, /a/1, /a/1/2 with a possibly unlimited path segment space 
starting from /a.

I agree with Craig it is simpler to avoid using the explicit regular 
expressions if possible...

Thanks, Sergey







On 24/06/15 02:39, Kevin Schmidt wrote:
> That is my fallback plan and isn't that much extra work.  Was just wanting
> to see if JAX-RS had a way to help with it.
>
> On Tue, Jun 23, 2015 at 6:22 PM, Craig McClanahan <cr...@gmail.com>
> wrote:
>
>> I tend to prefer solutions to things like this that will be obviously
>> understood when I pass responsibility for the code on to someone else in
>> the future, especially if that person is (like me) not particularly fluent
>> at regexp syntax :-).  How about just going for two resource methods with
>> individual paths (/{tenant}/resource{id} and /resource/{id}) and having
>> them both call a common service method that takes tenant and id parameters,
>> and the second one passes null for the tenant?
>>
>> Craig
>>
>>
>> On Tue, Jun 23, 2015 at 5:42 PM, Kevin Schmidt <kt...@gmail.com>
>> wrote:
>>
>>> I have a situation where I'd like a @PathParam to be optional.  For
>> example
>>> I'd like for the path /{tenant}/resource/{id} to be matched for both
>>> .../myTenant/resource/12345 and .../resource/12345, the second case
>> passing
>>> in null for the tenant @PathParam.
>>>
>>> I did some research and it seemed like using a regex in my @Path would do
>>> the trick, but I've been unable to get it to work.  And in one case, I'm
>>> getting behavior that seems to be a bug?
>>>
>>> Here is what I've done:
>>>
>>>      @GET
>>>      @Path("/{tenant : [^/]*}/resource/{id}")
>>>      @Produces({MediaType.APPLICATION_JSON})
>>>      public Response getResource(@PathParam("tenant") String tenant,
>>> @PathParam("id") String id) {
>>> ...
>>>      }
>>>
>>> When I use the above with a URI .../t1/resource/12345 I get tenant=t1 and
>>> id=12345 as expected, so all is well.
>>>
>>> If I use .../resource/12345 I get a "No operation matching request path"
>> in
>>> my log.
>>>
>>> If I use ...//resource/12345 I oddly get tenant=12345 and id=null.
>>>
>>> It is this last case that seems odd, even if my regex in @Path isn't
>> fully
>>> correct, I'd still expect tenant=null and id=12345 in this case.  So is
>>> this a bug?  Or is there an explanation for it?
>>>
>>> But more importantly, is there a regex I can use to have tenant=null
>> when I
>>> use .../resource/12345 and tenant=t1 when I use .../t1/resource/12345?
>>>
>>> FWIW, if I try this @Path:
>>>
>>> @Path("/{tenant : ([^/]*)?}/resource/{id}")
>>>
>>> I get tenant=t1 and id=t1 which also doesn't seem right.  Another bug?
>>>
>>> I'm using CXF as part of Camel 2.15.1 running in Karaf 3.0.x.
>>>
>>> Thanks,
>>>
>>> Kevin
>>>
>>
>


-- 
Sergey Beryozkin

Talend Community Coders
http://coders.talend.com/

Blog: http://sberyozkin.blogspot.com

Re: Problem with optional @PathParam

Posted by "John D. Ament" <jo...@apache.org>.
Realistically in REST, path params are never optional.  What you're trying
to do is bind the same service to two different URIs.  This might be a
reason why you want to use sub resources to do the implementation.

Assuming you have some wrapper level around this, adding these two methods
should work.  Note that I've never actually tried this with CXF, but
technically from a spec standpoint this should work fine.

@Path("/")
public class SomeAPI {

@Path("/{tenantId}/someResource")
public SomeResource tenantSomeResource() { // return the resource }

@Path("/someResource")
public SomeResource someResource() { // return the resource }

}

What this says is that the top level SomeAPI class is bound at / (root of
your context) and will serve any request.  It provides two subresources,
one bound at /tenantId/someResource and another at just /someResource which
both provide the same APIs.  Within SomeResource class here you would
provide the implementation of what you're describing, e.g.

public class SomeResource {
@GET @Path("/{id}")
public Response getBySomeId(@PathParam("id") int id, @PathParam("tenantId")
String tenantId)
}

And then tenantId would be null if not found (hopefully, again, never tried
with CXF).

John

On Tue, Jun 23, 2015 at 9:40 PM Kevin Schmidt <kt...@gmail.com> wrote:

> That is my fallback plan and isn't that much extra work.  Was just wanting
> to see if JAX-RS had a way to help with it.
>
> On Tue, Jun 23, 2015 at 6:22 PM, Craig McClanahan <cr...@gmail.com>
> wrote:
>
> > I tend to prefer solutions to things like this that will be obviously
> > understood when I pass responsibility for the code on to someone else in
> > the future, especially if that person is (like me) not particularly
> fluent
> > at regexp syntax :-).  How about just going for two resource methods with
> > individual paths (/{tenant}/resource{id} and /resource/{id}) and having
> > them both call a common service method that takes tenant and id
> parameters,
> > and the second one passes null for the tenant?
> >
> > Craig
> >
> >
> > On Tue, Jun 23, 2015 at 5:42 PM, Kevin Schmidt <kt...@gmail.com>
> > wrote:
> >
> > > I have a situation where I'd like a @PathParam to be optional.  For
> > example
> > > I'd like for the path /{tenant}/resource/{id} to be matched for both
> > > .../myTenant/resource/12345 and .../resource/12345, the second case
> > passing
> > > in null for the tenant @PathParam.
> > >
> > > I did some research and it seemed like using a regex in my @Path would
> do
> > > the trick, but I've been unable to get it to work.  And in one case,
> I'm
> > > getting behavior that seems to be a bug?
> > >
> > > Here is what I've done:
> > >
> > >     @GET
> > >     @Path("/{tenant : [^/]*}/resource/{id}")
> > >     @Produces({MediaType.APPLICATION_JSON})
> > >     public Response getResource(@PathParam("tenant") String tenant,
> > > @PathParam("id") String id) {
> > > ...
> > >     }
> > >
> > > When I use the above with a URI .../t1/resource/12345 I get tenant=t1
> and
> > > id=12345 as expected, so all is well.
> > >
> > > If I use .../resource/12345 I get a "No operation matching request
> path"
> > in
> > > my log.
> > >
> > > If I use ...//resource/12345 I oddly get tenant=12345 and id=null.
> > >
> > > It is this last case that seems odd, even if my regex in @Path isn't
> > fully
> > > correct, I'd still expect tenant=null and id=12345 in this case.  So is
> > > this a bug?  Or is there an explanation for it?
> > >
> > > But more importantly, is there a regex I can use to have tenant=null
> > when I
> > > use .../resource/12345 and tenant=t1 when I use .../t1/resource/12345?
> > >
> > > FWIW, if I try this @Path:
> > >
> > > @Path("/{tenant : ([^/]*)?}/resource/{id}")
> > >
> > > I get tenant=t1 and id=t1 which also doesn't seem right.  Another bug?
> > >
> > > I'm using CXF as part of Camel 2.15.1 running in Karaf 3.0.x.
> > >
> > > Thanks,
> > >
> > > Kevin
> > >
> >
>

Re: Problem with optional @PathParam

Posted by Kevin Schmidt <kt...@gmail.com>.
That is my fallback plan and isn't that much extra work.  Was just wanting
to see if JAX-RS had a way to help with it.

On Tue, Jun 23, 2015 at 6:22 PM, Craig McClanahan <cr...@gmail.com>
wrote:

> I tend to prefer solutions to things like this that will be obviously
> understood when I pass responsibility for the code on to someone else in
> the future, especially if that person is (like me) not particularly fluent
> at regexp syntax :-).  How about just going for two resource methods with
> individual paths (/{tenant}/resource{id} and /resource/{id}) and having
> them both call a common service method that takes tenant and id parameters,
> and the second one passes null for the tenant?
>
> Craig
>
>
> On Tue, Jun 23, 2015 at 5:42 PM, Kevin Schmidt <kt...@gmail.com>
> wrote:
>
> > I have a situation where I'd like a @PathParam to be optional.  For
> example
> > I'd like for the path /{tenant}/resource/{id} to be matched for both
> > .../myTenant/resource/12345 and .../resource/12345, the second case
> passing
> > in null for the tenant @PathParam.
> >
> > I did some research and it seemed like using a regex in my @Path would do
> > the trick, but I've been unable to get it to work.  And in one case, I'm
> > getting behavior that seems to be a bug?
> >
> > Here is what I've done:
> >
> >     @GET
> >     @Path("/{tenant : [^/]*}/resource/{id}")
> >     @Produces({MediaType.APPLICATION_JSON})
> >     public Response getResource(@PathParam("tenant") String tenant,
> > @PathParam("id") String id) {
> > ...
> >     }
> >
> > When I use the above with a URI .../t1/resource/12345 I get tenant=t1 and
> > id=12345 as expected, so all is well.
> >
> > If I use .../resource/12345 I get a "No operation matching request path"
> in
> > my log.
> >
> > If I use ...//resource/12345 I oddly get tenant=12345 and id=null.
> >
> > It is this last case that seems odd, even if my regex in @Path isn't
> fully
> > correct, I'd still expect tenant=null and id=12345 in this case.  So is
> > this a bug?  Or is there an explanation for it?
> >
> > But more importantly, is there a regex I can use to have tenant=null
> when I
> > use .../resource/12345 and tenant=t1 when I use .../t1/resource/12345?
> >
> > FWIW, if I try this @Path:
> >
> > @Path("/{tenant : ([^/]*)?}/resource/{id}")
> >
> > I get tenant=t1 and id=t1 which also doesn't seem right.  Another bug?
> >
> > I'm using CXF as part of Camel 2.15.1 running in Karaf 3.0.x.
> >
> > Thanks,
> >
> > Kevin
> >
>

Re: Problem with optional @PathParam

Posted by Craig McClanahan <cr...@gmail.com>.
I tend to prefer solutions to things like this that will be obviously
understood when I pass responsibility for the code on to someone else in
the future, especially if that person is (like me) not particularly fluent
at regexp syntax :-).  How about just going for two resource methods with
individual paths (/{tenant}/resource{id} and /resource/{id}) and having
them both call a common service method that takes tenant and id parameters,
and the second one passes null for the tenant?

Craig


On Tue, Jun 23, 2015 at 5:42 PM, Kevin Schmidt <kt...@gmail.com> wrote:

> I have a situation where I'd like a @PathParam to be optional.  For example
> I'd like for the path /{tenant}/resource/{id} to be matched for both
> .../myTenant/resource/12345 and .../resource/12345, the second case passing
> in null for the tenant @PathParam.
>
> I did some research and it seemed like using a regex in my @Path would do
> the trick, but I've been unable to get it to work.  And in one case, I'm
> getting behavior that seems to be a bug?
>
> Here is what I've done:
>
>     @GET
>     @Path("/{tenant : [^/]*}/resource/{id}")
>     @Produces({MediaType.APPLICATION_JSON})
>     public Response getResource(@PathParam("tenant") String tenant,
> @PathParam("id") String id) {
> ...
>     }
>
> When I use the above with a URI .../t1/resource/12345 I get tenant=t1 and
> id=12345 as expected, so all is well.
>
> If I use .../resource/12345 I get a "No operation matching request path" in
> my log.
>
> If I use ...//resource/12345 I oddly get tenant=12345 and id=null.
>
> It is this last case that seems odd, even if my regex in @Path isn't fully
> correct, I'd still expect tenant=null and id=12345 in this case.  So is
> this a bug?  Or is there an explanation for it?
>
> But more importantly, is there a regex I can use to have tenant=null when I
> use .../resource/12345 and tenant=t1 when I use .../t1/resource/12345?
>
> FWIW, if I try this @Path:
>
> @Path("/{tenant : ([^/]*)?}/resource/{id}")
>
> I get tenant=t1 and id=t1 which also doesn't seem right.  Another bug?
>
> I'm using CXF as part of Camel 2.15.1 running in Karaf 3.0.x.
>
> Thanks,
>
> Kevin
>