Method not allowed when using regex in #Path with jersey - java

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

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.

Is there a way to order #Path in Quarkus

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

JAX-RS wildcard annotation

EDIT: I have a not-thoroughly-tested answer below from a colleague, but I would still like to hear discussion and better answers.
I'll be happy to be pointed to an answer, but I haven't found it with my searches.
Java REST can be implemented with annotations of classes and methods, including #Path annotation to match URIs. Is there an annotation, or other method, to match anything not explicitly matched by another annotation. An example:
#Path("")
public class RestEntryPoint {
#GET
#Path("/{s:.*}")
#Produces(MediaType.TEXT_PLAIN)
public String anything(#Context UriInfo ui) {
return "anything at all..." + ui.getPath();
}
#GET
#Path("/restserver/heartbeat")
#Produces(MediaType.TEXT_PLAIN)
public String heartbeat() {
return "Jetty RestServer heartbeat";
}
}
http://localhost:8082/restserver/heartbeat/
... returns the correct text in a browser ("Jetty RestServer heartbeat"). But I want any other URI to respond with the message from the method anything(#Context UriInfo ui) - however I only receive a 404.
If this can be done some other way, please tell me how. If this can be done this way (but differently) please show me the changes.
I think you need to map it to "/" explicitly and then let the method listen to ".*" or ".+":
#Path("/")
public class RestEntryPoint {
#GET
#Path("s:.*")
#Produces(MediaType.TEXT_PLAIN)
public String anything(#Context UriInfo ui) {
return "anything at all..." + ui.getPath();
}
#GET
#Path("restserver/heartbeat")
#Produces(MediaType.TEXT_PLAIN)
public String heartbeat() {
return "Jetty RestServer heartbeat";
}
}
This should do the trick.
just add a slash into the path.
like this
#Path("/")
public class RestEntryPoint {
#GET
#Produces(MediaType.TEXT_PLAIN)
public String anything(#Context UriInfo ui) {
return "anything at all..." + ui.getPath();
}
#GET
#Path("/restserver/heartbeat")
#Produces(MediaType.TEXT_PLAIN)
public String heartbeat() {
return "Jetty RestServer heartbeat";
}
}

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!";
}

Grizzly Jersey - Only #Path("/") works

I try to run a Jersey(1.17) resource on a Grizzly(2.2.21) server using HTTPS and Basic Auth and get everything working except the resource.
#Path("/")
public class Helloworld {
#GET
public String helloworld2() {
return "asdf2";
}
#Path("helloworld")
#GET
public String helloworld() {
return "asdf";
}
}
Yea it's just the Helloworld example and it still freaks me out.
I can access localhost:port/ and it works fine, but localhost:port/somethingother also returns "asdf2". Especially localhost:port/helloworld also returns "asdf2".
I also tryed
#Path("/")
public class Helloworld {
#GET
#Path("/helloworld")
public String helloworld() {
return "asdf";
}
}
and
#Path("/helloworld")
public class Helloworld {
#GET
public String helloworld() {
return "asdf";
}
}
In both cases i get 404 in Firebug for every request.
Does someone has a solution?
Thx
Edit:
To create the server and so on i use this example code (without the server truststore):
https://svn.java.net/svn/jersey~svn/trunk/jersey/samples/https-clientserver-grizzly/src/main/java/com/sun/jersey/samples/https_grizzly/
adding registration.addMapping("/*"); to my initialization code worked.
Thanks ver much alexey

Categories