java webservice response object vs Produces - java

Im trying to java webservices and trying to follow couple of tutorial examples.
In one of the example, I see #Produces annotation being used to specify the type of response that is being returned.
Example:
#GET
#Produces(MediaType.TEXT_PLAIN)
public String sayPlainTextHello() {
return "Hello Jersey";
}
but in another case, I see the Response object is being used as a Response...
Example:
#GET
#Path("/{param}")
public Response getMsg(#PathParam("param") String msg) {
String output = "Jersey say : " + msg;
return Response.status(200).entity(output).build();
}
Question:
Which is the correct way to send a response back - Response object or #Produces annotation?
When both scenarios can be used?

The best way is to use the combination of both, all the times. Here's why
#Produces basically defines the CONTENT-TYPE (MIME-TYPE) of the Response. But that is pretty much all. It does not define the HTTP Status codes (on error/success/server error etc). #Produces annotation just makes your life easier by not explicitly specifying WHAT the content-type would be in the Response.
Now why to use Response instead of "String" as the return type? Here's an example
Let's take the following code into consideration:
#GET
#Produces(MediaType.TEXT_PLAIN)
public String sayPlainTextHello() {
try
{
return someRemoteServerApi.getHelloString();
}
catch(exception ex)
{
return getErrorMessageString();
}
}
Now lets say that the remote server api failed to return back, which cased some kind of an error. NOW, you want to return an error back to the client ALONG with an error code (because frankly speaking, clients will only care for the error message string when developing. Once the client is developed, they will solely base their apis on the HTTP return status code).
So, in the above example, lets say you return an error json String such as (the getErrorMessageString()) :
{
"error":500
"message": "something went wrong"
}
BUT, your actual HTTP status code will still be "200 OK", because the Response is still a String and the response went through just fine. How would a client know if something went wrong in the apis? He will have to parse the json string (on error) and determine what went wrong.
Basically, in the above case, your success and failure BOTH will have the same HTTP status return code, which is not very helpful to the clients. Instead, the above code should change something like:
#GET
#Produces(MediaType.TEXT_PLAIN)
public Response sayPlainTextHello() {
try
{
return Response.status(200).entity(someRemoteServerApi.getHelloString()).build();
}
catch(exception ex)
{
return Response.status(500).entity(getErrorMessageString()).build();
}
}
Finally,
In regards to answering both your questions:
1) #Produces has nothing to do with WHAT kind of Response will be sent. It just sets the content-type on whatever Response object you will be sending. JAX-RS, by default, will put 200 OK response, unless ofcourse, an exception occurs that is uncaught. Then it will probably return 500. Basically, you will be relying on JAX-RS to return your error codes for you, which is not very good. You, as an implementer, should determine what kind of error codes and error messages should be sent to the client that are MOST meaningful
2) I will ALWAYS use Response as the return type of the method, along with #Produces and #Consumes annotations for each method. If you believe that your full resource (all methods in java resource class) is using the same #Produces and #Consumes mime-type (in most cases it's application/json) then you can define this at the class level itself, something along the lines of:
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
#Path("/rest/someResource")
public class MyResource()
{
#Path("/{resourceId")
#GET
public Response getResource(#PathParam("resourceId") String id)
{
doStuffAndReturnResponse();
}
}
This will, by default, apply the #produces and #consumes annotation to ALL the resource methods and if you want something specific on some specific resource method, you can override it by just providing the annotation for that specific method.
I hope I have explained it good enough! Happy coding!

Related

How a rest API will return both Json and XML response depending on the input header?

The code is as follow.Where I am checking the content type extracted from the header then I want to write the code and return the response from the same method.
#POST
#Produces(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_XML)
public Response addMessage(Message message , #Context UriInfo uriInfo,
#HeaderParam ("content-type") String contentType) throws
URISyntaxException
{
//Conditional check based on the content type.
if(contentType.equals("application/json")) {
return json;
}else {
return xml;
}
}
How a rest API will return both Json and XML response depending on the input header?
First , your usage of multiple #Produces on same method is incorrect. A String[] can be specified for all types that you wish to produce with #Produces , Annotation Type Produces
And for your main question, I agree with vlumi's comment that ,
You should just return the Response built with the object to return,
and let JAX-RS handle the serialization into XML or JSON, depending on
which the client expects/prefers
i.e. let the framework do it for you depending on Accept header as specified by client as Raj has already mentioned in comments,
You have to pass the request header Accept: application/json or
application/xml
Jersey Multiple Produces

How to return proper error response for content-type text/csv?

I have a servlet where in general I return text/csv as response. So a plain comma separated string.
BUT: in case of exceptions, I'd want to show just some error text as response. But instead Spring generates a custom ResponseEntity object, then tries to convert this response to csv which obviously fails.
Is it possible to replace the requested format to format=json, and then just return the default Spring error response?
#RestController
public class CsvServlet {
#RequestMapping(value = "/test", produces = "text/csv")
#ResponseBody
public String errorCsv(HttpServletRequest request) {
return "some, plain, text";
}
}
Usage: localhost:8080/test?format=csv
When having spring.security.enabled=true, this will first validate the basic auth credentials. If they fail, spring will automatically redirect to /error servlet.
Thereby BasicErrorController.error() method comes in, catching the error and generating a ResponseEntity with error attributes like timestamp, exception, path, etc.
Problem: now an Object of type ResponseEntity is returned. This is fine as long as the format parameter is either ?format=json/xml. As the response can then be properly converted.
But in my special case, where I request ?format=csv, the conversation will fail, leading to:
Response could not be created:
org.springframework.web.HttpMediaTypeNotAcceptableException: Could not
find acceptable representation
This is partially true, moreover misleading because the user should directly see that the authentication credentials have been invalid. Because in general I'm accepting csv, but Spring and the mappers don't know how to convert a ResponseEntity to a plain format like csv.
Question: how can I preserve the original exception? So that I could just return a plain text error message. It would also be fine if I could return a application/json response in this case!
Workaround as follows for the moment: override the /error servlet handler, and if the ?format parameter is not either json/xml, just return the original http status error without a body.
Thus spring cannot fail on converting the body into plain format and return the response correctly (but without exception details in the body).
#Controller
public class WorkaroundBasicErrorController extends BasicErrorController {
//default constructor from ErrorMvcAutoConfiguration
public DefaultBasicErrorController(ErrorAttributes errorAttributes, ServerProperties serverProperties,
ObjectProvider<List<ErrorViewResolver>> errorViewResolversProvider) {
super(errorAttributes, serverProperties.getError(), errorViewResolversProvider.getIfAvailable());
}
#RequestMapping
#ResponseBody
#Override
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
String format = request.getParameter("format");
return (StringUtils.containsAny(format, "json", "xml"))
? super.error(request)
: new ResponseEntity<>(getStatus(request)); //neglect body for plain formats
}
}
Try to use a void #ExceptionHandler, and write error message directly in HttpResponse

REST WebServices - Is it a good or general practice to accept a JSON but return an XML or vice-versa?

I'm finding it a little bit tough to ask my question correctly to Google.
I'm building a REST Web Service, which can accept and return both XML and JSON media types. My client can decide what to send to my service (in case of a POST) by using the Content-Type header, and what it would like to recieve as a response by using the Accept header.
However, my question is, can I create a web service, such that it can accept only JSON (i.e., the client has to set Content-type=application/json), but the Service can return an XML only (the client has to set Accept=application/xml)?
While this can be technically possible, I would like to know if this is a good practice, and are there any general implementations of REST Webservices where a client can specify a particular Content-Type but a different Accept header.
Imagine I have implemented a Service like the one below:
#POST
#Path("/users")
#Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
#Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public User createUser(User user) {
/* Do something */
return user;
}
In the above case, a client can send the Post data as JSON, but expect to receive the response as XML, and vice versa.
Is this really a good practice? Should or should a REST Service not emphasize on returning content with the same Media Type as it accepts? Like, produce application/xml when it consumes application/xml and produce application/json when it consumes application/json and so on?
While technically possible, this is not good practice at all, especially if the client explicitely stated via its Accept header it only accepts application/json.
Instead of one method accepting both XML and JSON, you could make two methods on the same path, each one dedicated to one particular content-type:
#POST
#Path("/users")
#Consumes({MediaType.APPLICATION_JSON})
#Produces({MediaType.APPLICATION_JSON})
public User createUserJson(User user) {
/* Do something */
return user;
}
#POST
#Path("/users")
#Consumes({MediaType.APPLICATION_XML})
#Produces({MediaType.APPLICATION_XML})
public User createUserXml(User user) {
/* Do something */
return user;
}
See also the Accept header spec in RFC2616

Jersey - Redirect after POST to outside URL

I'm using Jersey to create REST API. I have one POST method and as a response from that method, the user should be redirected to a custom URL like http://example.com that doesn't have to be related to API.
I was looking at other similar questions on this topic here but didn't find anything that I could use.
I'd suggest altering the signature of the JAX-RS-annotated method to return a javax.ws.rs.core.Response object. Depending on whether you intend the redirection to be permanent or temporary (i.e. whether the client should update its internal references to reflect the new address or not), the method should build and return a Response corresponding to an HTTP-301 (permanent redirect) or HTTP-302 (temporary redirect) status code.
Here's a description in the Jersey documentation regarding how to return custom HTTP responses: https://jersey.java.net/documentation/latest/representations.html#d0e5151. I haven't tested the following snippet, but I'd imagine that the code would look something like this, for HTTP-301:
#POST
public Response yourAPIMethod() {
URI targetURIForRedirection = ...;
return Response.seeOther(targetURIForRedirection).build();
}
...or this, for HTTP-302:
#POST
public Response yourAPIMethod() {
URI targetURIForRedirection = ...;
return Response.temporaryRedirect(targetURIForRedirection).build();
}
This worked for Me
#GET
#Path("/external-redirect2")
#Consumes(MediaType.APPLICATION_JSON)
public Response method2() throws URISyntaxException {
URI externalUri = new URI("https://in.yahoo.com/?p=us");
return Response.seeOther(externalUri).build();
}

Null content-type when migrating from Jersey to RESTEasy.

So I wrote a sample REST resource that works like a charm in Jersey/Tomcat, but when I take it to RestEASY/Tomcat it blows. I mean really? what happened to working out of the box. Anyway a little frustrated. I get this error when trying to access the resource(http://localhost:7070/mg/mytest)
"content-type was null and expecting to extract a body"
7842 [http-7070-2] ERROR com.loyalty.mg.rest.exception.MGExceptionMapper - Error caught in the exception mapper -
org.jboss.resteasy.spi.BadRequestException: content-type was null and expecting to extract a body
at org.jboss.resteasy.core.MessageBodyParameterInjector.inject(MessageBodyParameterInjector.java:131)
at org.jboss.resteasy.core.MethodInjectorImpl.injectArguments(MethodInjectorImpl.java:98)
at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:121)
at org.jboss.resteasy.core.ResourceMethod.invokeOnTarget(ResourceMethod.java:247)
at org.jboss.resteasy.core.ResourceMethod.invoke(ResourceMethod.java:212)
at org.jboss.resteasy.core.ResourceMethod.invoke(ResourceMethod.java:202)
#Path("/mytest")
public class TestResource {
#GET
public Response getData()
I guess the question also is - is RestEASY any better than Jersey, this is just the start and I am getting errors. Should I just stick to Jersey?
Also already tried this as well :)
<context-param>
<param-name>resteasy.media.type.mappings</param-name>
<param-value>json : application/json, xml : application/xml</param-value>
</context-param>
The code that throws that exception looks like this:
final MediaType mediaType = request.getHttpHeaders().getMediaType();
if (mediaType == null) {
throw new BadRequestException(
"content-type was null and expecting to extract a body");
}
The problem seems to be that RestEASY cannot figure out a content type from the headers of the request that it received. This suggests that either that the content type in the request is bogus, or that there is a problem with the way that you have configured RestEASY.
I guess the question also is - is RestEASY any better than Jersey, this is just the start and I am getting errors. Should I just stick to Jersey?
I cannot answer that. However, I think you are being too quick to blame RestEASY for something that could be your code's fault.
A classic cause of this, is if you have code like this:
#GET
#Path("/foo/{bar}")
#Produces(MediaType.TEXT_HTML)
public Response foo(#PathParam("bar") String bar) {
...and you forget to annotate the bar argument with #PathParam. Then RestEasy thinks it should be reading bar from the body of the request, instead of from the URL path, and will chuck this exception.
That doesn't seem to be what's happening in your case, but I got the same exception, and this was the cause.
RestEASY vs Jersey is hard to say:
http://www.infoq.com/news/2008/10/jaxrs-comparison
Regarding your error, you can control the content type via annotations, what happens if you place #Produces annotation, for example:
#Produces("application/json")
#GET
public Response getData() {
...
}
Well I know this requested is dated, and so much on the internet old..in a year of two everything usually changes and works better. So RestEasy should not get a bad rap in comparison to other non-propertary RESTLET frameworks.
Actually I think JBoss RestEasy has the lightest footprint, it's not bloated with unnecessary *.jars, flexible, fully certified JAX-RS implementation, complete and its ease of use is beyond comparison.
Some eluded, that a GET request should not expect a Content_Type on the request, (And I agree), but with a every GET request one must indicate what you intend on sending back to the requestor? Right! (will it be JSON, XML, plain text, XML and a sheetsheet, multi-part, etc). Well RestEasy, JBoss's framework addresses this with annotation as shown below, and configurable per URL REST request. Therefore, therein is your answer
#GET
#Path("/echo/{message}")
#Produces("text/plain")
public String echo(#PathParam("message")String message){
return message;
}
#GET
#Path("/employees")
#Produces("application/xml")
#Stylesheet(type="text/css", href="${basepath}foo.xsl")
public List<Employee> listEmployees(){
return new ArrayList<Employee>(employees.values());
}
#GET
#Path("/employee/{employeeid}")
#Produces("application/xml")
public Employee getEmployee(#PathParam("employeeid")String employeeId){
return employees.get(employeeId);
}
#GET
#Path("/json/employees/")
**#Produces("application/json")**
public List<Employee> listEmployeesJSON(){
return new ArrayList<Employee>(employees.values());
}
a GET request must not have a body, and an application must not expet a Content-Type header.
If this is a bug of RestEASY, it makes one wonder how many people really are using the software.
EDIT
RFC2616 $4.3
A message-body MUST NOT be included in
a request if the specification of the
request method (section 5.1.1) does
not allow sending an entity-body in
requests.
A server SHOULD read and forward a
message-body on any request; if the
request method does not include
defined semantics for an entity-body,
then the message-body SHOULD be
ignored when handling the request.
The GET method does not "does not allow sending an entity-body in request" therefore a GET request COULD have a body. But GET "does not include defined semantics for an entity-body" therefore the body should be ignored anyway.
In any case, RestEASY should not have required the presence of Content-Type in a GET request.

Categories

Resources