I'm using robospice for my RESTful Communication to a backend. Here is the specification of the interface:
## carid [/cars]
### Register a car[POST]
+ Request (application/json)
+ Header
Accepted-Language: en
Authorization: Basic xyz
+ Body
{
"carId" : "ad885f2b-dbd3-49ad-aa34-a7671cf5e337"
}
+ Response 200 (application/json)
{
"magicQuestionDefined": true,
"newCar": true
}
+ Response 401
+ Response 404 (application/json)
{
"message" : "No car owner found"
}
+ Response 400
+ Response 500
Here ist the snipped of my listener, when I receive an error (in my case its only a problem with errors, the normal case returning a json works fine) :
private class RegisterCarRequestListener implements RequestListener<RegisterCarResult> {
@Override
public void onRequestFailure(SpiceException e) {
// e: Networkexception
// e.cause=RestClientException
// e.cause.detailMessage="Could not extract response: no suitable HttpMessageConverter found for response type [ch.mycarapp.lib.model.RegisterCarResult] and content type [text/html;charset=iso-8859-1]"
}
It all works fine until the Backend answers with a 401. In this case I except an Exception of type: HttpStatusCodeException for evaluating the exception status-code. The reason is "e.cause.detailMessage="Could not extract response: no suitable HttpMessageConverter found for response type [ch.mycarapp.lib.model.RegisterCarResult] and content type [text/html;charset=iso-8859-1]" but it is just a status code 401 that gets returned!
The service is created like this:
public class JsonCarService extends SpringAndroidSpiceService {
@Override
public CacheManager createCacheManager(Application application) throws CacheCreationException {
final CacheManager cacheManager = new CacheManager();
final JacksonObjectPersisterFactory jacksonObjectPersisterFactory = new JacksonObjectPersisterFactory(application);
cacheManager.addPersister(jacksonObjectPersisterFactory);
return cacheManager;
}
@Override
public RestTemplate createRestTemplate() {
final RestTemplate restTemplate = new RestTemplate();
// web services support json responses
final MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
// web Services support also Text Messages ( for some error cases
final StringHttpMessageConverter stringConverter = new StringHttpMessageConverter(Charset.forName("ISO-8859-1"));
final List<HttpMessageConverter<?>> listHttpMessageConverters = restTemplate.getMessageConverters();
listHttpMessageConverters.add(jsonConverter);
listHttpMessageConverters.add(stringConverter);
restTemplate.setMessageConverters(listHttpMessageConverters);
return restTemplate;
}
}
As you can see I have added a stringConverter too, but unfortunately it did not resolve the problem.
public class RegisterCarRequest extends BaseCarSpiceRequest<RegisterCarResult> {
RegisterCarInput input;
private final Context ctx;
public RegisterDeviceRequest(RegisterCarInput input, Context ctx) {
super(RegisterCarResult.class);
this.input = input;
this.ctx = ctx;
}
@Override
public RegisterCarResult loadDataFromNetwork() throws Exception {
final String url = ctx.getString(R.string.base_url) + "/cars";
final HttpEntity<?> requestEntity = new HttpEntity<Object>(input, getRequestHeaders());
final ResponseEntity<RegisterCarResult> response = getRestTemplate().exchange(url, HttpMethod.POST, requestEntity,
RegisterCarResult.class);
return response.getBody();
}
}
The pojo for sending to the server is this one:
public class RegisterCarInput {
private String carId;
public String getCarId() {
return carId;
}
public void setCarId(String carId) {
this.carId = carId;
}
}
the pojo for the Response is this Object:
public class RegisterCarResult {
private Boolean magicQuestionDefined;
private Boolean newCar;
public Boolean getMagicQuestionDefined() {
return magicQuestionDefined;
}
public void setMagicQuestionDefined(Boolean magicQuestionDefined) {
this.magicQuestionDefined = magicQuestionDefined;
}
public Boolean getNewDevice() {
return newCar;
}
public void setNewCar(Boolean newCar) {
this.newCar = newCar;
}
}
The Request gets called like this:
private void performRequest(RegisterCarInput input, User user) {
final RegisterCarRequest request = new RegisterCarRequest(input, getApplicationContext());
request.setAuthUser(user);
spiceManager.execute(request, new RegisterCarRequestListener());
}
Does anybody see a missing link for being able to parse different structures of responses?
Is it really necessary (for robospice) to receive httpstatuscodes (with a content-type=application/Json for errors with no payload and a 401 status code) ? http states in the Listener when the backend returns with a 401 ?
tia Luke
EDITED: I did some investigation and can now say that the backend returns a 403 when my basic authentication is wrong and sends following response:
+ Response 403 (text/html)
HTTP/1.1 403 Forbidden
Date: Wed, 12 Nov 2014 08:26:14 GMT
Server: Apache
Transfer-Encoding: chunked
Content-Type: text/html
97
<html>
<head>
<title>
403 Forbidden
</title>
</head>
<body>
<h1>Forbidden</h1>
You tried to access a page without having the required permissions.<br>
</body>
</html>
Unfortunately I don't have any influence to the behavior of the backend.
Facts:
- The calls are working when everything is ok (200) and receiving RegisterCarResult as json
- The calls are working when only a status code (401) (with no content in the response entity) is returned.
- The calls are failing when a status code (403) with html content is returned in the entity
So the question, which answer will solve my problem is this:
How can I handle RestResponses, which can have different content-types (application/json)(text/html) in their entities with ROBOSPICE?
- I want to send a Json with a request to the backend => ok
- I want to receive a 200 as response and a application/json in the response entity => ok
- I want to receive only a status code 401 with no content => ok
- I want to receive a status code 403 with text/html content => HOW?
All those responses(2,3,4) are possible answers to the one request(1)...