Multiple return type in Jersey Client request
I'm using Jersey Client API in the following way :-
User user = webRsrc.accept(MediaType.APPLICATION_XML).post(User.class, usr);
So I'm expecting the response in object of User class which is a JAXB annotated class. However, at times I might also get an error xml and for that I've created a JAXB class ErrorResponse.
Now the problem is that if my request returns an object of ErrorResponse instead of User how can I handle that ?
I tried like this -
ClientResponse response=null;
try {
response = webRsrc.accept(MediaType.APPLICATION_XML).post(ClientResponse.class,usr);
User usr = response.getEntity(User.class);
}catch(Exception exp)
{
ErrorResponse err = response.getEntity(ErrorResponse.class);
}
But when I try to use getEntity() in catch block, it throws following exception
[org.xml.sax.SAXParseException: Premature end of file.]
at com.sun.jersey.core.provider.jaxb.AbstractRootElementProvider.readFrom(AbstractRootElementProvider.java:107)
at com.sun.jersey.api.client.ClientResponse.getEntity(ClientResponse.java:532)
at com.sun.jersey.api.client.ClientResponse.getEntity(ClientResponse.java:491) .....
Seems like after ca开发者_JS百科lling getEntity() once, the inputstream is exhausted.
I think you missed a point in the whole "REST way of thinking".
Short answer: yes, you can only call getEntity once. You need to check the returned HTTP status to know what entity you should get.
On the server side:
- When designing a REST API, one should always use appropriate status codes regarding the HTTP RFC.
- For that matter, please consider using the ExceptionMapper interface (here's an example with a "NotFoundException"
So, now your server returns either "HTTP status OK - 200" with a User object, or an error status with an error object.
On the client side:
You need to check the return status and adapt your behavior according to the API spec. here's a quick and dirty code sample:
ClientResponse response=null;
response = webRsrc.accept(MediaType.APPLICATION_XML).post(ClientResponse.class,usr);
int status = response.getStatus();
if (Response.Status.OK.getStatusCode() == status) {
// normal case, you receive your User object
User usr = response.getEntity(User.class);
} else {
ErrorResponse err = response.getEntity(ErrorResponse.class);
}
NB: depending on the status code returned, this error could be very different (thus needing very different behavior):
- client error 40X: your client request is wrong
- server error 500: an unexpected error occured on the server side
This kind of code could be used to manage Error message or business message in the response :
protected <T> T call(String uri, Class<T> c) throws BusinessException {
WebResource res = new Client().create().resource(url);
ClientResponse cresp = res.get(ClientResponse.class);
InputStream respIS = cresp.getEntityInputStream();
try {
// Managing business or error response
JAXBContext jCtx = JAXBContext.newInstance(c, BeanError.class);
Object entity = jCtx.createUnmarshaller().unmarshal(respIS);
// If the response is an error, throw an exception
if(entity instanceof BeanError) {
BeanError error = (BeanError) entity;
throw new BusinessException(error);
// If this not an error, this is the business response
} else {
return (T) entity;
}
} catch (JAXBException e) {
throw(new BusinessException(e));
}
}
If you can't change the server code, you can use a ReaderInterceptor
. This does have some limitations but you will at least be able to get the contents of the error response object.
I'm using JSON but the same principles apply:
public class MyException extends RuntimeException {
private final ErrorResponse content;
public MyException(ErrorResponse content) {
this.content = content;
}
public ErrorResponse getContent() {
return content;
}
}
public class ErrorResultInterceptor implements ReaderInterceptor {
private static final ObjectReader JSON = new ObjectMapper().readerFor(ErrorResponse.class);
@Override
public Object aroundReadFrom(ReaderInterceptorContext context) throws IOException, WebApplicationException {
byte[] buffer = context.getInputStream().readAllBytes();
context.setInputStream(new ByteArrayInputStream(buffer));
try {
return context.proceed();
} catch (UnrecognizedPropertyException ex) {
try {
throw new MyException(JSON.readValue(buffer));
} catch (IOException errorProcessingEx) {
// Log errorProcessingEx using your preferred framework
throw ex;
}
}
}
}
Then use this as part of the invocation:
client.register(ErrorResultInterceptor.class);
// Oher client setup
try {
// Make the client call
// Handle OK case
} catch (ResponseProcessingException ex) {
if (ex.getCause() instanceof MyException) {
ErrorResponse respone = ((MyException)ex.getCause()).getContent();
// Handle error response
} else {
throw ex;
}
}
Limitations are:
- The response has to be buffered which could be a memory overhead
- The client call to get hold of the error result is very clunky
精彩评论