Android Retrofit - 404 errors on working URLs - android

I have an Android app that has this particular retrofit code that has not been touched in years. All of a sudden we started seeing 404 errors when making certain GET calls. When debugging through it, looking at the response, it looks like this:
Response{protocol=h2, code=404, message=, url=https://www.mainstreetartsfest.org/feed/artist/getAllCategories}
When I copy that URL into a browser, it loads the data fine. We are doing something similar on iOS with no issues, but on Android, it's failing now. I am completely stumped as to any reasons why this would have started occurring. Any ideas? The interface for the Retrofit API looks like this:
#GET("feed/artist/getAllCategories")
Call<List<Category>> getAllCategories();
Our base URL is defined as follows:
public static final String BASE_URL = "http://www.mainstreetartsfest.org/";
Let me know if more info is needed.

I have try this api on postman and this api is giving 404 Not Found. my backhand team says that there is some mistake in backhand side .

Something alike this should work - and won't break once they've fixed the problem on their side:
Call<Categories> api = SomeClient.getAllCategories();
api.enqueue(new Callback<Categories>() {
#Override
public void onResponse(#NonNull Call<Categories> call, #NonNull Response<Categories> response) {
switch(response.code()) {
/* temporary workaround: treating HTTP404 as if it would be HTTP200. */
case 200:
Log.e(LOG_TAG, "a proper HTTP200 header had been received.");
case 404:
if (response.body() != null) {
Categories items = response.body();
...
} else if (response.errorBody() != null) {
String errors = response.errorBody().string();
...
}
break;
}
}
}
Just logging an error on HTTP200, so that one can see when it's about time to fix the code.

Related

Android Azure Offline Sync - Not completing sync

I have an android app with Azure Mobile Services and implemented Offline Sync. The app works well but when syncing data it seems not to complete so there is always a few rows on tables which have not synced?
Anyone have any ideas what the problem might be. I believe that on the next try it would finish where it left off or am I wrong?
Thanks in advance
The app works well but when syncing data it seems not to complete so there is always a few rows on tables which have not synced?
I would recommend you use fiddler to capture the network traces when handling the sync operations.
For Incremental Sync, the request would be as follows:
Get https://{your-app-name}.azurewebsites.net/tables/TodoItem?$filter=(updatedAt%20ge%20datetimeoffset'2017-11-03T06%3A56%3A44.4590000%2B00%3A00')&$orderby=updatedAt&$skip=0&$top=50&__includeDeleted=true
For opting out of incremental sync, you would retrieve all records without the filter updatedAt.
Get https://{your-app-name}.azurewebsites.net/tables/TodoItem?$skip=0&$top=50&__includeDeleted=true
Note: If there are too many items, the SDK would send multiple requests to pull all items that match your given query from the associated remote table. Also, you need to make sure you specify the includeDeleted() in your query.
In summary, you need to make sure that all items could be retrieved via the above requests. Additionally, if the pull operation has pending local updates, then the pull operation would first execute a push operation. So, I assume that you could catch the exception when calling pull operation for handling the conflict resolution.
Bruce's answer is fine but I used a slightly different method without the need to use fiddler.
I change my connection from this
mClient = new MobileServiceClient("[AZUREWEBSITE]", cntxall);
mClient.setAndroidHttpClientFactory(new MyOkHttpClientFactory());
To this
mClient = new MobileServiceClient("[AZUREWEBSITE]", cntxall).withFilter(
new ServiceFilter() {
#Override
public ListenableFuture<ServiceFilterResponse> handleRequest(ServiceFilterRequest request, NextServiceFilterCallback nextServiceFilter) {
// Get the request contents
String url = request.getUrl();
String content = request.getContent();
if (url != null) {
Log.d("Request URL:", url);
}
if (content != null) {
Log.d("Request Content:", content);
}
// Execute the next service filter in the chain
ListenableFuture<ServiceFilterResponse> responseFuture = nextServiceFilter.onNext(request);
Futures.addCallback(responseFuture, new FutureCallback<ServiceFilterResponse>() {
#Override
public void onFailure(Throwable e) {
Log.d("Exception:", e.getMessage());
}
#Override
public void onSuccess(ServiceFilterResponse response) {
if (response != null && response.getContent() != null) {
Log.d("Response Content:", response.getContent());
}
}
});
return responseFuture;
}
}
);
This is the logging method for Azure connections and shows the request in the log.

Creating custom rx.Observable and rx.Subsciber

I'm working on an Android App. I'm using Retrofit to manage the http request to the server-side endpoints. Currently when I'm doing a request I'm doing something like this:
Observable<List<ApiFeedResponse>> feedObservable = mFeedRepository.getFeed(0, 50)
.flatMap(apiFeedsResponse -> {
if (apiFeedsResponse.code() != 200) {
if (apiFeedsResponse.code() == 304) {
List<ApiFeedResponse> body = apiFeedsResponse.body();
return Observable.just(body);
} else {
return Observable.error(new ServerSideErrorException(apiFeedsResponse));
}
} else {
return Observable.just(apiFeedsResponse.body());
}
});
My FeedRepository calls the Retrofit service. I've an endpoint that is like myhost.com/rest/userfeed?page=0&pageSize=50. The thing is that I'm also using etags to get cached server-side responses. And I want to be able to differentiate between a normal 200 http response and a "not modified" 304 response. I want to extend the rx.Subscriber lifecycle methods (onNext, onError and onComplete) to be something like (onSuccess, onServerError, onNotModified, onServerResult). That way when I subscribe to this methods is going to look like this:
getFeed(0, 50).subscribe(new ServerSubscriber<List<Feed>>() {
//Executed when the response is 200
#Override
protected void onSuccess(List<Feed> feed) {
}
//Executed when the response is 304
#Override
protected void onNotModified(List<Feed> feed) {
}
//Executed if something goes wrong while doing the http request (code is different than 200 or 304)
#Override
protected void onServerSideError(ServerSideErrorException e) {
}
//Executed always that the result of the http request is successfull (200 or 304)
#Override
protected void onServerResult(List<Feed> feed) {
}
});
I have been looking through different repos trying to find if someone has done something similar and the closes thing that I found was this: https://github.com/ReactiveX/RxJava/issues/1034
But I still can't fully understand how to implement custom rx.Observables and custom rx.Subscribers. Any advice is welcome.
Why not repackage your logic in a reusable form?
public <T> Transformer<Response<T>, T> applyCache(
Supplier<T> src,
Consumer<Response<T>> sink) {
return responseSrc -> responseSrc.flatMap(response -> {
switch(response.code()) {
case 200:
sink.accept(response);
return Observable.just(apiFeedsResponse.body());
case 304:
return Observable.just(src.get());
default:
return Observable.error(new ServerSideErrorException(apiFeedsResponse));
}
}
Just add the cache get/set functions (and adjust to taste); use like:
mFeedRepository
.getFeed(0, 50)
.compose(applyCache(feedCache::get, feedCache::set)
I think the solutions to your problems can be fixed in Retrofit/OkHttp.
Retrofit2 uses OkHttp3 under the hood to execute the API calls. OkHttp can handle the 304 not modified status code and deliver you the result from cache. To do that you need to set up retrofit to use a custom OkHttp client with cache.
For more custom callbacks there the solution is custom CallAdapter. There is an example for that in the retrofit repo which looks similar to yours. RxJava already uses a call adapter, maybe you can base is on that.

Conditional subsequent request with RxJava

I have an issue with my network client design. I have a use case, when the client tries to request an item from a REST API, but in case the API returns a 404 HTTP status code I need to send a request to create the item on the server and then request the item again.
I would like to use RxJava to avoid the callback hell. Is this a valid use case RxJava? Is it possible to create such a conditional sub-request?
Thank you for your time and answers.
Based on your question, I assume you have something that look like
public Observable<Item> getItem();
that will either return the item, or fire an error and
public Observable<?> createItem();
That will create one.
You can use those two together like so:
public Observable<Item> getOrCreateItem() {
return getItem().onErrorResumeNext(error -> {
// Depending on your framework, figure out which is the result code
if (error.getResultCode() == 404) {
return createItem().flatMap(ignored -> getItem());
} else {
return Observable.error(error);
}
});
}
With Retrofit, you'd have to simply make sure the exception is a RetrofitError, cast it, and get the response and the status code. (((RetrofitError) error).getResponse().getStatus())

Log API responses with status codes 400+ (fails) in Fabric

I'm trying to log all my API responses with status code 400+ in Fabric as non-fatal exception. Simplified method for obtain created RestAdapter look's like this:
public static Api get() {
if (mInstance == null) {
mInstance = new RestAdapter
.Builder()
.setEndpoint(API_URL)
.setErrorHandler(new ErrorHandler() {
#Override
public Throwable handleError(RetrofitError cause) {
Crashlytics.logException(new ApiException(cause.getMessage() + ": " + cause.getUrl(), cause));
return cause;
}
})
.build()
.create(Api.class);
}
return mInstance;
}
The problem is, all of this exceptions are collapsed to one in Fabric, because the same exception is thrown everytime at same line of code. I want to classify them accoring to status code of response.
I'm thinking how to solve this problem and few ideas come to my mind:
Generate exeptions (for example ApiException404 etc.) at runtime.
Throw exceptions in different lines (enormous switch with handling all of the status codes)
... but both of the approaches are really unsightly. Thanks for every advice or idea.

Unexpected HTTP 400 status code from NanoHTTPD on Android

Friends!
I'm getting occasional and unexpected HTTP 400 responses from nanohttpd in my Android app. The error is following a specific pattern. I've been looking at this for some time now but I've come to the point where I need a different angle or some other help pointing me in the right direction.
Could you please have a look and share your thoughts or even direct points and suggestions?
Why am I getting this HTTP 400 status code?
And why only under the given circumstances? (I don't want it at all!)
Some Background
I'm running nanohttpd in my Android project as a temporary isolation layer (due to server side not being mature enough yet). I have isolated the nanohttpd server in an Android Service, which I start from my custom Application object once it's created. This way nanohttpd is not bound to the lifecycle of any particular Activity but can live rather independent of the overall application logic and component life cycles.
The Problem
Now, (almost) everything is working nice and dandy: I can start nanohttpd and perform some initial login requests, my expected mock response is even delivered. When I perform my first "GET" request, though, nanohttpd throws a 400 Bad request status at me, but only the first time. If I back out of the Activity being responsible for the particular "GET" request, and launch it again (from the home screen), it delivers the expected payload with a 200 status, flawlessly.
What Have I Done So Far
I have had a closer look at the nanohttpd source code, trying to track down where and why this 400 status is set. It's not that many places this status code is used. Roughly speaking only here, here and here. Since I'm not dealing with multipart content, I'm left with the first and third "here". But - of course - I can not for my life find neither the root cause of the 400 status, nor which exact block is causing the state for me. When I debug the code, everything works just peachy.
Some Code
This is roughly what my nanohttpd Service (MyNanoHttpdService) looks like:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (ACTION_START.equals(intent.getAction())) {
String errorMessage = null;
if (myNanoHttpd == null) {
String hostUrl = intent.getStringExtra(EXTRA_HOST);
Uri uri = Utils.notEmpty(hostUrl) ? Uri.parse(hostUrl) : Uri.EMPTY;
myNanoHttpd = new MyNanoHttpd(this, uri.getHost(), uri.getPort(), null);
}
if (!myNanoHttpd.isAlive()) {
try {
myNanoHttpd.start();
} catch (IOException e) {
StringWriter stringWriter = new StringWriter();
PrintWriter printWriter = new PrintWriter(stringWriter);
e.printStackTrace(printWriter);
errorMessage = stringWriter.toString();
stopSelf();
}
}
final ResultReceiver resultReceiver = intent.getParcelableExtra(EXTRA_RESULT_LISTENER);
if (resultReceiver != null) {
int status = myNanoHttpd.isAlive() ? CODE_SUCCESS : CODE_FAILURE;
Bundle bundle = new Bundle();
bundle.putString(EXTRA_MESSAGE, errorMessage);
resultReceiver.send(status, bundle);
}
}
return Service.START_STICKY;
}
And this is how I start the service from my custom Application object, initialize my client side state and fetch some content:
#Override
public void onCreate() {
super.onCreate();
// Yes, that is a Java 8 Lambda you see there!
MyNanoHttpdService
.start(this, "http://localhost:8080")
.withStartupListener((status, message) -> {
if (status == 0) {
// POST REQUEST: Works like a charm
myNetworkHelper.login();
// GET REQUEST: Always fails on first launch
myNetworkHelper.getContent();
} else {
Log.e("LOG_TAG", "Couldn't start MyNanoHttpd: " + message);
}
});
}
It's safe to assume that the wrapping convenience code (the .withStartupListener(...) - which essentially wraps a ResultReceiver used by the above Service - and the myNetworkHelper object) works as expected. Also, in production, the getContent() call would be made from an Activity or Fragment, but for the sake ease I have moved it to the Application for now.
I may have found the root cause for my issue, and possibly even a workaround for the moment.
If I'm correct in my investigation, the issue was caused by unconsumed data from a previous (POST) request, contaminating the current (POST) request.
This line in the NanoHTTPD code base (the header parsing block in the NanoHTTPD.HTTPSession.execute() method, just before calling through to any custom serve(...) method - the third "here" in my question above) was the very line where the HTTP 400 status code was thrown, and just as the code suggests, there was no proper value for the "method" header.
The value - which I expected to be "POST" in clear text - was contaminated with parts of the JSON content body from the previous request. As soon as I realized this, I tried to consume the entire request body in my custom MyNanoHttpd.serve(IHTTPSession session) method, like so:
#Override
public Response serve(IHTTPSesion session) {
InputStream inputStream = session.getInputStream();
inputStream.skip(inputStream.available());
// or
// inputStream.skip(Long.MAX_VALUE);
// or even
// inputStream.close();
...
}
This didn't work, though, as I kept getting various exceptions. I ended up gently modifying the NanoHTTPD code, safely closing the input stream in the finally block of the very NanoHTTPD.HTTPSession.execute() method instead.
I'm, nonetheless, considering reaching out to the NanoHTTPD community to discuss a suitable and sustainable solution.

Categories

Resources