Jersey #GET method not working with 2 Path Params - java

in my Jersey REST interface, there are two methods (among others):
#GET
#Path("{scenarioId}/instance")
#Produces(MediaType.APPLICATION_JSON)
public Response getScenarioInstances(
#Context UriInfo uri,
#PathParam("scenarioId") int scenarioId,
#QueryParam("filter") String filterString) {
// code here
return Response.ok(result.toString(), MediaType.APPLICATION_JSON).build();
}
and
#GET
#Path("{scenarioId}/instance/{instanceId}")
#Produces(MediaType.APPLICATION_JSON)
public Response getScenarioInstance(
#Context UriInfo uri,
#PathParam("scenarioId") int scenarioId,
#PathParam("instanceId") int instanceId) {
// code here
return Response.ok(result.toString(), MediaType.APPLICATION_JSON).build();
}
A GET request via Postman to, say, /2/instance, calls the first method and results in a JSON object containing all instances.
A GET request to e.g. /2/instance/2 however does not call the second method, and results in a 404 error.
Am I missing something in the second method?

Okay, the problem was, that the REST interface was distributed over multiple classes, and there was another class that had a #Path(".../{scenarioId}/instance/{instanceId}) annotation.
I moved the method there, and it was called. Apparently this causes a conflict, even thought there was no #GET method declared for this path directly in this class.
In retrospect, I probably should have added that detail to the question.

Related

405 Method Not Allowed in implementing a custom HTTP method (possibly not a URL matching issue)

I am currently playing with JAX-RS, using Jersey. I was following the instructions here to try and implement a custom AUDIT method. I faced an issue where this AUDIT call was returning a "405 Method Not Allowed" message.
Below are the services that I have implemented so far.
#POST
#Consumes("application/json")
#Path("v1/Customers")
public Response createCustomers(final Customer input)
#GET
#Produces("application/json")
#Path("v1/Customers/{customerid: .*}")
public Response readCustomers(#PathParam("customerid") String customerId)
#PUT
#Consumes("application/json")
#Path("v1/Customers/{customerid}")
public Response updateCustomers(#PathParam("customerid") String customerId, final Customer input)
#DELETE
#Consumes("application/json")
#Path("v1/Customers/{customerid}")
public Response deleteAlerts(#PathParam("customerid") String customerId)
#AUDIT
#Produces("application/json")
#Path("v1/Customers/{customerid}")
public Response getCustomerHistory(#PathParam("customerid") String customerId)
Many of the responses to these kind of questions on SO indicate that there is a problem with the URL for the AUDIT call. However, when I replaced the #Path for the AUDIT call with, say, v1/Customers/test/{customerid} the call goes through perfectly. Besides URL issues, I cannot think of any other reason why the error occurs.
Any help would be much appreciated!

REST Subresource Locators

I am reading through the book RESTful Java with JAX-RS 2.0, 2nd Edition and am struggling to understand how Subresource Locators work, below is a cut-down version of the example provided.
CustomerDatabaseResource class
#Path("/customers")
public class CustomerDatabaseResource {
#Path("{database}-db")
public CustomerResource getDatabase(#PathParam("database") String db) {
// find the instance based on the db parameter
CustomerResource resource = locateCustomerResource(db);
return resource;
}
protected CustomerResource locateCustomerResource(String db) {
...
}
}
CustomerResource Class
public class CustomerResource {
private Map<Integer, Customer> customerDB =
new ConcurrentHashMap<Integer, Customer>();
private AtomicInteger idCounter = new AtomicInteger();
public CustomerResource(Map<Integer, Customer> customerDB)
{
this.customerDB = customerDB;
}
#GET
#Path("{id}")
#Produces("application/xml")
public StreamingOutput getCustomer(#PathParam("id") int id) {
...
}
So I understand that as a request such as GET /customers/northamerica-db/333 comes in, will first match the expression on the method CustomerDatabaseResource.getDatabase() which based upon the location, will create the correct instance of CustomerResource.
What I don't understand is what happens next...
The instance resource gets returned, but returned to where?
How does the web service know to then match and process the remaining part of the request with the method CustomerResource.getCustomer()? I guess this is because The CustomerDataBaseResource class doesn't have a #GET, but I don't really understand how the transition happens.
Is this specific to RESTEasy?
The instance resource gets returned, but returned to where?
It's get's returned to the request processing engine and continues to look for a matching method (inside the return resource object), just like any other request.
How does the web service know to then match and process the remaining part of the request with the method CustomerResource.getCustomer()? I guess this is because The CustomerDataBaseResource class doesn't have a #GET, but I don't really understand how the transition happens
Resource locators aren't supposed to be annotated with Http Methods. That's how they are known to be locators. Since it is not the resource method to be called, it should not be annotated. Imagine this
public class CustomerResource {
#PUT
#Path("{id}")
public Response updateCustomer(Customer customer) {}
#POST
#Path("{id}")
public Response createCustomer(Customer customer) {}
}
If CustomerDataBaseResource.getDatabase() were to be annotated with an Http method, then we couldn't hit the above methods. All the locator needs is the #Path, and the URI matching will continue starting from that path.
/customers/database-us
Once the CustomerResource is created, if the request uri is /customers/database-us/123, then now the next logical step is to find a matching resource method based on the URI, so will be looking for something annotated with #Path that will match 123. Then the Http method is checked.
Is this specific to RESTEasy?
Going through the jax-rs spec, I don't see anything about sub-resource locators, but Jersey also implements this exact behavior. I've read the book you are referring to, and from what I remember, the author doesn't really get much into anything that is implementation specific, but does mention common feautres that most implementers implemented, that is not part of the spec. Maybe this is one of those things.
UPDATE
So it is in the spec. Go to the link and download the spec. You will find everything under 3.4.1 Sub Resources and some algorithm info for request matching in 3.7.2 Request Matching

Jersey - Redirect after POST to outside URL

I'm using Jersey to create REST API. I have one POST method and as a response from that method, the user should be redirected to a custom URL like http://example.com that doesn't have to be related to API.
I was looking at other similar questions on this topic here but didn't find anything that I could use.
I'd suggest altering the signature of the JAX-RS-annotated method to return a javax.ws.rs.core.Response object. Depending on whether you intend the redirection to be permanent or temporary (i.e. whether the client should update its internal references to reflect the new address or not), the method should build and return a Response corresponding to an HTTP-301 (permanent redirect) or HTTP-302 (temporary redirect) status code.
Here's a description in the Jersey documentation regarding how to return custom HTTP responses: https://jersey.java.net/documentation/latest/representations.html#d0e5151. I haven't tested the following snippet, but I'd imagine that the code would look something like this, for HTTP-301:
#POST
public Response yourAPIMethod() {
URI targetURIForRedirection = ...;
return Response.seeOther(targetURIForRedirection).build();
}
...or this, for HTTP-302:
#POST
public Response yourAPIMethod() {
URI targetURIForRedirection = ...;
return Response.temporaryRedirect(targetURIForRedirection).build();
}
This worked for Me
#GET
#Path("/external-redirect2")
#Consumes(MediaType.APPLICATION_JSON)
public Response method2() throws URISyntaxException {
URI externalUri = new URI("https://in.yahoo.com/?p=us");
return Response.seeOther(externalUri).build();
}

Return error to client from Jersey's parameter injection code?

I have a couple of custom parameters that get injected into REST method handlers using Jersey 2.9. The parameter injection is performed by extending Jersey's AbstractContainerRequestValueFactory and related classes, as such:
#Provider
public class ParamProviderFactory extends AbstractContainerRequestValueFactory {
#Context
private UriInfo uriInfo;
#Override
public Object provide() {
// do some work, error happened due to user giving wrong query string format
// IMPORTANT PART:
// I need to return an error to the user from here
return result;
}
}
The parameter value injected depends on Query strings given by the user. If the query string values are invalid, I need to return an error to the user.
The question is: How can I return an error to the user and abort the REST call handling from within the provide method?
Thanks!!
Use the following in the provide() method:
getContainerRequest().abortWith(yourResponse)

java webservice response object vs Produces

Im trying to java webservices and trying to follow couple of tutorial examples.
In one of the example, I see #Produces annotation being used to specify the type of response that is being returned.
Example:
#GET
#Produces(MediaType.TEXT_PLAIN)
public String sayPlainTextHello() {
return "Hello Jersey";
}
but in another case, I see the Response object is being used as a Response...
Example:
#GET
#Path("/{param}")
public Response getMsg(#PathParam("param") String msg) {
String output = "Jersey say : " + msg;
return Response.status(200).entity(output).build();
}
Question:
Which is the correct way to send a response back - Response object or #Produces annotation?
When both scenarios can be used?
The best way is to use the combination of both, all the times. Here's why
#Produces basically defines the CONTENT-TYPE (MIME-TYPE) of the Response. But that is pretty much all. It does not define the HTTP Status codes (on error/success/server error etc). #Produces annotation just makes your life easier by not explicitly specifying WHAT the content-type would be in the Response.
Now why to use Response instead of "String" as the return type? Here's an example
Let's take the following code into consideration:
#GET
#Produces(MediaType.TEXT_PLAIN)
public String sayPlainTextHello() {
try
{
return someRemoteServerApi.getHelloString();
}
catch(exception ex)
{
return getErrorMessageString();
}
}
Now lets say that the remote server api failed to return back, which cased some kind of an error. NOW, you want to return an error back to the client ALONG with an error code (because frankly speaking, clients will only care for the error message string when developing. Once the client is developed, they will solely base their apis on the HTTP return status code).
So, in the above example, lets say you return an error json String such as (the getErrorMessageString()) :
{
"error":500
"message": "something went wrong"
}
BUT, your actual HTTP status code will still be "200 OK", because the Response is still a String and the response went through just fine. How would a client know if something went wrong in the apis? He will have to parse the json string (on error) and determine what went wrong.
Basically, in the above case, your success and failure BOTH will have the same HTTP status return code, which is not very helpful to the clients. Instead, the above code should change something like:
#GET
#Produces(MediaType.TEXT_PLAIN)
public Response sayPlainTextHello() {
try
{
return Response.status(200).entity(someRemoteServerApi.getHelloString()).build();
}
catch(exception ex)
{
return Response.status(500).entity(getErrorMessageString()).build();
}
}
Finally,
In regards to answering both your questions:
1) #Produces has nothing to do with WHAT kind of Response will be sent. It just sets the content-type on whatever Response object you will be sending. JAX-RS, by default, will put 200 OK response, unless ofcourse, an exception occurs that is uncaught. Then it will probably return 500. Basically, you will be relying on JAX-RS to return your error codes for you, which is not very good. You, as an implementer, should determine what kind of error codes and error messages should be sent to the client that are MOST meaningful
2) I will ALWAYS use Response as the return type of the method, along with #Produces and #Consumes annotations for each method. If you believe that your full resource (all methods in java resource class) is using the same #Produces and #Consumes mime-type (in most cases it's application/json) then you can define this at the class level itself, something along the lines of:
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
#Path("/rest/someResource")
public class MyResource()
{
#Path("/{resourceId")
#GET
public Response getResource(#PathParam("resourceId") String id)
{
doStuffAndReturnResponse();
}
}
This will, by default, apply the #produces and #consumes annotation to ALL the resource methods and if you want something specific on some specific resource method, you can override it by just providing the annotation for that specific method.
I hope I have explained it good enough! Happy coding!

Categories

Resources