I am following the jersey tutorial here to figure out how one would produce multiple mime outputs. From their website, this is the recommended way:
#GET
#Produces({"application/xml", "application/json"})
public String doGetAsXmlOrJson() {
...
}
What I cannot figure out is how to abstract the #Produces away, so that my code is more welcoming to additional mime types it can produce. Say for example I have 500 methods that all have this annotation:
#Produces({"application/xml", "application/json"})
If I get a requirement to add kml as a mime type, editing and replacing all of those values would certainly be time consuming.
#Produces({"application/xml", "application/json", "application/kml"})
Is it possible to architect #Produces more efficiently so that I do not have this issue down the road?
Understanding the #Produces annotation
The #Produces annotation is used to specify the MIME media types of representations a resource can produce and send back to the client.
The JAX-RS runtime compares value of the Accept header of an incoming request with the value of the #Produces annotation to match the resource method that will handle such request.
In the absence of the #Produces annotation, support for any media type (*/*) is assumed. For a complete reference, check the JAX-RS specification.
What you can do
To reduce the amount of #Produces annotations in your code, you could annotate the resource classes instead of annotating the resource methods.
Tip: To reduce typographical errors you could use constant values:
#Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
Have a look at the MediaType class.
Related
Using jersey jersey.java.net How do I set JSON as the default serialization instead of XML when there is no accept header or .xml suffix is in the URI?
You can assign the quality index to each media type in #Produces annotation. I.e.you can do the following to make Jersey prefer JSON if both XML and JSON are allowed:
#Produces({"application/json;qs=1", "application/xml;qs=.5"})
You should be able to set the #Produces annotation to specify the return format like so:
#Produces( { "application/json" })
How come there is no accepts header?
You can specify preference of generation by specifying media types in your order of preference in the #Produces annotation.
#Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
In the above code since "application/json" comes first, if no accept header is specified in the request Jersey will default to generating JSON response.
Using qs (as suggested by Martin) makes the preference more explicit, but its a bit more complicated to understand.
I'm new in jersey rest service and I want to understand in this example the utility of adding #Consumes annotation to a delete method in this case this is the code it's work well (in a video ), is the #Consumes annotation optional here ? Thanks in advance
#path("activities")
public class ActivityResource {
#DELETE
#Path("{activityId}")
#Consumes(MediaType.APPLICATION_JSON)
#Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
public Response delete(#PathParam("activityId")String activityId) {
activityRepository.delete(activityId);
return Response.ok().build() ;
}
}
Is the #Consumes annotation optional here ?
Yes, I would even say that it is not needed as you have only one parameter and it is a PathParam which means that it will be extracted from the path.
The annotation #Consumes is used to indicate the JAX-RS implementation how to dynamically parse/deserialize the body of your request in order to have it as parameter in a more convenient type.
For example:
#POST
#Consumes("application/xml")
public void registerUser(User user) {
...
}
In this example, we indicate that the body of the request is of type application/xml, the JAX-RS implementation will then parse the body's content as an XML to finally get an instance of User.
NB: The HTTP method used has no effect on whether or not #Consumes is needed, only the need to parse the body matter.
A DELETE should not be interested in anything that is in the request body. It should only identify the resource to be deleted based on the URI.
Remove the #Consumes, it is wrong here.
Also think about returning a HTTP status 204 No Content instead of 200 OK. After deleting a resource, there is nothing to return. You should also remove the #Produces because of this.
#GET
#Produces(MediaType.APPLICATION_JSON)
public String getRscSubTypes(){
return AddResourceMysql.getRscSubType();
}
#GET
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public String getDbTypes() {
return AddResourceMysql.getDbType();
}
This is returning the following exception:
org.glassfish.jersey.server.model.ModelValidationException:
Validation of the application resource model has failed during application initialization.
Can you please help me?
How request matching works
Definitely, you can have more than one method annotated with #GET in the same class. However, your current definition is ambiguous.
For more clarification, have a look at the JAX-RS 2.0 specification:
3.7.2 Request Matching
A request is matched to the corresponding resource method or sub-resource method by comparing the normalized request URI, the media type of any request entity, and the requested response entity format to the metadata annotations on the resource classes and their methods. [...]
How to fix it
You need change your method annotations to ensure you have no ambiguity. To do it, you can play with the following annotations:
HTTP method: #GET, #POST, #PUT, #DELETE, #HEAD and #OPTIONS
Request URI: #Path
Media type of any request entity: #Consumes
Requested response entity format: #Produces
To fix it, for example, you can just add a #Path annotation with different values to each method.
If you want to define multiple resource methods, which handle GET requests for the same MIME type, within the same class, you have to specify a different subpath for the methods:
#Path("rcsubtypes")
#GET
#Produces(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public String getRscSubTypes()
{
return AddResourceMysql.getRscSubType();
}
#Path("dbtypes")
#GET
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public String getDbTypes()
{
return AddResourceMysql.getDbType();
}
The path, specified in the #Path annotation of this method, is a subpath of the path specified in the #Path annotation of the class, which is a subpath of the path you defined for your application.
To explain your behaviour, that always the second method is called, if there is no #Consumes annotation present on the first method: #Consumes defines which media type (set in the Content-Type header of the request) can be accepted by the method. Without a #Consumes annotation all requests are accepted, but i think, if a method specifies an accepted media-type, it will be preferred.
The matching section in the jersey documentation: 3.1. Root Resource Classes
I am currently integrating Swagger (swagger-jaxrs artifact in version 1.5.7) into our existing JAX-RS REST application. After adding Swagger I added the #Api annotation to our interface class and already got a documentation which doesn't look to bad.
Unfortunately Swagger does not honor the #Produces annotations on my API methods until I annotate those methods with #ApiOperation:
Doesn't list text/plain as the returned media type:
#GET
#Path("/overallStatus")
#Produces(MediaType.TEXT_PLAIN)
public String getOverallStatus() {
}
Does list it:
#GET
#Path("/overallStatus")
#Produces(MediaType.TEXT_PLAIN)
#ApiOperation(value = "Get the overall system status")
public String getOverallStatus() {
}
Is there a way to have the media type in the Swagger output without adding #ApiOperation to all of them? Since the required information is already there, I don't see why I would need it.
You have to add the #ApiOperation to your JAX-RS endpoints since methods without the #ApiOperation annotation will be ignored as per the Swagger Wiki page:
Only methods that are annotated with #ApiOperation will be scanned and added the Swagger definition.
You can find more information here: https://github.com/swagger-api/swagger-core/wiki/Annotations-1.5.X#apioperation
I feel embarrassed to ask: but what is the right combination of annotations for a resteasy service method that will unmarshall a custom type?
I am able to successfully generate json and xml from methods which return custom types (with jaxb annotations), but I have failed to turn these types into method parameters. All the examples around the web seem to pass simple types such as strings.
Documentation claims that resteasy can unmarshall json and xml to annotated types, but how? The following signature requires an object with a string parameter taking constructor, which is not what I'm looking for.
#GET
#Path("/somepath/ontheserver/settestchild")
#Produces("application/xml")
String getQueryParam(#QueryParam("testchild")TestChild param);
TestChild has JAXB annotations, but I want resteasy to unmarshal incoming xml to an instance of this object, which is not happening. Am I missing something here?
You can use the #Consumes annotation:
#PUT
#Path("/")
#Consumes(MediaType.APPLICATION_XML)
#Produces(MediaType.APPLICATION_XML)
TestChild addTestChild(TestChild testChild);