Is there a way to order #Path in Quarkus - java

I currently have a problem similar to this but in quarkus-resteasy. I have looked around and I cannot find a way to order the paths. The endpoint for getAllNotifications() is never selected, all requests go to getModel():
#Get
#Path("/cars/{brand}/{model}")
#Produces(MediaType.APPLICATION_JSON)
public Response getModel(#PathParam("brand") String brand, #PathParam("model") String model) {
...
}
#Get
#Path("/cars/notification/all")
#Produces(MediaType.APPLICATION_JSON)
public Response getAllNotifications() {
...
}
Is there a way to prioritize either endpoint 1 or 2 like it was done here

Related

How to make multiple #Path in a #GET in JAX-RS API?

So, in my Resource class I have the following:
#GET
#Produces(MediaType.APPLICATION_JSON)
public List<Carta> get() {
return repositorio.getAll();
}
#GET
#Path("{id}")
#Produces(MediaType.APPLICATION_JSON)
public Carta getById(#PathParam("id") int id) {
return repositorio.getID(id);
}
both works, one is a general get (will get all) and the other get by ID. I need to add a third get by String but I'm failing with the #params. I need to add this to the Resource class:
#GET
#Path("{nome}")
#Produces(MediaType.APPLICATION_JSON)
public List<Carta> getNome(#PathParam("nome") String nome) {
return repositorio.getString(nome);
}
if I comment the getById lines, my getByString works, the code is good, i just need to make both function at the same time, if it receives a number, it looks for an ID, if its a String it looks into name and description. Also, I wonder if it's better code practice to create more endpoints? Like /search/byid/xx and /search/byname/xxx instead of a general /search/xxx? Thanks.
Rather than creating a different endpoint, you should enhance List<Carta> get() to support filtering by card name.
The expected way to do that is to use the #QueryParam annotation like this:
#GET
#Produces(MediaType.APPLICATION_JSON)
public List<Carta> get(#QueryParam("name") String name) {
return repositorio.getCartas(name);
}
In the case of null name parameter, all Carta instances will be returned.

Method not allowed when using regex in #Path with jersey

I am trying to provide endpoints that will listen on multiple versions, i.e /v1/test and /v2/test. In order not to duplicate my code, I use jersey's ability to use patterns in the #Path annotation.
Let's assume I want to provide a GET and a POST endpoint:
#Controller
#Slf4j
#Path("/")
public class TestController {
#GET
#Path("/v{version:[12]}/test")
#Produces(MediaType.APPLICATION_JSON)
public String test1(#PathParam("version") String version) {
System.out.println(String.format("GET /v%s/test called", version));
return "{\"foo\":\"bar\"}";
}
#POST
#Path("/v{version:[12]}/test")
#Produces(MediaType.APPLICATION_JSON)
public String test2(#PathParam("version") String version) {
System.out.println(String.format("POST /v%s/test called", version));
return "{\"foo\":\"bar\"}";
}
}
That works fine.
If I, however, try to use a specific path for the GET endpoints and use a pattern for the POST endpoint, I run into trouble.
Here the controller that would not work:
#Controller
#Slf4j
#Path("/")
public class TestController {
#GET
#Path("/v1/test")
#Produces(MediaType.APPLICATION_JSON)
public String test1() {
System.out.println("GET /v1/test called");
return "{\"foo\":\"bar1\"}";
}
#GET
#Path("/v2/test")
#Produces(MediaType.APPLICATION_JSON)
public String test2() {
System.out.println("GET /v2/test called");
return "{\"foo\":\"bar2\"}";
}
#POST
#Path("/v{version:[12]}/test")
#Produces(MediaType.APPLICATION_JSON)
public String test3(#PathParam("version") String version) {
System.out.println(String.format("POST /v%s/test called", version));
return "{\"foo\":\"barPOST\"}";
}
}
Doing GET /v1/test or GET /v2/test works fine, POST /v1/test however does not.
I get a 405 Method Not Allowed Exception.
As far as I got it the exception is thrown in the MethodSelectingRouter when it recognizes the path, but cannot find a method with the appropriate HTTP verb.
The issue seems to be that it picks the most specific path (/v1/test in my case) for which it does not know the POST verb.
Does anybody have an idea how to avoid this problem?
Cheers
PS: I am using spring boot with jersey (i.e. spring-boot-starter-web and spring-boot-starter-jersey) in version 1.5.2.RELEASE

Finding a method exists in rest service or not in Java

Hi i am new to Rest Service in Java. First i want to explain my problem and then at the end i will be asking question.
I am using Mozilla rest CLIENT. My rest class looks like:
#Path("/api1")
public class RestService {
#POST
#Path("/v1")
#Consumes("application/json")
#Produces("application/json")
public String v1(String json){
//Some code here
}
#POST
#Path("/v2")
#Consumes("application/json")
#Produces("application/json")
public String v2(String json){
//Some code here
}
}
Now in this code i have two functions.
To access v1, call will be:
http://localhost:8080/project_name/package/api1/v1
To access v2 call will be:
http://localhost:8080/project_name/package/api1/v2
Question:
Now in my rest service class i want to add a patch of code which detects that whether any function which has been called either v1,v2 or v3 exists in this service or not?
Can i do this? Or anyother way to do this?
Thanks
Well, you could add a wildcard response:
#POST
#Path("/{what}")
#Consumes("application/json")
#Produces("application/json")
public String v2(String json, #PathParameter("what") String what){
return "The path "+what+" does not exist.";
}
However, since the user will never see the direct responses, you can answer with a customized 404:
#POST
#Path("/{what}")
#Consumes("application/json")
#Produces("application/json")
public Response v2(String json, #PathParameter("what") String what){
return Response.status(Status.NOT_FOUND).entity("The path "+what+" does not exist.");
}
This way you can also detect on the client side when something is incorrect.
The best approach for you is to add a fallback response. That will be called when somebody tries to access any non existing WS method.
#RequestMapping(value = {"*"})
public String getFallback()
{
return "This is a fallback response!";
}

How to handle Backgrid.js cell edition requests

I am working with Backgrid these days.
Trying to edit a row value and to persist the database object, I end up with the following HTTP error:
NetworkError: 405 Method Not Allowed - http:// localhost:8084/fsrtool/api/roles/5
My web application driven by a Java Spring backend.
The Backgrid frontend is supposed to call the following method:
#GET
#Path("/{id}")
#Produces(MediaType.APPLICATION_JSON)
#Override
public Role getById(#PathParam("id") Long id) {
LOG.info("get a role with its ID");
Role r = rds.getById(id);
return r;
}
I know that my service class is working, because I am able to create new 'Roles' from the Backgrid table and thanks to the following method:
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
#Override
public Response create(final Role r) {
return Response.status(Status.CREATED).entity(rds.getOrSave(r)).build();
}
Investigating the problem, I figured out that backgrid sent the request with PUT method.
I then tried several changes into my service class in order to handle this request, but I have not been able to find the good way of setting this up.
Would you know how Backgrid cells should be edited?
So, as the application I'm working on seems to be designed, I actually was not using the good library...
The way I need my service method to be:
#PUT
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
#Path("/{id}")
public Response update(final Role r, #PathParam("id") Long id) {
// do some processing... save to the db
}

How do I get the JAX-RS #Path of a different resource during a POST?

I have two REST classes for a simple web service (Jersey and GlassFish) that involves user resources - one to operate on all users (e.g., a factory for #POSTing) and another on individual users (e.g., #GET, #PUT, #DELETE). They are at:
#Stateless #Path("users") public class AllUsersResource {...}
#Stateless #Path("user") public class OneUserResource {...}
respectively. When POSTing to AllUsersResource I want to return the Location (via Response.created(uri).build()) of the new User, e.g.,
http://localhost:8080/.../user/152
My question is how to do this. AllUsersResource injects #Context UriInfo uriInfo, but that does not get me #Path info for OneUserResource, only that of the current call ("users"). The way I finally got it working was simply to use reflection, but I'm worried it is brittle and unclean:
OneUserResource.class.getAnnotation(Path.class).value();
Searching StackOverflow the only other things I found to try were the following, without success:
com.sun.jersey.api.core.ResourceContext
javax.ws.rs.core.UriInfo.getMatchedResources()
#javax.inject.Inject OneUserResource oneUserRes;
Any help would be terrific!
You can use UriBuilder.fromresource(), but this only works if the supplied Resource class is a root resource (this is clearly mentioned in the javadocs). I found a way to achieve this even if you are in a sub-resource class:
#POST
#Consumes({MediaType.APPLICATION-XML, MediaType.APPLICATION-JSON})
public Response createUser(final User user, #Context UriInfo uriInfo) {
// persist the user here
URI uri = uriInfo.getAbsolutePathBuilder().path(user.getId()).build();
return Response.created(uri).build();
}
I found a couple of javax.ws.rs.core.UriBuilder methods that did the trick, which I wanted to share in case others had this question. They are: UriBuilder.fromResource(OneUserResource.class) and javax.ws.rs.core.UriBuilder.path(Class). I used the latter in a one-shot call:
URI newUserUri = uriInfo.getBaseUriBuilder().path(OneUserResource.class).path("/" + user.getId()).build();
return Response.created(newUserUri).build();
With the strict REST concept you can make it as one root resource
#POST /users -> CREATE a single user
#GET /users -> READ all users
#PUT /users -> UPDATE (REPLACE) all users ##?
#DELETE /users -> DELETE all users ##?
#POST /users/{id} -> CREATE a single user's some other child; ##?
#GET /users/{id} -> READ a single user
#PUT /users/{id} -> UPDATE a single user
#DELETE /users/{id} -> DELETE a single user
#Path("/users")
#Stateless
public class UsersResouce {
// /users
#POST
#Consumes({MediaType.APPLICATION-XML, MediaType.APPLICATION-JSON})
public Response createUser(final User user) {
// persist the user here
return Response.created("/" + user.getId()).build();
}
// /users
#GET
#Produces({MediaType.APPLICATION-XML, MediaType.APPLICATION-JSON})
public Response readUsers() {
//return all users
}
// /users/{id}
#GET
#Path("/{user_id: \\d+}")
#Produces({MediaType.APPLICATION-XML, MediaType.APPLICATION-JSON})
public Response readUser(
#PathParam("user_id") final Long userId) {
final User persisted = userBean.find(userId);
if (persisted == null) {
return Response.status(Status.NOT_FOUND).build();
}
return Response.ok().entity(persisted).build();
}
// /users/{id}
#Consumes({MediaType.APPLICATION-XML, MediaType.APPLICATION-JSON})
#PUT
#Path("/{user_id: \\d+}")
public Response updateUser(
#PathParam("user_id") final Long userId,
final User mergeable) {
final User persisted = userBean.find(userId);
if (persisted == null) {
userBean.persist(mergeable);
} else {
persist.setName(mergeable.getName());
userBean.merge(persisted);
}
return Response.status(Status.NO_CONTENT).build();
}
// /users/{id}
#DELETE
#Path("/{user_id: \\d+}")
public Response deleteUser(
#PathParam("user_id") final Long userId) {
userBean.delete(userId);
return Response.status(Status.NO_CONTENT).build();
}
#EJB
private UserBean userBean;
}
As of JAX-RS 2.0, the most correct way (As far as I know) is to use the builder method like so:
String uri = uriInfo.getBaseUriBuilder()
.path(ODataV4Endpoint.class)
.path(ODataV4Endpoint.class, "serviceEndpointJSONCatalog")
.resolveTemplate("endpointId", endpointId).build().toString();
FYI, I need to call path twice in my case, once for the path annotation on the class, and the second time for the annotation on the method. I suspected the call to the method would do both, but it does not.
The Path annotation on the endpoint serviceEndpointJSONCatalog declared a parameter, like so: 'endpoint/{endpointId}', so the call to resolveTemplate was needed. Otherwise you would just call path(Class cl, String method).
In my case I created a builder and a symbolic way to reference the methods so the compiler / runtime could check them.

Categories