How to allow slashes in path param in jax-rs endpoint - java

I have an endpoint as:
#Path("/products")
#Produces({ MediaType.APPLICATION_JSON })
public interface Products {
#PUT
#Path("/{productId}")
....
}
I have a jax-rs client implemented for this service and have it imported in the another service that I am calling this from.
So I am calling the client as below from my second service
public String updateProduct(String productId){
..
return client.target(this.getBaseUrl()).path("products/").path(productId).request(MediaType.APPLICATION_JSON_TYPE).put(Entity.json(""), String.class);
}
If I have a product with slashes say "control/register app" , the service does not seem to take it well. I did encode the productId before making a call to the service and then decoded it once received. But that doesnt seem to work and I get a 404 not found. Any ideas? Thanks in advance

Using #Path("{productId : .+}") should work.

Related

Adding another MediaType to REST api endpoint that is used by other clients

In my app I am sharing REST api using Spring MVC, which users may use in their custom apps. Let's say I have an endpoint in Controller class:
#GET
#Path("/getNumber")
#Produces({ MediaType.TEXT_PLAIN })
public String getNumber(Long id) {
return service.getNumber(id);
}
which response is available only as TEXT_PLAIN. What would happen, if for example one of the client say that his app will not work when he gets response as plain text and that the endpoint should return json/or should have possibility to return response in json? So when I add another MediaType to annotation #Produces, may that cause problems with other users custom apps using this endpoint? Because for ex. in their apps, client may be expecting response as plain text, and by getting response as json, response will not be handled correctly?
If adding this MediaType may cause problems, what can I do to take into account users custom apps using this endpoint? Should I create similar endpoint, but this one will have MediaType APPLICATION_JSON, or both, with added for ex. "/v2" in endpoint path, or is there some better solution for that?
#GET
#Path("/v2/getNumber")
#Produces({ MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON })
public String getNumber(Long id) {
return service.getNumber(id);
}
No need to create multiple endpoints. You can define multiple mediaTypes in #Produces.
Inorder to decide which MediaType should be sent as response, Client has to set the type of MediaType they are expecting in the "Accept" header when making the request.
Based on this "Accept" header you can decide what content-type to return.
you can use "consume" keyword for Multiple media type
#GET(value = "/configuration", consumes = {
MediaType.APPLICATION_JSON_VALUE,
MediaType.MULTIPART_FORM_DATA_VALUE
})
public String setConfiguration(#RequestPart MultipartFile file,
#RequestBody Configuration configuration)

How to specify path annotation in rest api java to accept any path?

I am using jersey rest service in java to accept request.
Here is my snippet
#Path("main")
public class xxxx{
#GET
#Path("test/{path}")
public void test(#Context HttpServletRequest req ) {
System.out.println(req.getRequestURI());
}
}
I am invoking this using REST Api as test/abcd , it is working. I want #path to accept test/abcd or test/abcd/ab and so. I tried with "test/{path}/*" nothing works.
Please someone help me as I am new to this.
You should use regex in the #Path for example :
#Path("{parameter: .*}")
Response getData(#PathParam("parameter") List<String> parameter){
//do processing
}
For more details you can see the examples given here.

Jersey 2.x - Conflicting route priority of PUT and GET

I'm working with Dropwizard, which uses Jersey internally. I have two methods on a controller:
PUT /garbage/[id1,id2,...idN] is intended to take a path parameter that's a list of numeric IDs representing resources to be updated. I'm using a regex-based PathParam here. I've fudged the regex in this example because I don't think it matters, but the point is that a single numeric ID should match the regex.
GET /garbage/[id] fetches data about a single piece of garbage.
Jersey seems to get confused, despite the difference in method. When I query with something like
curl localhost:8080/garbage/1
Jersey gives me a 405 error. If I take the PUT out of the picture (for example, sabotage the path param regex, or remove it entirely), the GET endpoint works fine.
I assume there is some detail in JAX-RS 3.7.2 I'm missing that explains why this should be the case, but I can't figure out what it is.
Here's the code:
#Path("/garbage")
#Produces(MediaType.APPLICATION_JSON)
public class GarbageController {
private static final Logger LOG = LoggerFactory.getLogger(GarbageController.class);
#PUT
#Path("/{params: [\\d,]+}")
#Consumes(MediaType.APPLICATION_JSON)
#Timed
public Response updateGarbage(#PathParam("params") List<PathSegment> params) {
LOG.warn("updateGarbage");
return Response.status(Response.Status.OK).build();
}
#GET
#Path("/{garbageId}")
public Response getGarbageById(#PathParam("garbageId") long garbageId) {
LOG.warn("getGarbage");
return Response.status(Response.Status.OK).build();
}
}
The main purpose of #PathSegment is to handle fragments of the URI which is useful to retrieve Matrix Parameters. For example the method below:
#GET
#Path("/book/{id}")
public String getBook(#PathParam("id") PathSegment id) {...}
Should be able to handle this request:
GET /book;name=EJB 3.0;author=Bill Burke
Because the #PathSegment intercepts the entire URL fragment the GET method seems to be ignored. You can handle the comma-separated IDs on the PUT request with a simple String split:
#PUT
#Path("/{params}")
#Consumes(MediaType.APPLICATION_JSON)
#Timed
public Response updateGarbage(#PathParam("params") String params) {
LOG.warn("updateGarbage ", params.split(","));
return Response.status(Response.Status.OK).build();
}
You can also change the request format to query parameters or implement a Converter/Provider to handle a custom object. All of them should solve the GET not implemented issue.
I believe this is not a case of route priorities between GET and PUT but instead this is related to the #Consumes annotation which cannot be used on a GET request. Either DW is ignoring this endpoint or is converting it into the default POST method, which would explain the 405 response for the GET request.
I figured this out, although I have not traced far enough into Jersey to know why it works. The solution is to rewrite the #GET method to use the same regex syntax as the #PUT. Jersey will handle the type conversion in the method signature, with the note that it will return a 404 if the type conversion fails (ie, GET /garbage/xyz).
#PUT
#Path("/{params: .+}")
#Consumes(MediaType.APPLICATION_JSON)
public Response updateGarbage(#PathParam("params") List<PathSegment> params) {
LOG.warn("updateGarbage");
return Response.status(Response.Status.OK).build();
}
#GET
#Path("/{params: .+}")
public Response getGarbageById(#PathParam("params") long garbageId) {
LOG.warn("getGarbage {}", garbageId);
return Response.status(Response.Status.OK).build();
}

How to create RESTful web service client by Jersey2.0 or above

There seems to be many examples about creating RESTful clients by Jersey 1.x, but not Jersey 2.0 or above.
I referred to other questions and the Jersey's web site, but I still cannot create a client for REST due to the differences between Jersey 2.0 and the previous one.
So I'd like to ask some advice.
So far, my coding is like this.
ClientConfig config = new ClientConfig();
Client client = ClientBuilder.newClient(config);
WebTarget target = client.target("http://localhost:8080/CustomerBack2211/webresources/entities.customer");
Invocation.Builder invocationBuilder = target.request(MediaType.TEXT_XML_TYPE);
Response response = invocationBuilder.get();
System.out.println(response.getStatus());
System.out.println(response.readEntity(String.class));
This produces 406 error.
However, when I tried to test RESTful service by Glassfish server, the test works properly, and the server side class has its #GET methods having #Produces({"application/xml", "application/json"}).
So I don't see why the coding above produces 406 error on a Java application.
(i.e. the client side has #GET methods in the following way)
#GET
#Path("{id}")
#Produces({"application/xml", "application/json"})
public Customer find(#PathParam("id") Integer id) {
return super.find(id);
}
#GET
#Override
#Produces({ "application/xml"})
public List<Customer> findAll() {
return super.findAll();
}
Does any of you see what I'm doing wrong, or could you please suggest an example of a RESTful client?
Any advice will be helpful...thanks in advance!
In addition, I'd appreciate if you would offer information about how to invoke methods like GET, PUT and DELETE with appropriate parameters.
I just needed to put an ID number (i.e. integer values) when I was testing the server side class on Glassfish RESTful test. However, it seems that I need to set "Class" and/or "Entity" values as arguments, but I cannot see any information associated with them on the Jersey website.
For the first block of code:
406 means Not Acceptable.
Look at your request() method target.request(MediaType.TEXT_XML_TYPE). From the Javadoc of request() if states
Invocation.Builder request(MediaType... acceptedResponseTypes)
Start building a request to the targeted web resource and define the accepted response media types.
Invoking this method is identical to:
webTarget.request().accept(types);
So basically, in your request, you are saying that you will only Accept: text/plain. Now look at your resource methods. Look at the #Produces. None of them "produce" text/plain. It's all json or xml. That's why you get the exception. Change the accept to application/xml (or MediaType.APPLICATION_XML) on the client side, and you should no longer get this error.
For the second question: I'm assuming you mean why does it work when you test it from the browser.
If you send a request from the browser by simply typing in the url, it will send out the request with many Accept types. If you have firebug (for FireFox) or the developer tools (for Chrome), if you send out a request, you will see a header similar to
Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
You can see application/xml in there. Even if application/xml wasn't there, the wild card */* is there, so basically almost all media types are acceptable as a return type when working in the browser.
For your last question:
Look at the API for SyncInvoker, which Invocation.Builder extends from. You will see different overrloaded put and post methods, most of which, as you mentioned accept an Entity.
There are a few different ways to build an Entity, all of which use one of the static methods. Here are some
Entity.entity( body, mediaType )
Entity.json( body )
Entity.xml( body )
And many more (see the Entity link above). But all of these static method return an Entity. So we could do something like
// resource method
#POST
#Consumes(MediaType.APPLICATION_XML)
public Response getResponse(Customer customer) { ... }
// some model class
#XmlRootElement
public class Customer { ... }
// client request
Customer customer = new Customer();
Response response = target.request().post(Entity.xml(customer));
Internally, the Customer will get converted to XML. If you used Entity.json is would get converted to JSON, BUT you need to make sure you have a JSON provider dependency. Jersey will not come with one by default. See more at Support for Common Media Type Representations
Also note, with your method find, when you try and make a request to the method, the request should end with an integer value, as that's the type specified for the {id} path parameter.

Adding POST Method within Jersey Class

Let's say I have a Jersey JAX-RS api end-point for handling http://<some_path>/foo. Ignore the ....
#Path("foo")
public class FooResource
#GET
#Produces("application/json")
public response getMethod(...)
I want to create POST end-point for foo/{id}/bar, where id is a path parameter and there's a body associated with the HTTP POST.
Example: HTTP POST foo/1/bar with body: { data : "...." }.
How can I add this POST method within the FooResource class? I tried an inner class, but it didn't work when I tested with Postman.
#POST
#Path("{id}/bar")
#Produces("application/json")
public response myPostMethod(...)
You can have path at method level. This will have your post method accessible via /foo/{id}/bar

Categories