Error 404 accessing to a SubResource in a JAX-RS Jersey - java

I have these 2 resources
#Path("/orders")
public class OrderResource {
#GET
#Path("/{id}")
#Produces(MediaType.APPLICATION_JSON)
public Response getOrder(#PathParam("id") String orderid)
throws JSONException {
Order order = db.getOrder(orderid);
return Response.status(Status.OK).entity(order).build();
}
#GET
#Path("/{orderid}/products")
public ProductResource getProducts() {
return new ProductResource();
}
}
#Path("/")
public class ProductResource {
#GET
#Path("/{productid}")
#Produces(MediaType.APPLICATION_JSON)
public Response getProduct(#PathParam("orderid") String orderid, #PathParam("productid") String productid) throws JSONException {
Product product = db.getProduct(productid);
return Response.status(Status.OK).entity(product).build();
}
}
I get a successful output when I do this:
http://localhost:8080/testApp/api/orders/O101
I can see the collection of the products linked to the order in the output so I copied the id and tried this
http://localhost:8080/testApp/api/orders/O101/products/P101
But I always get a 404 error. Why? How can I solve this?
This is my config in the web.xml
<servlet-mapping>
<servlet-name>TestApp</servlet-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>
EDIT
Thank you so much for your answers. Woke up this morning tired to test it with no success.
I tried your suggestions, but still get 404.
#Path("/orders")
public class OrderResource {
#GET
#Path("/{id}")
#Produces(MediaType.APPLICATION_JSON)
public Response getOrder(#PathParam("id") String orderid)
throws JSONException {
Order order = db.getOrder(orderid);
return Response.status(Status.OK).entity(order).build();
}
#GET
#Path("/{orderid}/products") //Here I added after products /{productID} which gives me an empty JSON. Never reach the method from the subresource.
public ProductResource getProducts() {
return new ProductResource();
}
}
public class ProductResource {
#Path("/{productid}") //Here I tried to remove the slash also.
#Produces(MediaType.APPLICATION_JSON)
public Response getProduct(#PathParam("orderid") String orderid, #PathParam("productid") String productid) throws JSONException {
Product product = db.getProduct(productid);
return Response.status(Status.OK).entity(product).build();
}
}

The problem is the #GET on the getProducts. A sub-resource locator is defined as a method with a #Path and which has no #METHOD. If you think about it, it makes perfect sense, as the there can be more than say just a #GET in the sub-resource class. So remove the #GET, and it should work. Leaving it would cause the method to not be a sub-resource locator, and it would behave like a normal resource method.
Aside from that, what others have suggested about the #Path("/") is not the cause of the problem, but it is a problem. What this does is cause Jersey to also register the ProductsResource as a root resource. So would be able to access /api/1234, since it is mapped to /. You probably don't want this. So you should remove the #Path("/") from the ProductsResource.

Sub-resources shouldn't be annotated with #Path on class level and they need to be registered with the JAX-RS runtinme.
Just remove the #Path annotation.

In your case, the problem seems to be the annotation #Path in your sub-resource. When defining a sub-resource, it should not be annotated at the class level with #Path. Also in your ProductResource, try removing the '/' from #Path("/{productid}") as it should be referenced from the context of the parent(OrderResource) and should not exists as an individual instance.
Thanks

Related

Overriding a #Path annotation in a class by method in JAX-RS

I have a REST API service i maintain in java (over jersey, JAX-RS)
I want to support the following route in my service:
/api/v1/users/{userId}/cars
however, it concatinates to the class's #Path annotation. e.g.
/api/v1/cars/api/v1/users/{userId}/cars
This is my service class:
#Path("api/v1/cars")
public class CarsService {
#GET
#Path("/api/v1/users/{userId}/cars")
public Response getUserCars(#PathParam("userId") Long userId) {
// ...
}
#GET
public Response getCars() {
// ...
}
}
Is there any way to override it?
Note the following:
The #Path annotation in a class designates a root resource.
The #Path annotation in a method designates a sub-resource of a root resource.
When placed on methods, the #Path annotation does not override the #Path annotation of the class. JAX-RS/Jersey performs a hierarchical matching using the #Path annotations.
So, you can try:
#Path("api/v1")
public class CarsService {
#GET
#Path("/cars")
public Response getCars() {
...
}
#GET
#Path("/users/{userId}/cars")
public Response getUserCars(#PathParam("userId") Long userId) {
...
}
}
However, have you considered using different resource classes?
#Path("api/v1/cars")
public class CarsService {
#GET
public Response getCars() {
...
}
}
#Path("api/v1/users")
public class UsersService {
#GET
#Path("{userId}/cars")
public Response getUserCars(#PathParam("userId") Long userId) {
...
}
}
For more details on resources, have a look at the documentation.
You just should change the #Path annotation of the method to:
#Path("users/{userId}/cars")
In this way, the resulting path of concatenating the class and the method #Path annotations will produce your desired path.

Add new resource in restfull with Jersey

I have a messages resource:
#Path("/messages")
#Consumes("application/json")
#Produces("application/json")
public class MessageResource {
...
#Path("/{msgId}/comments")
public CommentsResource getCommentsResource() {
return new CommentsResource();
}
Now i want to add a new comment for a particular messageId,
So here is the comments resource:
#Path("/")
#Produces("application/json")
#Consumes("application/json")
public class CommentsResource {
#POST
public Comment addNewComment(Comment newComment, #PathParam("msgId") long messageId) {
commentService.addComment(messageId, newComment);
return newComment;
}
and here is commentService.addComment:
public Comment addComment(long msgId, Comment newComment) {
Map<Long, Comment> allCommentsOfAMessage = messages.get(msgId).getComments();
newComment.setId(allCommentsOfAMessage.size() + 1);
allCommentsOfAMessage.put(newComment.getId(), newComment);
return newComment;
}
But problem is that the addNewComment() method of the CommentsResource class never been called with some URL like: http://localhost:8080/messages/2/comments
Including a new comment in Json format by POST method.
(This URL should add a new comment to message with Id=2)
The Jersey documentation says that you can do this in a way such as the root resource is /messages/comments/{msgId}. It probably cannot propagate the pathparam if you catch it in the root resource and not in the sub-resource.
Maybe you should just have them in the same class or have comments as a root resource like POST /comments/{msgId}.

Multiple GET methods match: select most specific

I have a web service that looks like:
#Path("/ws")
public class Ws {
#GET public Record getOne(#QueryParam("id") Integer id) { return record(id); }
#GET public List<Record> getAll() { return allRecords(); }
}
The idea is that I can either call:
http://ws:8080/ws?id=1 to get a specific record
http://ws:8080/ws to get all available records
However when I use the second URL, the first #GET method is called with a null id.
Is there a way to achieve what I want without using different paths?
I think this can be achieved with Spring using the #RequestMapping(params={"id"}) and #RequestMapping annotations for the first and second methods respectively but I can't use Spring in that project.
Since the path is the same, you cannot map it to a different method. If you change the path using REST style mapping
#Path("/ws")
public class Ws {
#GET #Path("/{id}") public Response getOne(#PathParam("id") Integer id) { return Response.status(200).entity(record(id)).build(); }
#GET public Response getAll() { return Response.status(200).entity(allRecords()).build(); }
then you should use:
http://ws:8080/ws/1 to get a specific record
http://ws:8080/ws to get all available records

Error:param #param is always null

I have a problem with Jersey and Grizzly. The problem could be very basic but I am struggling to solve it. The idea is that I am creating an exercise application that needs to store books. Everything seems to be alright but it does not work as expected. Here is the source code:
#Path("/books")
public class BooksResource
{
private BookDao bookDao= new BookDao();
#GET
#Produces(MediaType.APPLICATION_JSON)
public Collection<Book> getBooks()
{
return (bookDao.getBooks());
}
#Path("/{id}")
#GET
#Produces(MediaType.APPLICATION_JSON)
public Book getBook(#PathParam("id")String id)
{
Book book = bookDao.getBook(id);
return (book);
}
As can be observed, the path /books is working perfectly but the problem is that id is always null and it shouldn't be. Does anyone know where the problem comes from?
Try removing "/" from the path and it should work.
From
#Path("/{id}")
To
#Path("{id}")

Getting place holder value from resource path in REST?

I am using Jersey RESTful web services. I have below resource.
#Path("/banker/{location}")
#Component
public class BankResource{
#GET
#Path("/{id}")
#Produces({MediaType.APPLICATION_XML})
#Override
public Response getBankerByID(#PathParam("id") String id) {
//some logic
}
}
In above class how can i get the value of {location} ? Because based on {location} value i need to do some processing. Is it possible to get its value?
Thanks!
You can get the location value using #PathParam and map it to method parameter as follows.
`
#Path("/banker/{location}")
#Component
public class BankResource{
#GET
#Path("/{id}")
#Produces({MediaType.APPLICATION_XML})
#Override
public Response getBankerByID(#PathParam("location") String location,
#PathParam("id") String id) {
//your code here
}
`

Categories

Resources