I need to implement a webservice that uses the first query parameter to identify the operation, i.e. the client call would be something like: http://localhost:8080/ws/oper
I think Claudio is correct - you could use Jersey, but you'd be on your own to handle the query parameters since it only matches on the path.
You could inject a UriInfo and pull the query parameters out of that:
@Path("/operation") public class Operation { @Context protected UriInfo info; @GET public String operation() { if (info.getQueryParameters().containsKey("create")) // do stuff else if (info.getQueryParameters().containsKey("info")) // other stuff }
}
You could switch from Jersey to another framework. I believe that Spring can route to multiple methods based on query parameters.
As you mentioned, perhaps you could write something that is a bit more standard and then remap the requests to that. For example, you could use a Servlet filter or a front end server like Apache httpd or nginx to modify the request.
Thinking in terms of resources what is it that these operations are acting on? Customer accounts, movies, stock trades, etc. For arguments sake, let's say it's "Foo". You might go with something like this:
@Path("/foo") public class FooResource { @Context protected UriInfo info; @GET @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) public Foo getById(@QueryParam("id") int id) { // get Foo by id Foo = .... // return an instance of Foo and let Jersey convert it to XML // or JSON depending on the "Accept" header that the client sent return foo; } @POST @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) public Response create(Foo instance) { // let Jersey deserialize the request body from JSON or XML. // save the instance however you want int id = fooService.save(instance); // return a 204 "created" with a "Location" header URI location = info.getAbsolutePathBuilder().path("{id}").build(id); return Response.created(location).build(); }
}
It sounds like your URI structure is mandated by someone else so this might not be an option for you. If you do proceed with the current URI structure, there's one major pitfall that you should be aware of.
According to the HTTP 1.1 spec, GET requests should be idempotent. It appears that your current design is creating new server side instances with GET requests. There's potential for breakage... intermediate proxies or web browsers could cache responses to your GET requests and prevent new instances from being created.
I see this is already answered, but I had a similar problem and achieved a solution using sub-resources.
package com.example.ws;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
@Path("/")
public class Operation {
@Path("/")
public Object sub(@QueryParam("operation") String operation)
{
if ("info".equals(operation))
return new InfoOp();
if ("create".equals(operation))
return new CreateOp();
//handle via error code
}
public class InfoOp {
@GET
public String info() {
return "info";
}
}
public class CreateOp {
@GET
public String create(@QueryParam("name") String name) {
return "create: " + name;
}
}
}
I think method selection is based on the non parameter part of the URI. Can't you design this in a way the client invoke http://localhost:8080/ws/operation/info
and http://localhost:8080/ws/operation/create?name=something
instead?
That would be easy to achieve:
@Path("/operation")
public class Operation {
@GET
@Path("info")
public String info() {
return "info";
}
@GET
@Path("create")
public String create(@QueryParam("name") String name) {
return "create";
}
}