Can we have more than one @Path annotation for sam

2019-01-10 20:40发布

问题:

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.

回答1:

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.



回答2:

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.



回答3:

Another solution for your particular example:

  • http://a/b/c
  • http://a/b

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.