Why don't I receive POST vars from OkHttp3? (Android Studio) - java

I've answered my own question here. See the first code block where I use php://input to get the posted data.
I'm trying to send a post request from my app to a webserver, and I'm checking for the post vars using PHP:
if( isset( $_POST['name'] ) ){
echo json_encode(['status' => 1]);
}else if( $posted_data = (string) #file_get_contents('php://input') ) {
echo json_encode(['status' => 2]);
}else{
echo json_encode($_POST);
}
The request always returns and empty json encoded array.
I'm using the latest Android Studio, and the latest OkHttp, com.squareup.okhttp3:okhttp:3.4.1. For me, this is like the "Hello World" of OkHttp in Android Studio.
In MainActivity.java:
public void postSomething(View view) {
String url = "https://example.com/json_api_test.php";
String json = "{\"name\":\"cholula\"}";
OkHttpPostHandler handler = new OkHttpPostHandler();
String result = "";
try {
result = handler.execute(url, json).get();
} catch (Exception e) {
e.printStackTrace();
}
displayPostResponse(result + "\n");
}
My OkHttpPostHandler.java:
public class OkHttpPostHandler extends AsyncTask<String, Void, String> {
OkHttpClient client = new OkHttpClient();
public static final MediaType JSON
= MediaType.parse("application/json; charset=utf-8");
#Override
protected String doInBackground(String... params) {
RequestBody body = RequestBody.create(JSON, params[1]);
Request request = new Request.Builder()
.url(params[0])
.post(body)
.build();
try {
Response response = client.newCall(request).execute();
return response.body().string();
}
catch( Exception e ){
return "HTTP Request Error";
}
}
}
When I debug, I can see that the params[1] value is the expected json-like string, but that's the last time I see it.
I've tried forming the json in a number of ways, so I'm not sure if that's the problem. I just don't understand why I can't see the posted vars when the post request gets to the server.
How can I see the posted vars on the webserver? What am I doing wrong? I've only been using Android Studio and Java for less than a week, so I have no clue. I've really looked around the internet a lot for the answer, and so posting here is the last resort.
Thanks!

It turns out that one must use php://input to get the posted data.

Related

send json to Laravel using postman and android

i am trying to send json using postman to Lavavel but i facing this error.
enter image description here
this is my json code:
{
"email":"test#test.com",
"password":"testtest"
}
and this is Laravel codes :
Route::get('/r','test#store');
and
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use DB;
use Log;
class test extends Controller
{
public function store(Request $request)
{
$email = $request->input('email');
$password = $request->input('password');
Log::info('test');
Log::info($email);
Log::info($password);
DB::table('login')->insert([
['email' => $email],
['password' => $password]
]);
}
}
also i trying using android for send data using volley and so checked Laravel logs :
Column 'email' cannot be null (this is Laravel logs)
and on android Logs:
E/Volley: [299] BasicNetwork.performRequest: Unexpected response code 500 for http://192.168.1.4:8000/r
D/error: com.android.volley.ServerErro
my android code is :
public class ApiService {
private final Context context;
public ApiService(Context context){
this.context=context;
}
public void loginUser(String email, String password, final OnLoginResponse onLoginResponse){
JSONObject requestJsonObject=new JSONObject();
try {
requestJsonObject.put("email",email);
requestJsonObject.put("password",password);
JsonObjectRequest request=new JsonObjectRequest(Request.Method.GET, "http://192.168.1.4:8000/r",requestJsonObject , new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
Log.d("response",response.toString());
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.d("error",error.toString());
}
}) {
#Override
public Map getHeaders() throws AuthFailureError {
HashMap headers = new HashMap();
headers.put("Content-Type", "application/json");
return headers;
}
};
request.setRetryPolicy(new DefaultRetryPolicy(18000,DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
Volley.newRequestQueue(context).add(request);
} catch (JSONException e) {
Log.e(TAG, "loginUser: "+e.toString());
}
}
public interface OnLoginResponse{
void onResponse(boolean success);
}
}
I hope this helps people trying to search on how to send JSON data to laravel not only specific to android applications but to all. The goal of this solution is to identify whether you can send a JSON data to laravel or not.
First of all you have to download postman from https://www.getpostman.com/ to test if your API is really working or not.
Create a post request using postman. Be sure that you follow the example data below
Be sure that you set your Routes that would associate to the controller
This is the controller part that will show the JSON data you sent if it was successfully accepted or not.
And also, if ever you are trying to send POST data to laravel, by default they provided a CSRF Token which is applicable for the forms if you are going to use the MVC of laravel. For the meantime, we are going to take this down and comment it out. Just go to app/http/kernel.php
and now you'll get the following result from the code earlier
$json = json_decode($request['json']);
echo $json->{'email'};
echo "\n";
echo $json->{'password'};
We tested that we were able to send data to laravel. I hope this truly helps.
Wen you want to send data, you will want to use POST or PUT method on your postman, specially if you are sending a body, that means that you are sending data. Get method is used to retrieve data from a service.
Take a look into CRUD functions for more information.
Your postman should look something like this
Last in your android code try to change this line
JsonObjectRequest request=new JsonObjectRequest(Request.Method.GET, "http://192.168.1.4:8000/r",requestJsonObject , new Response.Listener<JSONObject>() {
to use Request.Method.POST

Play framework 2.6 - Java : Ws request POST with oAuth Instagram

I'm trying to communicate with Instagram's API but the reply I get back from my request says that the parameters I passed onto the body weren't detected.
{"error_type":"OAuthException","code":400,"error_message":"You must provide a client_id"}
I tried to send the request by passing a JsonNode or a string inside .post(), like below, but both where unsuccessful.
public CompletionStage<Result> getInstagramToken() {
String code = request().getQueryString("code");
if(code != null) {
WSRequest request = ws.url("https://api.instagram.com/oauth/access_token").setContentType("application/x-wwww-form-urlencoded");
// Json body
/*JsonNode body = Json.newObject()
.put("client_id", insta_clientId)
.put("client_secret", insta_clientSecret)
.put("grant_type", "authorization_code")
.put("redirect_uri", redirect_uri)
.put("code", code);*/
// String body
String body = "client_id="+insta_clientId+"&client_secret="+insta_clientSecret+"&grant_type=authorization_code&redirect_uri="+redirect_uri+"&code="+code;
CompletionStage<WSResponse> response = request.post(body);
return response.thenApplyAsync(resp -> ok(resp.asJson()), exec);
}
return null;
}
The same request passed flawlessly when trying to send it by using a curl command on a terminal or with the Rested plugin on chrome ( where "content type" is set to "application/x-www-form-urlencoded" and the parameters are placed inside "Request body" )
Does anyone have any idea as to how I am supposed to send this request ?
ps: I am also looking for a way to retrieve the value received from my request and store it in a variable instead of returning it to the client.
It seems you are missing a:
.setContentType("application/x-www-form-urlencoded")
Look at our code below. In the post() you can also use a Json object so you can send a HashMap:
CompletionStage<Result> out = ws.url(cbUrl)
.setAuth(<<your user>> , <<your password>>, WSAuthScheme.BASIC)
.setRequestTimeout(Duration.ofSeconds(5))
.setContentType("application/x-www-form-urlencoded")
.post("param=value")
.handle((response, error) -> {
// Was the Chargebee API successful?
if (error == null) {
// Debugging purposes
JsonNode jn = response.asJson();
Logger.debug(Json.toJson(postMap).toString());
Logger.debug(jn.toString());
// Success stuff
return ok("good");
} else {
// Error stuff
return ok("bad");
}
});
Hope this helps you.

Android WebView custom headers

I am currently using this code to add a custom header to android WebView
Map<String, String> extraHeaders = new HashMap<String, String>();
extraHeaders.put("example", "header");
webView.loadUrl(url, extraHeader);
Above code is working but only on the main page. So if I write this code echo $_SERVER['example'] it prints header. But there is an iframe in the loaded URL which shows an undefined error when I try the same code. Is there any way I can fix this?
So what I want to do is add custom header not only to the main loaded URL but also on the iframe of the loaded page.
This worked for me:
mWebView.setWebViewClient(new MyWebViewClient(token));
in MyWebViewClient
#Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
try {
OkHttpClient httpClient = new OkHttpClient();
Request request = new Request.Builder()
.url(url.trim())
.addHeader("token", mToken) //add headers
.build();
Response response = httpClient.newCall(request).execute();
return new WebResourceResponse(
getMimeType(url), // set content-type
response.header("content-encoding", "utf-8"),
response.body().byteStream()
);
} catch (IOException e) {
return null;
}
}
//get mime type by url
public String getMimeType(String url) {
String type = null;
String extension = MimeTypeMap.getFileExtensionFromUrl(url);
if (extension != null) {
if (extension.equals("js")) {
return "text/javascript";
}
else if (extension.equals("woff")) {
return "application/font-woff";
}
else if (extension.equals("woff2")) {
return "application/font-woff2";
}
else if (extension.equals("ttf")) {
return "application/x-font-ttf";
}
else if (extension.equals("eot")) {
return "application/vnd.ms-fontobject";
}
else if (extension.equals("svg")) {
return "image/svg+xml";
}
type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
}
return type;
}
You can put this setting to your web setting, every request from webview will be using this User-Agent header.
webview.getSettings().setUserAgentString("user-agent-string");
No, that is not possible with Android WebView itself. You have to work around either in your page code, or in your app's code, or on the server.
For fixing this on the page's side, you can use XMLHttpRequest for loading subresources. But for that you will have basically to construct the page on the fly.
On the app's side, you can use WebViewClient.shouldInterceptRequest, to intercept all the network requests. You are not allowed to just modify the provided request, instead, you will need to make a new request yourself, but there you will be able to set any headers you want. See this example: Android WebViewClient url redirection (Android URL loading system)
On the server side, you can look into Referer header of subresources, which must contain the url of the page that has requested it.
Just add this piece of code before load URL:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
CookieManager.getInstance().removeAllCookies(null);
}

JSON response is getting cut off, Android

I've seen multiple posts about this topic, but none of them seem to be the solution to my problem.
The problem is that the JSON response from the server is getting cut off and therefore I'm getting a JSONException when trying to get the response into a JSONArray.
json = new JSONArray(EntityUtils.toString(response.getEntity()));
Here is the whole code:
private class AsyncFetchForms extends AsyncTask<String, Void, JSONArray> {
private HttpClient mClient = new DefaultHttpClient();
private AsyncTaskCompleteListener<JSONArray> listener;
private String serverUrl;
private String credentials;
private ProgressDialog progressDialog;
private HttpGet httpGet;
private String response;
private BasicResponseHandler responseHandler;
private boolean showDialog;
private JSONArray json;
public AsyncFetchForms(String url, String message, AsyncTaskCompleteListener<JSONArray> listener, boolean showDialog)
{
serverUrl = Utils.getServerUrl(context) + url;
credentials = Utils.getUserCredentials(context);
this.listener = listener;
this.showDialog = showDialog;
httpGet = new HttpGet(serverUrl);
httpGet.setHeader("Authorization", "Basic " + credentials);
httpGet.setHeader("Accept", "application/json");
httpGet.setHeader("Accept-Encoding", "gzip");
httpGet.setHeader("Connection", "keep-alive");
responseHandler = new BasicResponseHandler();
if(showDialog)
{
progressDialog = new ProgressDialog(context);
progressDialog.setMessage(message);
progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
progressDialog.show();
}
}
#Override
protected JSONArray doInBackground(String... params) {
try {
HttpResponse response = mClient.execute(httpGet);
if (response.getStatusLine().getStatusCode() == 200) {
json = new JSONArray(EntityUtils.toString(response.getEntity()));
return json;
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ParseException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(JSONArray result) {
System.out.println(result.toString());
}
}
Can anyone see the problem?
Logcat can only show about 4000 characters. So you will have to implement a recursive function to see the entire log. Use below function to see the entire log:
public static void longInfo(String str) {
if (str.length() > 4000) {
Log.d("", str.substring(0, 4000));
longInfo(str.substring(4000));
} else
Log.d("", str);
}
If you (or your team) implement the server side yourself, first thing I'd check is if the server is returning the correct HTTP response. In particular, if you transfer the data by HTTP, you need to have correct Content-Length or otherwise your data will be cut off. Also, Content-Length must be the length of data after any Transfer Encodings are applied, in other words, after the length of the data after being gzipped. Alternatively, use chunked transfer.
Second, make sure that your server is generating valid JSON. Maybe you missed a closing parentheses or so. Maybe you need to parse JSON Object rather JSON Array.
Also, if you receive exceptions, please always post the the entire traceback.
First of all, try to log the EntityUtils.toString(response.getEntity()) response and make sure that it starts with "[" not "{" ie. it's a jsonArray not jsonObject.
Then try to open the url in your browser ,if avilable, and make sure that there are no encoding issues.
Finally, if the problem is still exists please send us the error log output.
This answer is completely out of the subject but :
What are you trying do here ? Do you know that there are libraries which are doing all this boring job for you ?
When I talk about boring job, I'm talking about managing all the background running stuff (like AsyncTask), JSON decoding and HTTP response. I know that it's sometimes a pain in the a** (I've been there) but now I've choose to not worry anymore and use a dedicated library : http://square.github.io/retrofit/
This little baby will contact the Webservice of your choice, download the JSON and put it into a custom java class with all the attributes you want to deal with.
If you plug it with something like ORMLite, it can even save your JSON response object into a SQLite DB that you can access in the same way (it "populates" a java class object by setting all the attributes for you).
Personally I can't imagine myself doing all this stuff by hand anymore, it's just trouble without the benefits =)

POST request to REST service in AsyncTask

I'm writing some small android application that works with REST services.
The base structure of the response is:
{
result: "ok/error",
message: "some string",
entity: "either object or error description"
}
The entity type is different each time whether the response is ok or error.
Now, I'm calling the service from AsyncTask and I need to return the result to the UI thread.
I'm using gson library to desirialize the JSON from the server. The problem is that I do not have the ability to know what type of response I've got (ok or error).
Also, the AsyncTask can return only single type to the UI thread. Below is an example for what I could come up with. I need to state that I'm not a java programmer and I may not know all the patterns and maybe I miss something.
Anyway, I'll be glad for any help.
public class RegisterProxyAsync extends AsyncTask<User, String, Object> {
#Override
protected Object doInBackground(User... params) {
try {
Gson gson = new Gson();
String request = gson.toJson(params[0]);
HttpClient client = new DefaultHttpClient();
HttpPost postAction = new HttpPost("http://SomeServiceEndpoint/register");
postAction.addHeader(new BasicHeader(HTTP.CONTENT_TYPE, "application/json"));
postAction.setEntity(new StringEntity(request));
HttpResponse response = client.execute(postAction);
if (response != null) {
InputStream stream = response.getEntity().getContent();
String strResult = CharStreams.toString(new InputStreamReader(stream));
try {
UserResponse userResponse = gson.fromJson(strResult, UserResponse.class);
return userResponse;
} catch (Exception e) {
ErrorResponse errorResponse = gson.fromJson(strResult, ErrorResponse.class);
return errorResponse;
}
Log.e("debug", strResult);
}
} catch (Exception e) {
// TODO:Handle exception
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Object result) {
// TODO: execute some work
}
}
EDIT:
I've changed the server to return proper HttpCodes (200 for success, 400 for error) but, I still have the problem of returning two different types of object from the doinbackground method, one for error and another for success.
A proper REST service should include an HTTP response code indicating the status of the processed request. If you have control over the service then I would recommend altering it to return a variant of 40x or 50x codes, to signal that an error has occurred. The service should only return a 200 OK if the request succeeded. On your client side, you would then parse the response based on the status code (normal entity for 200 ok, error entity for anything else). Pseudocode:
if(response.getStatusLine().getStatusCode() == 200) {
UserResponse userResponse = gson.fromJson(strResult, UserResponse.class);
} else {
ErrorResponse errorResponse = gson.fromJson(strResult, ErrorResponse.class);
}
If you can't change the server side for whatever reason, then on your client side you will have to use a generic JsonObject to parse the response. Pseudocode:
JSONObject jsonObj = new JSONObject(strResult);
if("ok".equals(jsonObj.get("result")) {
return gson.fromJson(jsonObj.toString(), UserResponse.class);
} else {
return gson.fromJson(jsonObj.toString(), ErrorResponse.class);
}

Categories