Android Retrofit + Rxjava: How to get response on non200 code? - android

This is how my request looks like:
ApiService apiService = retrofit.create(ApiService.class);
Observable<Response<UserUpdateResponse>> response = apiService.updateUser(Utils.getHeader(), object);
response.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::onSuccessUpdate,
this::onErr,
this::hideDialogLoading);
It's supposed to return 'code':'205' 'msg':'successfully update'. But when server response any code 201,202 (anything not 200) it will go to error.
Here is the Error.
java.net.ProtocolException: HTTP 205 had non-zero Content-Length: 121
So how do I prevent it from error, or how do I get error body? Thank you!.

HTTP response codes have a predefined definition and some have requirements that they must fullfill to be considered a valid HTTP payload. You cannot redefine what these codes mean for your application and expect well-implemented clients to accept it.
Looking specifically at HTTP 205 - Reset Content, which has the following requirement:
Since the 205 status code implies that no additional content will be provided, a server MUST NOT generate a payload in a 205 response.
Generally applications will just return HTTP 200 for all requests and include application-specific error codes in the payload. What you're doing does not make much sense.

So technically, I can get response 2xx. The problem was that server response body in response code 205 that suppose to be null (https://www.rfc-editor.org/rfc/rfc7231#section-6.3.6). So after set body null on server, android side works fine.

Related

Retrofit2 posting form-data works android 7, not android 5

I'm using retrofit2 to post credentials in a multipart/form-data. The goal is to receive a session cookie.
The solution works as expected running in Android 8, but can't seem to work on older versions such as 5 - 6. I get no errors, but the server doesn't return any cookie.
The code looks something like this
protected Void doInBackground(Void... params) {
if (service == null) {
boxRestService = getService(Constants.URL);
}
MultipartBody mPart = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart(Constants.FORMS_USERNAME, credentials.first)
.addFormDataPart(Constants.FORMS_PASSWORD, credentials.second).build();
Call<ResponseBody> currentCall = service.getFormAuthCookie(mPart);
try {
currentCall.execute();
} catch (Exception e) {
Log.d("GET_AUTH_BACKGROUND", e.getMessage());
}
return null;
}
Furthermore, the call looks as such:
#POST("/check/login")
Call<ResponseBody> getFormAuthCookie(#Body MultipartBody body);
In both platforms, retrofit produces the same (correct) post request:
--> POST https://website.dom/check/login http/1.1
Content-Type: multipart/form-data; boundary=aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
Content-Length: 258
--aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
Content-Disposition: form-data; name="sph_username"
Username
--aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
Content-Disposition: form-data; name="sph_password"
Password
--aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa--
--> END POST (258-byte body)
Since I need the cookie for the next request, this request is performed synchronously in a background thread.
For the cookie part, that I don't believe it's an issue, I'm using a JavaNetCookieJar.
The request also works fine using Advanced Rest Client.
I'm sure I'm missing something very obvious.
Are those methods usable in older android versions?
Thank you very much.
-nls
So, turns out the server was redirecting the requests without my knowledge. I found this by accident while experimenting with the HTTP client options.
The solution, for this (very, very) specifit cenario, is to disable the following of redirects.
Thus, I changed the retrofit builder method to do something as follows:
OkHttpClient clt = new OkHttpClient().Builder()
builder.followRedirects(false) // <-- Important!
...
.build();
Retrofit r = new Retrofit.Builder()
.client(clt)
...
.build();
Still don't know why it worked in newer versions of android.
Hopefully this helps anyone.
Cheers
-nls

Events subscription via webhooks using MS graph Android SDK

I am implementing events subscription using MS graph android SDK. Below is the code.
IGraphServiceClient mGraphServiceClient = GraphServiceClientManager.getInstance(context).getGraphServiceClient();
mGraphServiceClient.getSubscriptions().buildRequest().post(getSubscription(email, expirationDate), subscriptionICallback);
private Subscription getSubscription(String email, Calendar expirationDate) {
Subscription subscription = new Subscription();
subscription.expirationDateTime = expirationDate;
subscription.notificationUrl = "<Webhook link>";
subscription.changeType = "created,updated";
subscription.resource = "me/events";
subscription.clientState = Base64.encodeToString(email.getBytes(), Base64.URL_SAFE | Base64.NO_PADDING | Base64.NO_WRAP);
return subscription;
}
The request body generated through this always throws the error:
Error during http request Error code: InvalidRequest
Error message: Could not process subscription creation payload. Are all property names spelled and camelCased properly?
POST https://graph.microsoft.com/v1.0/subscriptions
SdkVersion : graph-android-v1.0.0
Authorization : Bearer eyJ0eXAiOiJKV1QiLCJub25jZSI[...]
{"#odata.context":"https://graph.microsoft.com/v1.[...]
400 : Bad Request
E/DefaultHttpProvider[sendRequestInternal] - 286: [...]
E/DefaultHttpProvider[sendRequestInternal] - 286: [Some information was truncated for brevity, enable debug logging for more details]
E/DefaultHttpProvider[sendRequestInternal] - 286: Throwable detail:
com.microsoft.graph.http.GraphServiceException: Error code: InvalidRequest
Error message: Could not process subscription creation payload. Are all property names spelled and camelCased properly?
POST https://graph.microsoft.com/v1.0/subscriptions
SdkVersion : graph-android-v1.0.0
Authorization : Bearer eyJ0eXAiOiJKV1QiLCJub25jZSI[...]
{"#odata.context":"https://graph.microsoft.com/v1.[...]
400 : Bad Request
[...]
[Some information was truncated for brevity, enable debug logging for more details]
at com.microsoft.graph.http.DefaultHttpProvider.handleErrorResponse(DefaultHttpProvider.java:310)
at com.microsoft.graph.http.DefaultHttpProvider.sendRequestInternal(DefaultHttpProvider.java:246)
at com.microsoft.graph.http.DefaultHttpProvider.access$000(DefaultHttpProvider.java:47)
at com.microsoft.graph.http.DefaultHttpProvider$1.run(DefaultHttpProvider.java:129)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:818)
E/agenday.com.pgi.agenday.ui.BaseActivity: create Subscription exceptionError code: InvalidRequest
Error message: Could not process subscription creation payload. Are all property names spelled and camelCased properly?
POST https://graph.microsoft.com/v1.0/subscriptions
SdkVersion : graph-android-v1.0.0
Authorization : Bearer eyJ0eXAiOiJKV1QiLCJub25jZSI[...]
{"#odata.context":"https://graph.microsoft.com/v1.[...]
400 : Bad Request
[...]
[Some information was truncated for brevity, enable debug logging for more details]
but the same request body is working fine in graph explorer,
I have tried many things now but unable to resolve it. How can I get this to work for subscriptions in Android, I can't find any resources for this online.
The issue was due to the BaseSubscription model in Microsoft graph SDK where it is setting "#odata.type":"microsoft.graph.subscription" by default and subscription api is not supporting this in request body and throwing the error: 400
So I resolved it by explicitly making "#odata.type" to null in the subscription model and pass it to post request which worked successfully for me.
Thanks!

Make Go http.Response verbose all parameters

I am having a problem getting a parameter sent from android app into go application. I called r.FormValue(key) but it returned null. I want to find the way to check what are parameters available on Go side after the android app sent the post data to it. Is there any way to do this, getting all parameters without using keys?
The Request structure in go has a Form field which is populated with request parameters after ParseForm() is called.
Form contains the parsed form data, including both the URL field's
query parameters and the POST or PUT form data.This field is only
available after ParseForm is called. The HTTP client ignores Form and
uses Body instead.
You could try adding the following code after receiving a request:
func(w http.ResponseWriter, request *http.Request) {
request.ParseForm()
log.Printf("%v",request.Form)
//....
}
If this is for debugging, you can use DumpRequest:
func(w http.ResponseWriter, r *http.Request) {
dump, err := httputil.DumpRequest(r, true)
if err != nil {
http.Error(w, fmt.Sprint(err), http.StatusInternalServerError)
return
}
log.Printf("%s", dump)
}

Issues with Caching on OkHttpClient 2.0

I have an issue with caching using OkHttpClient 2.0. It seems that the Response is ignoring the Cache-Control header completely. This is how I am setting up the client and the cache.
OkHttpClient client = new OkHttpClient();
cache = new Cache(new File(Session.getInstance().getContext().getCacheDir(),"http"), 10 * 1024 * 1024);
client.setCache(cache);
client.setCookieHandler(CookieHandler.getDefault());
client.setConnectTimeout(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS);
client.setReadTimeout(SOCKET_TIMEOUT, TimeUnit.MILLISECONDS);
I believe that the cache directory is created correctly. This is what I see in journal in the /cache/http directory of my application.
libcore.io.DiskLruCache
1
201105
2
This is how I am creating the Request.
Request mRequest = new Request.Builder().url(mUrl).get().build();
Getting the response :
Response response = client.newCall(mRequest).execute();
When using curl, the headers as as follows.
< HTTP/1.1 200 OK
< Date: Fri, 27 Jun 2014 19:39:40 GMT
* Server Apache-Coyote/1.1 is not blacklisted
< Server: Apache-Coyote/1.1
< Cache-Control: no-transform, max-age=1800
< Content-Type: application/json
< Transfer-Encoding: chunked
The OKHttp response headers are as follows.
Connection:Keep-Alive
Content-Type:application/json
Date:Fri, 27 Jun 2014 18:58:30 GMT
Keep-Alive:timeout=5, max=100
OkHttp-Received-Millis:1403895511337
OkHttp-Selected-Protocol:http/1.1
OkHttp-Sent-Millis:1403895511140
Server:Apache-Coyote/1.1
Transfer-Encoding:chunked
The responses never get cached and the call client.getCache().getHitCount() always gives 0. Can someone please suggest what changes might be required here to make the cache work? Thanks.
Okay, the problem was all my get and post requests were using the Authorization Bearer xxxx header and http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html section 14.8 states that these requests can't be cached. The solution was to use s-maxage on the server instead of just max age according to this :
When a shared cache (see section 13.7) receives a request
containing an Authorization field, it MUST NOT return the
corresponding response as a reply to any other request, unless one
of the following specific exceptions holds:
If the response includes the "s-maxage" cache-control
directive, the cache MAY use that response in replying to a
subsequent request.
Are you reading the entire response body? OkHttp won't cache unless you consume the entire response.
I realize you solved your specific problem, but the symptom you describe has another cause.
When using the okhttp-urlconnection, caching doesn't kick in by default, unless we do this:
connection.setUseCaches(true)
(It should be on by default, but some library I was using was setting it to off)

Previous Android Response Being Added to Status Line

I'm using Volley to communicate with an API.
I am sending a request and on success of that request I immediately fire off another one, using the same message queue.
The issue I have is the second request is responding with the following error:
java.net.ProtocolException: Unexpected status line: {"id":47}HTTP/1.1 200 OK
The {"id":47} is the response body from the first request. I'm not even going near the status line in my code and the requests are fairly simple.
What on earth is happening?! Is it a bug within Volley?
I would suppose that the status line "HTTP/1.1 200 OK" is expected to be before the actual response content {"id":47} - according to the error message it seems to be exactly reversed.
In my case, the server return content when status code is 204,
api should not return data when 204 responses

Categories

Resources