Jersey JAX-RS MessageException when using a ResponseBuilder - exception

When I throw a WebApplicationException in my service like the following, I get a 404 response code which is what I'm expecting
throw new WebApplicationException(Response.Status.NOT_FOUND);
When I attempt to put a little more information in my exception via a ResponseBuilder, I'm getting a 500 response code. Here's my enhanced code and the response message I get back:
List<Error> errors = new ArrayList<>();
errors.add(new Error(ErrorCode.PDF_GENERATION_ERROR));
ErrorResponse errResponse = new ErrorResponse();
errResponse.setErrors(errors);
throw new WebApplicationException(Response.status(Response.Status.NOT_FOUND).entity(errResponse).build());
javax.ws.rs.WebApplicationException:
com.sun.jersey.api.MessageExceptio n: A message body writer for Java
class idexx.ordering.rest.common.types.ErrorRe sponse, and Java type
class idexx.ordering.rest.common.types.ErrorResponse, and MIME media
type application/octet-stream was not found
My REST service has the following method signature:
#Produces("application/pdf")
#GET
#Path("/{id}")
public Response getPdfById(#PathParam("id") final Long id) {
Is there anyway I can get that extra error code information into the response when I throw a WebApplicationException?

Try this:
String errResponse = "convert your errors to text";
throw new WebApplicationException(Response.status(Response.Status.NOT_FOUND).entity(errResponse).build());
Probably this will work because JAX-RS can convert basic types, but to convert List<Error> you need to register a custom MessageBodyWriter.

Related

Should a server always send a JSON object as an http response?

I'm working on a node.js server using express and a android native app, using Retrofit 1.9.
For a login API that returns only a true/false answer to the client, should JSON still be used?
As I see it, the server has only to send a status code response:
if(isLegal) {
res.sendStatus(200);
dbConnector.updateUser(token);
}
else{
console.log('Token is not legal');
res.sendStatus(403);
}
But the Retrofit framework tries to convert the response to JSON, which makes me think I must send a JSON object with the answer, though it seems weird.
My retrofit restClient:
public class RestClient {
private static final String URL = SessionDetails.getInstance().serverAddress;
private retrofit.RestAdapter restAdapter;
private ServerAPI serverAPI;
public RestClient() {
restAdapter = new retrofit.RestAdapter.Builder()
.setEndpoint(URL)
.setLogLevel(retrofit.RestAdapter.LogLevel.FULL)
.build();
serverAPI = restAdapter.create(ServerAPI.class);
}
public ServerAPI getService() {
return serverAPI;
}
}
And usage:
restClient.getService().login(token.getToken(), token.getUserId(), new Callback<Void>() {
#Override
public void success(Void aVoid, Response response) {
Log.d("Chooser", "Successful login on server.");
}
#Override
public void failure(RetrofitError error) {
error.printStackTrace();
Log.d("Chooser", "Login failed on server.");
}
});
Using it as it is results with the following error:
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING
There are many topics on this issue but no certain answer about the correct (or better) method to use.
Any ideas about the best implementation in these cases?
Sending an empty body with your HTTP response is perfectly legal and some clients may care only about the response status but some clients may expect to get a response so sending a body never hurts and sometimes may be useful.
You can include a JSON response in addition to the HTTP response status:
// Express 4.x:
res.status(403).json({error: 'Token is not legal'});
// Express 3.x:
res.json(403, {error: 'Token is not legal'});
Such an error message can be very useful for the client development. You can get 403 for many reasons, illegal token, expired token, a legal not expired token but for the wrong user that doesn't have some privilege - adding a specific error message in addition to the HTTP response code can tell the client what exactly went wrong and allows the client-side code to show a better error message to the user.
Also, note that true and false are also valid JSON.

No Suitable MessageConverter

I have a spring boot application and am testing integration test. My REST service produces JSON and I can confirm it when testing it in postman.
But when I make a getForObject call by restTemplate:
#Test
public void testGetObject() {
Pet pet = restTemplate.getForObject("http://localhost:9000/pets/10000", User.class, Collections.emptyMap());
assertThat(pet.getName(), is("Bobby"));
}
It fails with following error:
Could not extract response: no suitable HttpMessageConverter found for response type [class petstore.entity.User] and content type [text/html;charset=utf-8]
I read lots of posts in stackoverflow, and having that restTempalte itself has MappingJackson2HttpMessageConverter as one of default converters which has JSON as default media type then I should not get this error.
Is there anything I am missing here?
Well, the message is pretty indicative - you're getting text/html as a response type, make your endpoint return application/json. If you're using Spring MVC then you can do it by adding the produces parameter to the annotation:
#RequestMapping(value = "/pets/{id}", method = RequestMethod.GET, produces = "application/json")
Or, if you're using Jersey, add this annotation to your method:
#Produces("application/json")

JSON + Spring 3.1

Hi I am trying to write small app with REST Json. I have some method that returns ArrayList of entity objects. And I am doing that:
#RequestMapping(value="/workers/", method = RequestMethod.GET)
public #ResponseBody ArrayList<Workers> showAllEmployes() throws Exception
{
ArrayList<Workers> workers = new ArrayList<Workers>();
workers = (ArrayList<Workers>) spiroService.getAllWorkers();
return workers;
}
And after this I got:
HTTP Status 500. The server encountered an internal error that prevented it from fulfilling this request.
When I try to return primitive data type then all is ok. I have nothing in server logs. And I have necessary imports. Please some tip.
Seems you have issue in produce json format, try this.
#RequestMapping(value = "/workers/", method = RequestMethod.GET,
produces={MediaType.APPLICATION_JSON_VALUE})

Spring MVC, force JSON response in plain request

I am using Spring 3.0.6 and i have a single controller for uploading files to the server. I am using a script to upload using XmlHttpRequest for browsers that support it while the rest of the browsers submit a (hidden) multipart form. The problem however is that when a form is submitted it sends the following header:
Accept text/html, application/xhtml+xml, */*
I figure that due to this header the Controller which is marked with #ResponseBody replies with the response been converted to XML instead of JSON. Is there a way to get around this without hacking the form submit request?
You can force JSON using #RequestMapping(produces = "application/json"). I don't remember if this is available in 3.0 but it is available in 3.1 and 3.2 for sure.
As others noted, Jackson needs to be on your classpath.
Thank you! I was having exactly the same issue and your post resolved my problem.
On the UI I'm using JQuery with this file upload plugin:
https://github.com/blueimp/jQuery-File-Upload/wiki
Here's my completed method (minus the biz logic):
#RequestMapping(value = "/upload", method = RequestMethod.POST)
public void handleUpload( #RequestParam("fileToUpload") CommonsMultipartFile uploadFile, ServletResponse response){
List<UploadStatus> status = new ArrayList<UploadStatus>();
UploadStatus uploadStatus = new UploadStatus();
status.add(uploadStatus);
if(uploadFile == null || StringUtils.isBlank(uploadFile.getOriginalFilename())){
uploadStatus.setMessage(new Message(MessageType.important, "File name must be specified."));
}else{
uploadStatus.setName(uploadFile.getOriginalFilename());
uploadStatus.setSize(uploadFile.getSize());
}
ObjectMapper mapper = new ObjectMapper();
try {
JsonGenerator generator = mapper.getJsonFactory().createJsonGenerator(response.getOutputStream(), JsonEncoding.UTF8);
mapper.writeValue(generator, status);
generator.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
If you want a JSON response, you can easily accomplish that by having the Jackson JARs on your classpath. Spring will auto-magically pick up on them being there and will convert your #ResponseBody to JSON.
I made it work by getting rid off #ResponseBody and instead doing manually the conversion (always using Jackson), i.e.
Response r = new Response();
ObjectMapper mapper = new ObjectMapper();
JsonGenerator generator = mapper.getJsonFactory().createJsonGenerator(response.getOutputStream(), JsonEncoding.UTF8);
try {
File f = uploadService.getAjaxUploadedFile(request);
r.setData(f.getName());
} catch (Exception e) {
logger.info(e.getMessage());
r = new Response(new ResponseError(e.getMessage(), ""));
}
mapper.writeValue(generator, r);
generator.flush();
Does anyone know another way? I tried setting up a ContentNegotiatingViewResolver but i don't want to break any other controllers by assigning all hmtl to json. Also, i tried to do it for this method only via a custom viewresolver but when i setup a jsonview and use BeanNameViewResolver although the response is correctly converted to JSON the server throws an
HttpRequestMethodNotSupportedException: exception, with Request method 'POST' not supported and set status to 404.

How to override response header in jersey client

I have a jersey client that I am trying to unmarshall a response entity with. The problem is the remote web service sends back application/octet-stream as the content type so Jersey does not know how to unmarshall it (I have similar errors with text/html coming back for XML and such). I cannot change the web service.
What I want to do is override the content-type and change it to application/json so jersey will know which marshaller to use.
I cannot register application/octet-stream with the json marshaller as for a given content type I actually might be getting back all kinds of oddities.
As laz pointed out, ClientFilter is the way to go:
client.addFilter(new ClientFilter() {
#Override
public ClientResponse handle(ClientRequest request) throws ClientHandlerException {
request.getHeaders().putSingle(HttpHeaders.CONTENT_TYPE, "application/json");
return getNext().handle(request);
}
});
I'm not well-versed in the Jersey client API, but can you use a ClientFilter to do this? Perhaps you could add a property to the request via ClientRequest.getProperties().put(String, Object) that tells the ClientFilter what Content-Type to override the response with. If the ClientFilter finds the override property, it uses it, otherwise it does not alter the response. I'm not sure if the ClientFilter is invoked prior to any unmarshalling though. Hopefully it is!
Edit (Have you tried something like this):
public class ContentTypeClientFilter implements ClientFilter {
#Override
public ClientResponse handle(ClientRequest request) throws ClientHandlerException {
final ClientResponse response = getNext().handle(request);
// check for overridden ContentType set by other code
final String overriddenContentType = request.getProperties().get("overridden.content.type");
if (overriddenContentType != null) {
response.getHeaders().putSingle(HttpHeaders.CONTENT_TYPE, overriddenContentType);
}
return response;
}
}
Under Java 8 and Jersey 2 you can do it with a lambda:
client.register((ClientResponseFilter) (requestContext, responseContext) ->
responseContext.getHeaders().putSingle("Content-Type", "application/json"));