37

Can we have more than one @Path annotation for same REST method i.e. the method executed is the same, but it is executed on accessing more than one URL?

E.g.: I want to run the searchNames() method on both http://a/b/c and http://a/b.

ROMANIA_engineer
  • 54,432
  • 29
  • 203
  • 199
Unreal user
  • 1,045
  • 2
  • 9
  • 13

4 Answers4

51

You can't have mutliple @Path annotations on a single method. It causes a "duplicate annotation" syntax error.

However, there's a number of ways you can effectively map two paths to a method.

Regular expressions in @Path annotation

The @Path annotation in JAX-RS accepts parameters, whose values can be restricted using regular expressions.

This annotation:

@Path("a/{parameter: path1|path2}")

would enable the method to be reached by requests for both /a/path1 and /a/path2. If you need to work with subpaths, escape slashes: {a:path1\\/subPath1|path2\\/subPath2}

Serving responses with a redirection status code

Alternatively, you could set up a redirection. Here's a way to do it in Jersey (the reference implementation of JAX-RS), by defining another subresource. This is just an example, if you prefer a different way of handling redirections, feel free to use it.

@Path("basepath")
public class YourBaseResource {

  //this gets injected after the class is instantiated by Jersey    
  @Context
  UriInfo uriInfo; 

  @Path("a/b")
  @GET
  public Responce method1(){
    return Response.ok("blah blah").build();
  }

  @Path("a/b/c")
  @GET
  public Response method2(){
    UriBuilder addressBuilder = uriInfo.getBaseUriBuilder();
    addressBuilder.path("a/b");
    return Response.seeOther(addressBuilder.build()).build();
  }

}

Using a servlet filter to rewrite URLs

If you're going to need such functionality often, I suggest intercepting the incoming requests using a servlet filter and rewriting the paths on the fly. This should help you keep all redirections in one place. Ideally, you could use a ready library. UrlRewriteFilter can do the trick, as long as you're fine with a BSD license (check out their google code site for details)

Another option is to handle this with a proxy set up in front of your Java app. You can set up an Apache server to offer basic caching and rewrite rules without complicating your Java code.

divideByZero
  • 1,120
  • 16
  • 27
toniedzwiedz
  • 17,895
  • 9
  • 86
  • 131
  • Thanks Tom. Regular expressions in @Path would not work as one of the path contains a slash. Redirection would send status 303, but I want 200 (same for both). Will try the servlet filter approach. – Unreal user Jun 08 '13 at 21:56
  • You can use slashes in @Path regular expressions – Jonas Jun 19 '13 at 21:14
  • @Jonas good to know. Do the slashes matched by regular expressions take precedence over the ones separating path segments? Personally, I wouldn't implement it this way. It would make the code harder to understand. – toniedzwiedz Jun 19 '13 at 21:16
  • @Tom Have you tried the example you provided in second solution? First, `Response.ok` or `Response.seeOther` return `Response.ResponseBuilder` object, you should add `.build()` to return `Response`. Even after I added, this doesn't work for me. – Cacheing Sep 16 '14 at 17:56
  • @Cacheing not this specific snippet. You're right, the `build()` call is missing. What happens when you add it? What do you mean by "doesn't work for me"? – toniedzwiedz Sep 16 '14 at 18:02
  • @Tom When I curl the `a/b/c` path, it doesn't return anything. – Cacheing Sep 16 '14 at 18:24
  • @Cacheing it should return a 303 status code and set a `Location` header in the response but there will be no response body. What happens when you call it with `curl -I` ? – toniedzwiedz Sep 16 '14 at 18:42
  • @Tom I see, I saw a 303 in the response header. – Cacheing Sep 16 '14 at 18:51
  • @Cacheing try it in a browser then, it should follow the redirect. Let me know if it works. Running `curl` with the `-L` option should also do the trick. – toniedzwiedz Sep 16 '14 at 18:57
21

As explained in Tom's answer, you can not use more than one @Path annotation on a single method, because you will run into error: duplicate annotation at compile time.

I think the simplest way to get around this is to use method overloading:

@Path("{foo}")
public Response rest(@PathParam("foo") final String foo) {
    return this.rest(foo, "");
}

@Path("{foo}/{bar}")
public Response rest(@PathParam("foo") final String foo,
                     @PathParam("bar") final String bar) {
    return Response.ok(foo + " " + bar).build();
}

You could also use more different method names if you run into the case where multiple overloaded methods have the signature.

Community
  • 1
  • 1
yegeniy
  • 1,272
  • 13
  • 28
12

Another solution for your particular example:

Let's suppose that:

  • /a is for the resource class
  • /b/c and /b are the paths for the methods

because a full path looks like:

<protocol><host><port><app><url-pattern><resource-path><method-path>.

Use optional parameter

@Path("/b{c : (/c)?}")
public Response searchNames(@PathParam("c") String val) {
    ...
}

The example above works for all examples like:

  • /b
  • /b/
  • /b/c
  • /b/c/

but when c is provided, the val is /c (it has a / before).

If you want to fix the problem above (to avoid Java parsing), you need something more complex:

@Path("/b{slash : (/)?}{c:((?<=/).*)?}")

which will return only c (not /c) for the 3rd bullet point, but for the 4th bullet point it will return c/ which has to be parsed in Java.

But for your case ("the method executed is the same"), don't worry about parsing because you don't have different actions.

ROMANIA_engineer
  • 54,432
  • 29
  • 203
  • 199
0

If you are using Spring then try

@RequestMapping(value = {"/def", "/abc"}, method = RequestMethod.POST)

This will work for both /abc and /def.

sSaroj Nov 17 '17 at 10:13

rogerdpack
  • 62,887
  • 36
  • 269
  • 388