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";
}
}
Related
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.
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
I wrote a jax-rs service using jersey, and the class is as follows,
I want to define the url for the following like this,
One to get the reports by passing parameters
http://localhost:8085/DiginReport/rs/Reports/getreport/{test1:value,test2:value}
One to start the Engine:
http://localhost:8085/DiginReport/rs/Reports/start
#Path("Reports")
public class ReportService {
#GET
#Path("/getreport}/{parameters}")
#Produces(MediaType.TEXT_PLAIN)
public Response getReport(#PathParam("reportName") String reportName,#PathParam("parameters") String parameters) throws KettleXMLException, KettleMissingPluginsException, JSONException, UnknownParamException {
JSONArray jsonarr;
return Response.status(200).entity("ok").build();
}
#GET
#Path("{start}")
#Produces(MediaType.TEXT_PLAIN)
public Response startEngine(#PathParam("start") String command) {
return Response.status(200).entity("ok").build();
}
}
When i type this in url , it says resource not avaliable,
http://localhost:8085/DiginReport/rs/Reports/start
Try the following code.
I would also consider the following things:
Pass Json data as application/json instead of string as path parameter (note that you need to escape.
Use another method than GET to start the engine (e.g. POST), since get should be only used to retrieve data.
Use resources within the URL instead of start or getreports.
#Path("Reports")
public class ReportService {
#GET
#Path("/getreport/{parameters}")
#Produces(MediaType.TEXT_PLAIN)
public Response getReport(#PathParam("parameters") String parameters) throws KettleXMLException, KettleMissingPluginsException, JSONException, UnknownParamException {
JSONArray jsonarr;
return Response.status(200).entity("ok").build();
}
#GET
#Path("/start")
#Produces(MediaType.TEXT_PLAIN)
public Response startEngine() {
return Response.status(200).entity("ok").build();
}
}
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!";
}
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