Show the actual raw data + HTTPS headers sent by HttpsURLConnection - android

I have this simple code to connect to my host:
URL url = new URL("https://www.example.com/getResource");
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/json");
m_HttpsConnection.setDoOutput(true);
OutputStream os = m_HttpsConnection.getOutputStream();
DataOutputStream wr = new DataOutputStream(os);
wr.writeBytes("{\"key\":\"value\"}");
wr.flush();
wr.close();
Log.d("MyActivity", "http raw data: " + conn.toString()); <-- this is what I want to achieve!
What I want to see in my logs is the actual HTTP packets that I send to the host like so:
POST /getResource/ HTTP/1.1
Content-Type: application/json
Cache-control: no-cache
Content-Length: 15
{"key":"value"}
How do I do this? Thanks!

For both OkHttpClient and HttpsURLConnection, there is no way to check the complete raw request HTTP packet that you send to a host.
For OkHttpClient, this is the best you can do to check the headers:
class LoggingInterceptor implements Interceptor {
#NotNull
#Override
public Response intercept(#NotNull Chain chain) throws IOException {
Request request = chain.request();
long t1 = System.nanoTime();
logger.info(String.format("Sending request %s",
request.headers().toString()));
Response response = chain.proceed(request);
long t2 = System.nanoTime();
logger.info(String.format("Received response for %s in %.1fms%n%s",
response.request().url(), (t2 - t1) / 1e6d, response.headers()));
return response;
}
}
Please take note that when you did not set any header (Request.Builder.header()), nothing will show in your logs. The same observation holds true for HttpsURLConnection. The HttpsURLConnection.getRequestProperties() will only show what you have set in HttpsConnection.setRequestProperty().
For HTTP response, it is a different story. You can get the entire raw response headers.
For HttpsURLConnection you can use below code:
Map<String, List<String>> map = httpsURLConnection.getHeaderFields();
Set<String> keys = map.keySet();
Log.d(TAG, "https response headers: ");
for (String key : keys) {
List<String> list = map.get(key);
for (String value : list) {
Log.d(TAG, "key: " + key + ", value: " + value);
}
}
This gives you everything in your response packet including the HTTP/1.1 200 OK field:
D/MyActivity: key: null, value: HTTP/1.1 200 OK
D/MyActivity: key: Cache-Control, value: private
D/MyActivity: key: Content-Length, value: 20
D/MyActivity: key: Content-Type, value: application/json
D/MyActivity: key: Date, value: Thu, 22 Aug 2019 09:16:06 GMT
D/MyActivity: key: Server, value: Microsoft-IIS/10.0
I believe you can also get the complete response packet for OkHttpClient. I did not check.

Related

Why Okhttp post request is not setting token after I give them properly

This is My Okhttp Kotlin Request
val gson = Gson()
val json = gson.toJson(orderShip)
Log.e(TAG, "onResponse: -- $json")
val mediaType: MediaType? = MediaType.parse("application/json")
val body: RequestBody = RequestBody.create(
mediaType,
json)
val request: okhttp3.Request = okhttp3.Request.Builder()
.url("https://apiv2.shiprocket.in/v1/external/orders/create/adhoc")
.method("POST", body)
.addHeader("Content-Type", "application/json")
.addHeader("H-Authorization", "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOjI3MzY3MDIsImlzcyI6Imh0dHBzOi8vYXBpdjIuc2hpcHJvY2tldC5pbi92MS9leHRlcm5hbC9hdXRoL2xvZ2luIiwiaWF0IjoxNjU1MzcxNjgzLCJleHAiOjE2NTYyMzU2ODMsIm5iZiI6MTY1NTM3MTY4MywianRpIjoiMHZ4ZlhuUEg3MXpMZ3poMSJ9.QN03Kr5eSikr3FIF7CNYFKM56a6RdXfscTefIeh_eos")
.build()
val response2: okhttp3.Response = client.newCall(request).execute()
if(response2.isSuccessful){
val topic = gson.fromJson(response2.body().toString(), OrderShipResult::class.java)
Log.e(TAG, "onResponse: ${topic.shipment_id}")
}else{
Log.e(TAG, "onResponse: ${response2.code()}")
Log.e(TAG, "onResponse: ${response2.message()}")
Log.e(TAG, "onResponse: ${response2.headers()}")
Log.e(TAG, "onResponse: ${response2.body().toString()}")
}
And Logs
2022-06-16 15:00:59.121 5233-5233/com.gayatriladieswears.app E/ContentValues: onResponse: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOjI3MzY3MDIsImlzcyI6Imh0dHBzOi8vYXBpdjIuc2hpcHJvY2tldC5pbi92MS9leHRlcm5hbC9hdXRoL2xvZ2luIiwiaWF0IjoxNjU1MzcxODU5LCJleHAiOjE2NTYyMzU4NTksIm5iZiI6MTY1NTM3MTg1OSwianRpIjoiSUhOZzh3Sk9EdGlybDNQQyJ9.MxjdrJTD1F5aUeN6ew7eW5QyXqTLiHd4BqcwgO3upbI
2022-06-16 15:00:59.143 5233-5233/com.gayatriladieswears.app E/ContentValues: onResponse: -- {"billing_address":"Plot no 289 Shivganga Colony, Saneguruji Vhashat, Salokhe nagar road, Kolhapur","billing_address_2":"Near Shivganga Colony Bus Stop","billing_city":"","billing_country":"","billing_customer_name":"Atharv Kotekar","billing_email":"atharvkotekar7#gmail.com","billing_isd_code":"","billing_last_name":"","billing_phone":918669026158,"billing_pincode":416012,"billing_state":"","breadth":0.0,"channel_id":2994444,"comment":"","company_name":"","customer_gstin":"","ewaybill_no":"","giftwrap_charges":0,"height":0.0,"invoice_number":"","length":0.0,"order_date":"16-06-2022","order_id":"qRGZsr9ECSp2AsmEBRRY","order_items":[{"discount":0,"name":"Dummy Product 2","selling_price":499,"sku":"bBaaWv1oC1qjXa2dy4On","tax":0,"units":1}],"order_type":"","payment_method":"Prepaid","pickup_location":"Primary","reseller_name":"","shipping_address":"Plot no 289 Shivganga Colony, Saneguruji Vhashat, Salokhe nagar road, Kolhapur","shipping_address_2":"Near Shivganga Colony Bus Stop","shipping_charges":0,"shipping_city":"","shipping_country":"","shipping_customer_name":"Atharv Kotekar","shipping_email":"atharvkotekar7#gmail.com","shipping_is_billing":true,"shipping_last_name":"","shipping_phone":918669026158,"shipping_pincode":416012,"shipping_state":"","sub_total":59800,"total_discount":0,"transaction_charges":0,"weight":0.0}
2022-06-16 15:00:59.732 5233-5233/com.gayatriladieswears.app E/ContentValues: onResponse: 422
2022-06-16 15:00:59.732 5233-5233/com.gayatriladieswears.app E/ContentValues: onResponse: Unprocessable Entity
2022-06-16 15:00:59.733 5233-5233/com.gayatriladieswears.app E/ContentValues: onResponse: Date: Thu, 16 Jun 2022 09:30:59 GMT
Content-Type: application/json
Transfer-Encoding: chunked
Connection: keep-alive
Server: nginx
Strict-Transport-Security: max-age=31536000; includeSubdomains; preload
Cache-Control: no-cache, private
X-Frame-Options: SAMEORIGIN
2022-06-16 15:00:59.733 5233-5233/com.gayatriladieswears.app E/ContentValues: onResponse: okhttp3.internal.http.RealResponseBody#3ad1fe8
There in a log no such hear as "Authorization" but eventually added to my request.
I tried All method like Interceptors and more but nothing works
Is that because of my api has no access to android apps?
Or something Else.

How to get this iOS Http Post Request to work?

I am setting up a payout process for a driver in my app with Firebase Cloud Functions and Paypal. The url to be posted is the url of the actual cloud function in Firebase:
https://us-central1-ryyde-sj.cloudfunctions.net/payout
When trying to send an HTTP Post Request, it doesn't seem to be working. See the payoutRequest() and the Response code below:
payoutRequest()
let email = txtPayoutEmail.text!
let uid = self.uid!
// Prepare URL:
let url = URL(string: "https://us-central1-ryyde-sj.cloudfunctions.net/payout")
guard let requestUrl = url else { fatalError() }
// Prepare URL Request Object:
var request = URLRequest(url: requestUrl)
request.httpMethod = "POST"
// Set HTTP Request Headers
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("Your Token", forHTTPHeaderField: "Authorization")
request.setValue("no-cache", forHTTPHeaderField: "cache-control")
print("request = \(request)")
// HTTP Request Parameters which will be sent in HTTP Request Body:
let postString = "uid=\(uid)&email=\(email)"
print("postString = \(postString)")
// Set HTTP Request Body
request.httpBody = postString.data(using: String.Encoding.utf8)
// Perform HTTP Request
let task = URLSession.shared.dataTask(with: request) { (data, response, error ) in
print("data: \(String(describing: data))")
print("response: \(String(describing: response))")
print("error: \(String(describing: error))")
if let response = response as? HTTPURLResponse {
// Read all HTTP Response Headers
print("All headers: \(response.allHeaderFields)")
// Read a specific HTTP Response Header by name
if #available(iOS 13.0, *) {
print("Specific header: \(response.value(forHTTPHeaderField: "Content-Type") ?? " header not found")")
} else {
// Fallback on earlier versions
}
}
// Check for Errors
if let error = error {
print("Error took place \(error)")
return
}
// Convert HTTP Response Data to a String
if let data = data, let dataString = String(data: data, encoding: .utf8) {
print("Response data string: \(dataString)")
}
}
task.resume()
Response
request = https://us-central1-ryyde-sj.cloudfunctions.net/payout
postString = uid=kv8JRVBwAfS1tgD04lNeM9esVzI2&email=myiosapp#me.com
data: Optional(138 bytes)
response: Optional(<NSHTTPURLResponse: 0x6000037d1c20> { URL: https://us-central1-ryyde-sj.cloudfunctions.net/payout } { Status Code: 400, Headers {
"Content-Length" = (
138
);
"Content-Type" = (
"text/html; charset=utf-8"
);
Date = (
"Thu, 17 Sep 2020 01:00:50 GMT"
);
Server = (
"Google Frontend"
);
"alt-svc" = (
"h3-Q050=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000,h3-27=\":443\"; ma=2592000,h3-T051=\":443\"; ma=2592000,h3-T050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\""
);
"content-security-policy" = (
"default-src 'none'"
);
"function-execution-id" = (
cmrwbktlroxl
);
"x-cloud-trace-context" = (
"a85aaacd578e60690581aa64ead13b23;o=1"
);
"x-content-type-options" = (
nosniff
);
"x-powered-by" = (
Express
);
} })
error: nil
All headers: [AnyHashable("content-security-policy"): default-src 'none',
AnyHashable("Date"): Thu, 17 Sep 2020 01:00:50 GMT, AnyHashable("alt-svc"): h3-Q050=":443";
ma=2592000,h3-29=":443"; ma=2592000,h3-27=":443"; ma=2592000,h3-T051=":443"; ma=2592000,h3-
T050=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443";
ma=2592000; v="46,43", AnyHashable("Content-Type"): text/html; charset=utf-8,
AnyHashable("Content-Length"): 138, AnyHashable("x-cloud-trace-context"):
a85aaacd578e60690581aa64ead13b23;o=1, AnyHashable("Server"): Google Frontend,
AnyHashable("x-powered-by"): Express, AnyHashable("x-content-type-options"): nosniff,
AnyHashable("function-execution-id"): cmrwbktlroxl]
Specific header: text/html; charset=utf-8
Response data string: <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>Bad Request</pre>
</body>
</html>
If the request is successful, then it would show up in the PayPal Notifications for PayPal Sandbox at the below link, but it isn't.
PayPal developer notifications link
I don't have much experience in PayPal HTTP requests.
I have done the same thing as I am trying to do here but in Android and it works perfectly so I know this should work, other than the Post Request (I tried using examples online to match what i had for the Android app)
Edit
updated payoutRequest():
Code surrounded in ** ** is new code
let email = txtPayoutEmail.text!
let uid = self.uid!
// Prepare URL:
let url = URL(string: "https://us-central1-ryyde-sj.cloudfunctions.net/payout")
guard let requestUrl = url else { fatalError() }
// Prepare URL Request Object:
var request = URLRequest(url: requestUrl)
request.httpMethod = "POST"
// Set HTTP Request Headers
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("Your Token", forHTTPHeaderField: "Authorization")
request.setValue("no-cache", forHTTPHeaderField: "cache-control")
print("request = \(request)")
// HTTP Request Parameters which will be sent in HTTP Request Body:
**let body = ["uid": uid, "email": email]**
print("body = \(body)")
// Set HTTP Request Body
**request.httpBody = try? JSONSerialization.data(withJSONObject: body, options: [])**
// Perform HTTP Request
let task = URLSession.shared.dataTask(with: request) { (data, response, error ) in
print("data: \(String(describing: data))")
print("response: \(String(describing: response))")
print("error: \(String(describing: error))")
if let response = response as? HTTPURLResponse {
// Read all HTTP Response Headers
print("All headers: \(response.allHeaderFields)")
// Read a specific HTTP Response Header by name
if #available(iOS 13.0, *) {
print("Specific header: \(response.value(forHTTPHeaderField: "Content-Type") ?? " header not found")")
} else {
// Fallback on earlier versions
}
}
// Check for Errors
if let error = error {
print("Error took place \(error)")
return
}
// Convert HTTP Response Data to a String
if let data = data, let dataString = String(data: data, encoding: .utf8) {
print("Response data string: \(dataString)")
}
}
task.resume()
Response:
request = https://us-central1-ryyde-sj.cloudfunctions.net/payout
body = ["uid": "kv8JRVBwAfS1tgD04lNeM9esVzI2", "email": "driver#ryyde.com"]
data: Optional(0 bytes)
response: Optional(<NSHTTPURLResponse: 0x600001f0d6a0> { URL: https://us-central1-ryyde-sj.cloudfunctions.net/payout } { Status Code: 200, Headers {
"Content-Length" = (
0
);
"Content-Type" = (
"text/html"
);
Date = (
"Thu, 17 Sep 2020 04:41:29 GMT"
);
Server = (
"Google Frontend"
);
"alt-svc" = (
"h3-29=\":443\"; ma=2592000,h3-27=\":443\"; ma=2592000,h3-T051=\":443\"; ma=2592000,h3-T050=\":443\"; ma=2592000,h3-Q050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\""
);
"function-execution-id" = (
cmrwtq89fdsr
);
"x-cloud-trace-context" = (
"f3fe884ca8499e7a10c7081ce222876e;o=1"
);
"x-powered-by" = (
Express
);
} })
error: nil
All headers: [AnyHashable("Content-Length"): 0, AnyHashable("x-cloud-trace-context"): f3fe884ca8499e7a10c7081ce222876e;o=1, AnyHashable("Server"): Google Frontend, AnyHashable("x-powered-by"): Express, AnyHashable("function-execution-id"): cmrwtq89fdsr, AnyHashable("alt-svc"): h3-29=":443"; ma=2592000,h3-27=":443"; ma=2592000,h3-T051=":443"; ma=2592000,h3-T050=":443"; ma=2592000,h3-Q050=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000; v="46,43", AnyHashable("Date"): Thu, 17 Sep 2020 04:41:29 GMT, AnyHashable("Content-Type"): text/html]
Specific header: text/html
Response data string:
EDIT 2
When I run my code, I check the function logs in firebase/functions (read from bottom up - seems to go ok with the function activity)
EDIT 3 - Charles Session results
URL https://us-central1-ryyde-sj.cloudfunctions.net Status Sending
request body… Notes Transaction began prior to session being cleared,
body content transmitted before the session clear has not been
captured Response Code 200 Connection established Protocol HTTP/1.1
TLS TLSv1.2 (TLS_AES_128_GCM_SHA256) Protocol TLSv1.2 Session
Resumed Yes Cipher Suite TLS_AES_128_GCM_SHA256 ALPN - Client
Certificates - Server Certificates - Extensions Method CONNECT Kept
Alive No Content-Type Client Address 127.0.0.1:57209 Remote
Address us-central1-ryyde-sj.cloudfunctions.net/216.239.36.54:443
Tags - Connection WebSockets - Timing Size Request 1.77 KB (1,817
bytes) Response 1.35 KB (1,379 bytes)
EDIT 4 - Android code
private void payoutRequest() {
progress = new ProgressDialog(this);
progress.setTitle("Processing your payout ...");
progress.setMessage("Please Wait .....");
progress.setCancelable(false);
progress.show();
// HTTP Request ....
final OkHttpClient client = new OkHttpClient();
// in json - we need variables for the hardcoded uid and Email
JSONObject postData = new JSONObject();
try {
postData.put("uid", FirebaseAuth.getInstance().getCurrentUser().getUid());
postData.put("email", mPayoutEmail.getText().toString());
} catch (JSONException e) {
e.printStackTrace();
}
// Request body ...
RequestBody body = RequestBody.create(MEDIA_TYPE, postData.toString());
// Build Request ...
final Request request = new Request.Builder()
.url("https://us-central1-ryyde-sj.cloudfunctions.net/payout")
.post(body)
.addHeader("Content-Type", "application/json")
.addHeader("cache-control", "no-cache")
.addHeader("Authorization", "Your Token")
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
// something went wrong right off the bat
progress.dismiss();
}
#Override
public void onResponse(Call call, Response response) throws IOException {
// response successful ....
// refers to response.status('200') or ('500')
int responseCode = response.code();
if (response.isSuccessful()) {
switch(responseCode) {
case 200:
Snackbar.make(findViewById(R.id.layout),
"Payout Successful!", Snackbar.LENGTH_LONG)
.show();
break;
case 500:
Snackbar.make(findViewById(R.id.layout),
"Error: no payout available", Snackbar
.LENGTH_LONG).show();
break;
default:
Snackbar.make(findViewById(R.id.layout),
"Error: couldn't complete the transaction",
Snackbar.LENGTH_LONG).show();
break;
}
} else {
Snackbar.make(findViewById(R.id.layout),
"Error: couldn't complete the transaction",
Snackbar.LENGTH_LONG).show();
}
progress.dismiss();
}
});
}
try this:
let body = ["uid": uid,
"email": email]
request.httpBody = try? JSONSerialization.data(withJSONObject: body, options: [])

How to get header response in Retrofit in Android?

This is my end point :
#POST("v4/MyStore/data")
Observable<Headers> requestStore(#Body MyStoreRequest request);
I am trying to get response like this :
requestService.requestStore(request)
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.computation())
.map(headers -> {
Log.d("Response",""+headers);
return headers;
}).subscribe(headers -> {
Log.d("Response",""+headers);
},
error -> {
Log.d("ERRRROOOR","Error");
});
I am getting error like below:
Exception: retrofit2.adapter.rxjava.HttpException: HTTP 403 Forbidden
While in postman I am getting response:
Connection →keep-alive
Content-Length →0
Content-Type →application/json; charset=utf-8
Date →Mon, 03 Sep 2018 18:47:30 GMT
MYid →f028df50-c8c5-4cce-92e7-70130345ba46
What I am doing wrong here?
You have to use a Response as your response model because your api is entering error stream in with a code 403
#POST("v4/MyStore/data")
Observable<Response<Void>> requestStore(#Body MyStoreRequest request);
now when you consume response
requestService.requestStore(request)
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.computation())
.map(response -> {
Log.d("Response",""+response);
return response.header();
}).subscribe(headers -> {
Log.d("Response",""+headers);
},
error -> {
Log.d("ERRRROOOR","Error");
});
response.header() will return you the header you want even when your api fails
Probably you're missing headers in your API request from Android client. As per your comment, you're sending bearer token in Authorization headers.
So, you need to send bearer token along with request body like below
#Headers({
"Authorization: Bearer <your_bearer_token_here>", /* change token here */
"Content-Type: application/json"
})
#POST("v4/MyStore/data")
Observable<Headers> requestStore(#Body MyStoreRequest request);
Example,
#Headers({
"Authorization: Bearer ca0df98d-978c-410b-96ec-4e592a788c18",
"Content-Type: application/json"
})
#POST("v4/MyStore/data")
Observable<Headers> requestStore(#Body MyStoreRequest request);

WireMock proxying not returning any body

My application does post request to url:
https://myserver.domain.com/authenticate/credentials
OkHttp client interceptor shows my headers:
11-17 10:10:56.780 3140-3304/com.myapp.debug D/OkHttp: Content-Type:
application/x-www-form-urlencoded
11-17 10:10:56.780 3140-3304/com.myapp.debug D/OkHttp: Content-Length:
187
11-17 10:10:56.781 3140-3304/com.myapp.debug D/OkHttp: Authorization:
Basic authorisationkeyfortest5430593045903495034905==
11-17 10:10:56.781 3140-3304/com.myapp.debug D/OkHttp:
email=testlogin%40gmail.com&password=test%4012&deviceId=1484564155&deviceLabel=Android%20SDK%20built%20for%20x86_64&deviceType=ANDROID&deviceVersion=23%20%28REL%29
I have created standalone WireMock server and I want redirect every POST request from my APP to my WireMock server. Thats why I have added *.JSON with request definition:
{
"request": {
"method": "POST",
"urlPattern": ".*"
},
"response": {
"proxyBaseUrl" : "https://myserver.domain.com/",
"additionalProxyRequestHeaders": {
"Authorization": "Basic authorisationkeyfortest5430593045903495034905== "
}
}
}
What I expect that should happen:
When I change basepath of my Http client from https://myserver.domain.com/ to http://myserveraddress.com/ - then every request from my app should go to my MockServer. And MockServer according to JSON above should proxy/forward that request to https://myserver.domain.com/ and return the same response - so everything should work fine.
What happens:
Each POST request returns status 200 but body is empty. (it should return authenticated user object)
Question: Is it possible to achieve that? Am I doing something wrong?
Try to add your body in "__files" folder and set path in "bodyFileName"
{
"request": {
"method": "POST",
"urlPattern": ".*"
},
"response": {
"proxyBaseUrl" : "https://myserver.domain.com/",
"additionalProxyRequestHeaders": {
"Authorization": "Basic authorisationkeyfortest5430593045903495034905== "
},
"bodyFileName":".*.json"
}
In JSON file put your answer. For example:
{
"errorCode": 0,
"errorMessage": "",
"result":
{
"filed1":"value",
"filed2":"value"
}
}

Retrofit 2 - POST request became GET?

My POST request keeps sending as GET & got rejected by API endpoint
MyService class
#FormUrlEncoded
#POST("api/users/")
Call<List<User>> getUsers(#FieldMap HashMap<String, String> parameters);
Request code
Gson builder = new GsonBuilder().setLenient().create();
Retrofit client = new Retrofit.Builder()
.baseUrl(Constants.API_ENDPOINT_TEST_URL)
.addConverterFactory(GsonConverterFactory.create(builder))
.build();
mApiService = client.create(MyService.class);
Call<List<User>> call = mApiService.getUsers(mParameters);
call.enqueue(new Callback<List<User>>() {
#Override
public void onResponse(Call<List<User>> call, Response<List<User>> response) {
mResponse = response.body();
mResponseObserver.onFinish(mResponse);
}
#Override
public void onFailure(Call<List<User>> call, Throwable t) {
mResponseObserver.onFailure();
}
});
But server rejects it as it reached the server in GET request form !? Having checked with debugger, I saw that:
rawResponse.request.method = GET
Here is screenshot of watch window showing the retrofit's request object:
As you can see, the request method was GET. But the weird part is in the tag, it shows a request object with POST method?
Am i miss something here?
UPDATE
I added logging interceptor & here is the log:
D/OkHttp: --> POST http://***/api/users/ http/1.1
D/OkHttp: Content-Type: application/x-www-form-urlencoded
D/OkHttp: Content-Length: 56
D/OkHttp: --> END POST
D/OkHttp: <-- 200 OK https://***/api/users/ (388ms)
D/OkHttp: Date: Thu, 01 Sep 2016 11:50:23 GMT
D/OkHttp: Server: Apache
D/OkHttp: X-Powered-By: PHP/5.4.34
D/OkHttp: Cache-Control: max-age=2592000
D/OkHttp: Expires: Sat, 01 Oct 2016 11:50:23 GMT
D/OkHttp: Vary: Accept-Encoding
D/OkHttp: Content-Type: application/json; charset=UTF-8
D/OkHttp: Set-Cookie: SESSION_DEFAULT=***; expires=Sun, 04-Sep-2016 11:50:24 GMT; path=/; HttpOnly
D/OkHttp: Set-Cookie: COOKIE[***]=***; path=/; httponly
D/OkHttp: Connection: close
D/OkHttp: <-- END HTTP
looks like the request is a POST. But, the server still responds with error message, saying that the request method is a GET
Hmm, I'll dig a little bit more into it.
Actually, the issue is the combination of 2 factors:
Wrong request protocol has been made (http instead of https)
Server responded with a weird message on wrong protocol: "GET is not supported".
ANyway, thanks #nshmura for your assistant.
I checked your program with logging, and confirms the POST request is sent.
I recommend you to check like this:
class TestClass {
private void testRequest() {
HashMap<String, String> mParameters = new HashMap<>();
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.HEADERS);
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient.addInterceptor(logging); // <-- this is the important line!
Gson builder = new GsonBuilder().setLenient().create();
Retrofit client = new Retrofit.Builder()
.baseUrl(Constants.API_ENDPOINT_TEST_URL)
.addConverterFactory(GsonConverterFactory.create(builder))
.client(httpClient.build())
.build();
MyService mApiService = client.create(MyService.class);
Call<List<User>> call = mApiService.getUsers(mParameters);
call.enqueue(new Callback<List<User>>() {
#Override
public void onResponse(Call<List<User>> call, Response<List<User>> response) {
}
#Override
public void onFailure(Call<List<User>> call, Throwable t) {
}
});
}
interface MyService {
#FormUrlEncoded
#POST("api/users/")
Call<List<User>> getUsers(#FieldMap HashMap<String, String> parameters);
}
class User {
}
class Constants {
public static final String API_ENDPOINT_TEST_URL = "http://.........";
}
}
here is request log:
D/OkHttp: --> POST http://......... http/1.1
D/OkHttp: Content-Type: application/x-www-form-urlencoded
D/OkHttp: Content-Length: 0
D/OkHttp: --> END POST
Here is response:
D/OkHttp: <-- 200 OK http://.............../ (167ms)
D/OkHttp: Server: nginx
D/OkHttp: Date: Thu, 01 Sep 2016 11:30:32 GMT
D/OkHttp: Content-Type: text/html; charset=UTF-8
D/OkHttp: Connection: close
....
D/OkHttp: <-- END HTTP
I met a similar problem. In my case, I received in response to 301, redirect and change of method POST -> GET.
First, check your BASE_URL, it must be of the form "h ttps://www.YourSite.com/" www - very important. If everything is in place and the problem still exists, then you can change as described below:
From okhttp wiki and based on this image, you need to change something like this:
...
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
final RequestBody requestBody = new FormBody.Builder().build();
httpClient.addNetworkInterceptor(new Interceptor() {
#Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
HttpUrl.Builder builder = request.url().newBuilder();
HttpUrl url = builder.build();
request = request.newBuilder()
.url(url)
.post(requestBody)
.build();
return chain.proceed(request);
}
})
httpClient.addInterceptor(logging);
...
Can u try
#POST("/api/users/")
With slash before "api"?
Also it would be helpful to double check request with Fiddler or smth.
That is partly correct a http client behavior. According to the HTTP spec
If the 307 status code is received in response to a request other than GET or HEAD, the user agent MUST NOT automatically redirect the request unless it can be confirmed by the user, since this might change the conditions under which the request was issued.
Sometime it is not possible to change backend API. So here a workaround you can add to you interceptor to manually redirect a request with correct method.
fun forceRedirectMethod(chain: Interceptor.Chain, originResponse: Response): Response {
/* TODO
one more request is made; disable followRedirect and make it manually?
*/
if (originResponse.priorResponse()?.code() == 302) {
val priorResponse: Response = originResponse.priorResponse() as Response
val redirectRequest = priorResponse.request()
val builder = originResponse.request().newBuilder()
.method(redirectRequest.method(), redirectRequest.body())
val newRequest = builder.build()
return chain.proceed(newRequest)
} else {
return originResponse
}
}

Categories

Resources