Jersey #Path for plural/single REST nouns in same class - java

I have a class this is annotated with #Path like so:
#Path("widgets")
#Produces(MediaType.APPLICATION_XML)
public class WidgetResource {
#GET
public Response getWidgets(#QueryParam("limit"))
{
//This class returns the plural noun, a list of widgets
//...}
#GET
#Path("widget/{id}")
public Response getWidgetById(#PathParam("id") long id)
{
//This class returns a single widget by id
//...}
When I fire up a test client the localhost/widgets maps as expected, but when the getWidgetById method is mapped to localhost/widgets/widget/{id}. This is not what I want - I would like to have localhost/widgets and localhost/widget/{id}
I have tried omitting the #Path annotation at the class level, but that prevents Jersey from recognizing this class as a REST Resource (I tried both the ScanningResourceConfig and the ClassNameResourceConfig - both failed to load the class as a resource unless there was a #Path at the class level).
I guess a (ugly) workaround would be to split the methods between classes a WidgetResource class and a WidgetsResource class. I think this is a terrible solution since both of these methods share resources in the same class, but I really need the REST-ful localhost/widget (for a single entity) and localhost/widgets (for plural).
Am I missing something - is there some way for me to have Jersey pick up the class as a Resource class if I just #Path annotate the methods (I couldn't get it to work), if not can I force absolute mapping (#Path(/widget/{id})) or some relative mapping (#Path(../widget/id) - neither of those work in reality - just an analogy of what I'm after. Thanks!

This part is about what you need:
Personally, I find your mapping strange and confusing. Just keep it like this:
#Path("widgets")
#Produces(MediaType.APPLICATION_XML)
public class WidgetResource {
#GET
public Response getWidgets(#QueryParam("limit")) {
//This method returns the plural noun, a list of widgets
// it's also possible to limit the number returned by
// using a query parameter. You could easily implement
// pagination by adding further query parameters like
// 'offset', 'sortOrder', etc.
//...
}
#GET
#Path("{id}")
public Response getWidgetById(#PathParam("id") long id) {
//This method returns a single widget by id
//...
}
}
It seems natural to append the path to a collection with an ID to fetch an object from the collection. There's really no need to make it widgets/widget/{id}. The widget part is obvious and unnecessary.
Here's a really neat tutorial on RESTful APIs: "Teach a dog to REST" by apigee I think it's a really good video. The authors make a couple of good points. And here's a link to a longer version of the same presentation
This part is about what you want:
If you really want to keep the plural/singular dualism (which I really don't recomment), you can annotate your code like this:
But it's really ugly
#Path("/")
#Produces(MediaType.APPLICATION_XML)
public class WidgetResource {
#GET
#Path("widgets")
public Response getWidgets(#QueryParam("limit")) {
//This method returns the plural noun, a list of widgets
//...}
#GET
#Path("widget/{id}")
public Response getWidgetById(#PathParam("id") long id) {
//This method returns a single widget by id
//...
}
}

My suggestion is to have your paths be:
"widgets" and "widgets/id/{id}". Or if you knew you were never going to query by anything other than id, your second one could simply be "widgets/{id}".
I would not switch between plural and singular in your path. Since you accessing the same type of resource for both, your root should be the same. The second form just specifies it more -- a vectoring-based approach for getting more specific.

Related

Multiple resources under one request: RESTful API design pattern

I'm trying to create a GET request, where I have two different requests. Since both resources are related with each other, I'm trying to put them under one 'sub-resource'.
The first one is:
#QueryParam("{customerId}")
public List<customerModel> getCustomer(#QueryParam("customerId") Long customerId) {....}
this fetches the customer's name depending on the customerId
#QueryParam("{customerId}/details")
public List<customerDetailModel> getCustomerDetail(#QueryParam("customerId") Long customerId) {....}
this fetches the detailed information of the customer (phone number, address, etc.)
I'm running the first one with the following (works fine) :
......?customerId=7453112
but I can't reach the second request when I'm hitting the following URL:
......?customerId=7453112/details
Any suggestions?
Resource methods must be annotated with #Path and with a request method designator such as #GET, #POST, #PUT,
#DELETE, #HEAD or #OPTIONS. Your #QueryParam annotation is misplaced.
By the way, for this situation, you should consider using a path parameter instead of a query parameter. Hence you should use #PathParam instead of #QueryParam in the method parameter. For more details on when to use query or path parameters, have a look at this answer
Also, not sure why you are returning a List if you are requesting a resource by its unique identifier. In this situation, you are supposed to return a representation of a single resource (and not a represention of multiple resources).
So your resource class would be as following:
#Path("customers")
#Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public class CustomerResource {
#GET
#Path("{customerId}")
public CustomerModel getCustomer(#PathParam("customerId") Long id) {
...
}
#GET
#Path("{customerId}/details")
public CustomerDetailModel getCustomerDetail(#PathParam("customerId") Long id) {
...
}
}
The first endpoint can be requested as following:
GET http://example.com/api/customers/1
And the second endpoint can be requested as following:
GET http://example.com/api/customers/1/details
You need to go with #RequestMapping.
Something like:
#RequestMapping("/{customerId}") and #RequestMapping("/{customerId}/details").
Your URL becomes 7453112/details instead of query params.
You can specify the /details in an #Path annotation, and then use the same query parameter, like this:
#Path("/details/{customerId}")
public List<customerDetailModel> getCustomerDetail(#PathParam("customerId") Long customerId) {....}
Then your URL would look like this:
.../details/7453112
Or if you want to keep using it as a query parameter you can do something like this:
#Path("/details")
public List<customerDetailModel> getCustomerDetail(#QueryParam("customerId") Long customerId) {....}
using this url:
.../details?customerId=xxx

Will JAX-RS/Jersey resource paths honor inheritance?

Say I want the following URLs exposed by my JAX-RS/Jersey app:
http://myapp.example.com/app/fizz
http://myapp.example.com/app/buzz
http://myapp.example.com/app/foo
http://myapp.example.com/app/bar
Say I want /app to be a parent base resource, and /app/* to be "child" resources. Will the following accomplish the URL strategy I'm looking for (?):
#Path('/app')
#Produces(MediaType.APPLICATION_JSON)
public abstract class AppResource {
// Whatever...
}
#Path('/fizz') // <--- right here, will FizzResource live at /app/fizz?
#Produces(MediaType.APPLICATION_JSON)
public class FizzResource extends AppResource {
// Whatever...
}
Will the FizzResource be exposed at /app/fizz or just /fizz?
Will the FizzResource be exposed at /app/fizz or just /fizz?
Short answer
FizzResource will be exposed at /fizz.
Long answer
Quoting the JSR 339 (section 3.6 about Annotation Inheritance):
If a subclass or implementation method has any JAX-RS annotations then
all of the annotations on the superclass or interface method are
ignored.
The specification also says:
For consistency with other Java EE specifications, it is recommended to always repeat annotations instead of relying on annotation inheritance.
Creating sub-resources
The JAX-RS/Jersey documentation explains how to create sub-resources:
#Path may be used on classes and such classes are referred to as root resource classes.
#Path may also be used on methods of root resource classes. This enables common functionality for a number of resources to be grouped together and potentially reused.
The first way #Path may be used is on resource methods and such methods are referred to as sub-resource methods.
So, do the following to create sub-resources:
#Path("/app")
public class YourHandler {
#Produces(MediaType.APPLICATION_JSON)
public String yourHandlerForApp() {
// This method is be exposed at /app
}
#Path("/fizz")
#Produces(MediaType.APPLICATION_JSON)
public String yourHandlerForAppSlashFizz() {
// This method is be exposed at /app/fizz
}
}
I don't think the answers given are the best for the original problem statement.
He wants to have his subresources in separate classes. That's understandable and admirable because to not do that would mean putting all his endpoints in the same class, which would be huge.
If all endpoints on this port start with /app then I think the best way to do that is to configure your filter to put it in your #ApplicationPath.
If it's not the case that all endpoints start with the same prefix, then you will have to use this style of JAX-RS subresources where you specify a #Path but not an HTTP method annotation (#GET, etc.) and return an instance of the resource you want to delegate to:
#Path("/app")
public class AppResource {
#Context UriInfo uriInfo;
#Path("fizz")
public FizzResource getItemContentResource() {
return new FizzResource ();
}
}
#Produces(MediaType.APPLICATION_JSON)
public class FizzResource extends AppResource {
// Whatever...
}
This method of doing resources is provided in the JAX-RS documentation.
You can also have all your subresources declare their Paths as
#Path(BASE_URL + "/fizz")
Where BASE_URL is a static string, but I would try to avoid that because the use of a not-exactly constant parameter to #Path seems to cause every JAX-RS IDE plugin I've seen problems. They aren't able to compute the actual path, so they give up. So you might lose the ability to have a "JAX-RS View" that allows you to visualize/navigate your JAX-RS resources by the Paths.
What you want is
#Path("/app")
public class YourHandler {
#Path('/')
#Produces(MediaType.APPLICATION_JSON)
public String yourHandlerForApp() {
// Whatever...
}
#Path('/fizz') // <--- right here, will FizzResource live at /app/fizz?
#Produces(MediaType.APPLICATION_JSON)
public String yourHandlerForAppSlashFizz() {
// Whatever...
}
}

I cannot get to the correct #Path

Using JAX-RS, I have the following 3 #Paths.
#Path(JobRest.PATH)
#Api(value = JobRest.PATH, description = "REST APIs for Jobs")
public interface JobRest {
public static final String PATH = "/job";
#GET
#Path("/last")
#Produces(MediaType.APPLICATION_JSON)
public Job retrieveLastJob(...);
#GET
#Path("/{jobId}")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Job retrieveJob(...., #PathParam("jobId") String jobId, );
#GET
#Produces(MediaType.APPLICATION_JSON)
public JobList retrieveAllJobs(....);
}
/job correctly calls retrieveAllJobs()
/job/1236 correctly calls retrieveJob(..., "1236", ...).
I expected that /job/last would call retrieveLastJob(...), since it matches, but it calls retrieveJob(..., "last", ...) instead.
How do I change the notation so that /job/last will call retrieveLastJob(...)?
TL;DR
Remove the #Consumes(MediaType.APPLICATION_JSON) on the retrieveJob method. For one, it does not accept a body, so it does not consume anything. Secondly it conflicts with the expected behavior.
I've tested with both Jersey and RESTeasy and it seems to be a difference in implementation. Jersey works fine with your code, while RESTeasy always hits the retrieveJob method, as you are experiencing.
Here's my take. If you look at the JAX-RS spec; 3.7.2 Request Matching, there's a semi-cryptic algorithm for matching resources, that goes something like this.
Get all matching resource class (by path), put them into a set.
Get all matching resource methods (by path), put them into a set.
Sort the methods by best matching path (most literal characters go first).
Sort by media type (with consumes and produces).
From my perspective, in this particular case, after step 3, the retrieveLastJob should automatically win, as it has the most literal characters. The producing media types are the same, and the consumes media type should not even matter, since it is a GET request with no Content-Type to do any matching.
My guess it RESTeasy still uses the annotation to sort even though it should not even be taken into consideration in this case. So it would appear that the method with the annotation is given more precedence, as it appears to be more specific, by way of just having an annotation, while the other does not. But that (step 4) level of specificity really shouldn't matter in this case.
I don't know if it's a bug against the spec. It's not too clear on how it should be handled, but I personally think the Jersey behavior is the correct behavior, for the simple fact that this level of specificity should not matter in this particular case. In any case, it is not correct to have the #Consumes annotation anyway for a GET request with no body.

RESTful: how to dynamically extend an API path's (or available resources)?

I've had a nice 'ride' with RESTful technology. I am using a Hello.java resource like this:
#Path("/hello")
public class Hello {
... /* GET/PUT/POST */
}
With this I can access my resource with the path http://my.host/res/hello . I want to 'ride' RESTful even harder. Having this one resource path is a bit boring.
PROBLEM
I would like to have a dynamically created resources like this:
http://my.host/res/hello
http://my.host/res/hello/1
http://my.host/res/hello/2
...
http://my.host/res/hello/999
It doesn't make sense to create a .java resource for every #Path("/hello/1") ... #Path("/hello/999"). Right? Probably this list of sub-resources could be even bigger or dynamically change in time. What is the solution for that?
Thanks.
You can use the #Path annotation on methods inside your Resource class.
#Path("/hello")
public class Hello {
... /* GET/PUT/POST */
#GET
#Path("{id}")
public String myMethod(#PathParam("id") String id) {...}
}
The paths will be concatenated so it will match /hello/13. The {id} is a placeholder for the actual value entered, which can be retrieved with #PathParam. In the previous URI, the String id will have the value 13.
You will have to use PathParam feature for REST URI. http://docs.oracle.com/javaee/6/api/javax/ws/rs/PathParam.html
#Path("/hello/{id}")
public class Hello {
}

Rest set #Path for resource updates

Using Jersey, what is the RESTish way to this, Should I create a method starting with "update" like this, or I should create a subresource (or whatever Jax-Rs thing) under accountseetings path? Or should I simply use the same method name with different verbs?
#GET
#Path("/accountsettings")
public Settings accountSettings() {
}
#PUT
#Path("/updateaccountsettings")
public void updateAccountSettings() {
}
In REST verbs define what you are doing and URLs define what you are doing it to.
So here a PUT to /accountsettings would seem to be the normal way to do it.
Calling the method updateAccountSettings() seems to make sense.

Categories