RESTEasy - Reuse method parameters when @GET/@POST
Solution 1
Quoting the book RESTful Java with JAX-RS:
JAX-RS defines five annotations that map to specific HTTP operations:
@javax.ws.rs.GET
@javax.ws.rs.PUT
@javax.ws.rs.POST
@javax.ws.rs.DELETE
@javax.ws.rs.HEAD
(...)
The@GET
annotation instructs the JAX-RS runtime that this Java method will process HTTP GET requests to the URI. You would use one of the other five annotations described earlier to bind to different HTTP operations. One thing to note, though, is that you may only apply one HTTP method annotation per Java method. A deployment error occurs if you apply more than one.
(The text above was written by the creator of RESTEasy.)
And, in short, as RESTEasy complies with JAX-RS, you can't annotate a method with more than one HTTP verb.
If you are not convinced, looking at the @GET
annotation, you can see that it is only a meta-annotation to @HttpMethod
.
/**
* Indicates that the annotated method responds to HTTP GET requests
* @see HttpMethod
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@HttpMethod(HttpMethod.GET)
public @interface GET {
}
And if you open the @HttpMethod
, check the javadoc (It is an error for a method to be annotated with more than one annotation that is annotated with HttpMethod
.):
/**
* Associates the name of a HTTP method with an annotation. A Java method annotated
* with a runtime annotation that is itself annotated with this annotation will
* be used to handle HTTP requests of the indicated HTTP method. It is an error
* for a method to be annotated with more than one annotation that is annotated
* with {@code HttpMethod}.
*
* @see GET
* @see POST
* @see PUT
* @see DELETE
* @see HEAD
*/
@Target({ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HttpMethod
{
So, that's it, you can't have them both in the same method.
That said, you could, if you really really must, achieve that through a PreProcessInterceptor
that would be is called before a JAX-RS method.
Still, this way is much more complicated (as you'd have to parse the parameters yourself) and much less maintainable (services being delivered at interceptors!?).
Bottom line, to my knowledge, your solution is the optimal.
Check what I'm saying in the test below:
public class QueryAndFormParamTest {
@Path("/")
public static class InterceptedResource {
@GET
//@Path("/stuff") // uncomment this and it will not work
public String otherService(@QueryParam("yadda") String name){
return "Im never called in this example" + name;
}
}
public static class MyInterceptor implements PreProcessInterceptor, AcceptedByMethod {
@Override
public boolean accept(Class declaring, Method method) {
System.out.println("Accepted by method "+method.getName());
// you can check if this interceptor should act on this method here
return true; // it'll act everytime
}
@Override
public ServerResponse preProcess(HttpRequest request, ResourceMethod method)
throws Failure, WebApplicationException {
// parsing form parameters
if (request.getHttpHeaders().getMediaType() != null && request.getHttpHeaders().getMediaType().isCompatible(MediaType.valueOf("application/x-www-form-urlencoded"))) {
MultivaluedMap<String, String> formParameters = request.getFormParameters();
if (formParameters != null) {
for (String key : formParameters.keySet()) {
System.out.println("[FORM] "+key + ": "+formParameters.get(key));
}
}
}
// parsing query parameters
MultivaluedMap<String, String> queryParameters = request.getUri().getQueryParameters();
if (queryParameters != null)
for (String key : queryParameters.keySet()) {
System.out.println("[QUERY] "+key + ": "+queryParameters.get(key));
}
String responseText = "do something: " + request.getUri().getQueryParameters().getFirst("test");
return new ServerResponse(responseText, 200, new Headers<Object>());
}
}
@Test
public void test() throws Exception {
Dispatcher dispatcher = MockDispatcherFactory.createDispatcher();
dispatcher.getProviderFactory().getServerPreProcessInterceptorRegistry().register(new MyInterceptor());
dispatcher.getRegistry().addSingletonResource(new InterceptedResource());
MockHttpRequest request = MockHttpRequest.get("/?test=someStuff");
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
System.out.println(response.getContentAsString());
Assert.assertEquals("do something: someStuff", response.getContentAsString());
}
}
Solution 2
You cannot have a REST-method, which is annotated with more than one of the @GET
, @POST
, @PUT
, @DELETE
annotations, because this is conflict with the HTTP specification.
Also, if myMethod2
just returns the result of myMethod
, you can use the only one of them in you application (for instance, myMethod
), because basically myMethod2
just reads and retrieves data from the server, but doesn't update anything. This means it's not proper to be annotated with @POST
, since it doesn't update anything on the server. If annotated with @POST
, it will still work, but it won't meet the HTTP specs.
There's a mapping between the CRUD operations and the HTTP verbs. In the cases when you create a resource on the server, you have to use PUT
or POST
and in the cases when you want the read some resource from the server, you're supposed to use GET
. All the cases are as follows:
Create = PUT with a new URI
POST to a base URI returning a newly created URI
Read = GET
Update = PUT with an existing URI
Delete = DELETE
Solution 3
For binding the multiple queryParam into single object we need add @Form as a arguments in response method. It works fine for us.
@GET
@Path("/")
@Produces("application/json")
@Consumes("application/json")
public Response search(@Form CatalogSearchRequest reqObject)
{
System.out.println("Entered into service"+reqObject.getAttribute());
}
POJO CLASS should contain @QueryParam("") for every attributes
for example:
@QueryParam("pageSize")
public Integer pageSize;
@QueryParam("page")
public Integer page;
public Integer getPageSize() {
return pageSize;
}
public void setPageSize(Integer pageSize) {
this.pageSize = pageSize;
}
public Integer getPage()
{
return page;
}
public void setPage(Integer page)
{
this.page = page;
}
regards,
Prasanna.
Related videos on Youtube
gustavohenke
Developer and entrepreneur of the web. I love to invest my time, knowledge and forces to help build a more open, innovative, functional and liberal web. You'll likely find me on javascript related tags, but I obviously love html5 and css3 as well!
Updated on June 04, 2022Comments
-
gustavohenke over 1 year
I have a method in my RESTEasy service which I'd like to use as both
@GET
/@POST
, and its data may come from both query string and request body.@GET @POST public String myMethod(@QueryParam("param1") @FormParam("param1") String param1, @QueryParam("param2") @FormParam("param1") String param2) { // ...do things }
However, I haven't found yet a way to do this without doing the following:
@GET public String myMethod(@QueryParam("param1") String param1, @QueryParam("param2") String param2) { // ...do things } @POST public String myMethod2(@FormParam("param1") String param1, @FormParam("param2") String param2) { return this.myMethod(param1, param2); }
Does anyone knows how to make the first example work, or another approach with the least code possible?
-
eiden over 10 yearsWhat's bothering you about this limitation? Is it the fact that you need to duplicate the annotations?
-
-
gustavohenke over 10 yearsit's strange, I don't get any deployment error in the method I have with both
@GET
and@POST
annotations; also, I'm able to use it, but if@QueryParam
is defined, it's always used instead of@FormParam
.. -
acdcjunior over 10 yearsI'd have to check, but what may be happening is it is failing gracefully by using only the first annotation (both in the method and the parameter) and ignoring the rest. Still, this is unpredictable behaviour, since the spec is very clear about how more than one should not be used.
-
gustavohenke about 10 yearsThanks for the reply, but this doesn't answer the question; also, it has already been answered anyway :)
-
Zach Lysobey about 10 yearsWhile the spirit of this post is good.. "You cannot have a REST-method, ... annotated with more than one... because this is conflict with the HTTP specification." isn't true. You cannot because RESTEasy doesn't allow it. I wish it did... because sometimes it is (or would be) convenient to break with the spirit of HTTP to get a job done.
-
Edoardo over 7 yearseven though it is true that this does not answer the question directly, it should be noted that it answers the reason that triggers the question in the first place: one would like to have simultaneously
@get
and@post
methods mostly to reuse the entry parameters of the query.