I have following code where I am calling an API which is a PHP built. The code returns json stated as below which I am collecting in a stringBuilder object. Problem is its working on some carriers and on few devices with other carriers / wifi connection its throwing JSONException end of input at character 0 exception, i know this comes when input string is empty, it means stringBuilder object is empty. Problem is i don't have access to the devices on which its throwing these errors.
I am not getting on some device why following code returns empty string and on some its working fine, user has tested on 3G as well as wifi these devices are in other country on different carriers.
HttpClient httpClient = HttpClientBuilder.create().build();
HttpPost postRequest = new HttpPost(ServiceUrls.base_url + ServiceUrls.get_profile_url);
JSONObject object = new JSONObject();
object.put("username", params[0]);
StringEntity input = new StringEntity(object.toString());
input.setContentType("application/json");
postRequest.setEntity(input);
HttpResponse response = httpClient.execute(postRequest);
if (response.getStatusLine().getStatusCode() != 200) {
throw new RuntimeException("Failed : HTTP error code : "
+ response.getStatusLine().getStatusCode());
}
BufferedReader br = new BufferedReader(
new InputStreamReader((response.getEntity().getContent())));
String output;
StringBuilder stringBuilder = new StringBuilder();
while ((output = br.readLine()) != null) {
stringBuilder.append(output);
}
If it was for all API call then it was logical but doest happen for other API call, this API call returns bigger size JSON string as follows in stringbuilder
{
"status":1,
"parking":{
"name":"ghgjjghghg",
"cost":3,
"ownerId":29,
"address":"xyz pqr",
"slots":4,
"image":"d4bc95c1dd031685746f2c3570788acf.jpg",
"details":"gjhjghjgg",
"amenities":"gjhg",
"id":70,
"lon":73.7898023,
"lat":19.9974533,
"type":0,
"available":1
},
"rating":0,
"ratingCount":0,
"owner":{
"id":29,
"username":"vd#gmail.com",
"password":"",
"fullname":"vi hdjh",
"phone":"23434fddf",
"ccNum":null,
"ccType":null,
"type":1,
"authType":1,
"image":"582e3a77d76ae3203cfd6d6a346da429.jpg",
"dni":"abc123",
"account":"ABCBANK"
}
}
I have no clue whats happening , please help. Any input will be appreciated.
There is nothing unusual about the code you posted. No clues there.
Let me summarize what I think you said about the symptoms.
For some devices / carriers, a specific API call fails. But not all devices / carriers.
For the same devices / carriers as above, other API calls work, all if the time.
The client-side code is identical in all cases, apart from the URLs.
To me, this is pointing at a problem on the server side that is triggered by what the requests look like to it. But either the way, I would try to investigate this by looking at the requests and responses on the server side, and checking the server-side logs. See if there are significant differences in the requests coming from different devices / carriers. Especially the ones that work versus the ones that don't work. And see if the responses are empty when the server sends them.
I found the theory of Leonidos usefull:
https://stackoverflow.com/a/19540249/6076711
And here is my end of solution you can try using the following code.
string output = "";
while(br.ready() && (br.readLine() != null))
{
output += (String.valueOf(br.readLine()))+"\n";
}
The code can be improved by checking (before putting it in the string builder) whether the length of the content is bigger than 0.
HttpClient httpClient = HttpClientBuilder.create().build();
HttpPost postRequest = new HttpPost(ServiceUrls.base_url +
ServiceUrls.get_profile_url);
JSONObject object = new JSONObject();
object.put("username", params[0]);
StringEntity input = new StringEntity(object.toString());
input.setContentType("application/json");
postRequest.setEntity(input);
HttpResponse response = httpClient.execute(postRequest);
String output;
if (response.getStatusLine().getStatusCode() != 200) {
throw new RuntimeException("Failed : HTTP error code : "
+ response.getStatusLine().getStatusCode());
} else {
HttpEntity entity = response.getEntity();
if ((entity != null) && (entity.getContentLength() != 0)) {
// Use writing to output stream to prevent problems with chunked responses
ByteArrayOutputStream os = new ByteArrayOutputStream();
entity.writeTo(os);
output = new String(os.toByteArray(),"UTF-8");
} else {
// Handle case there is not content returned
System.out.println("Received no content (HTTP response code " + response.getStatusLine().getStatusCode() + " , reason: " + getReasonPhrase() +")");
}
}
The code above however doesn't solve the issue why you get an empty response. I its only handling the fact it is happening is a more elegant way.
I noted however that you require a username in the request. Are you sure the user exist on the device and in case of non existing user, should there be returned something else?
Related
I am trying to implement the MOT history API https://dvsa.github.io/mot-history-api-documentation/ and they give an example using CURL which works with the supplied api key successfully when using an online CURL tool.
I am trying to implement this in Android and realise I have to use something like HttpPost rather than CURL, this is my code:
//Tried with full URL and by adding the registration as a header.
//HttpPost httpPost = new HttpPost("https://beta.check-mot.service.gov.uk/trade/vehicles/mot-tests?registration=" + reg_selected);
HttpPost httpPost = new HttpPost("https://beta.check-mot.service.gov.uk/trade/vehicles/mot-tests");
httpPost.addHeader("Content-Type", "application/json");
httpPost.addHeader("Accept", "application/json+v6");
httpPost.addHeader("x-api-key", "abcdefgh123456");
httpPost.addHeader("registration", reg_selected);
StringEntity entity = new StringEntity(jsonObj.toString(), HTTP.UTF_8);
httpPost.setEntity(entity);
HttpClient client = new DefaultHttpClient();
try {
HttpResponse response = client.execute(httpPost);
if (response.getStatusLine().getStatusCode() == 200) {
InputStream inputStream = response.getEntity().getContent();
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String readLine = bufferedReader.readLine();
String jsonStr = readLine;
JSONObject myJsonObj = new JSONObject(jsonStr);
}else if (response.getStatusLine().getStatusCode() == 400){
//Bad Request Invalid data in the request. Check your URL and parameters
error_text = "Bad Request";
}else if (response.getStatusLine().getStatusCode() == 403){
//Unauthorised – The x-api-key is missing or invalid in the header
error_text = "Authentication error"; //<<<< FAILS HERE 403
}
response.getStatusLine().getStatusCode() returns • "403 – Unauthorised – The x-api-key is missing or invalid in the header".
However the x-api-key that I use works correctly with the online CURL test so the actual key is correct but how I am adding it to my android code request must be invalid or similar.
Can anyone throw any light as to the correct way to convert the CURL into Android java so that the server does not return 403?
Thanks
It's easy to do with Jsoup:
// CREATE CONNECTION
Connection conn=Jsoup.connect("URL_GOES_HERE");
// ADD POST/FORM DATA
conn.data("KEY", "VALUE");
// ADD HEADERS HERE
conn.header("KEY", "VALUE");
// SET METHOD AS POST
conn.method(Connection.Method.POST);
// ACCEPT RESPONDING CONTENT TYPE
conn.ignoreContentType(true);
try
{
// GET RESPONSE
String response = conn.execute().body();
// USE RESPONSE HERE
// CREATE JSON OBJECT OR ANYTHING...
} catch(HttpStatusException e)
{
int status = e.getStatusCode();
// HANDLE HTTP ERROR HERE
} catch (IOException e)
{
// HANDLE IO ERRORS HERE
}
Ps: I guess you are confused with Header and Post Data. The key etc (Credentials) must be used as Post Data and Content Type etc as Header.
I have written a REST client for this endpoint:
textmap.com/ethnicity_api/api
However, while passing it a name string like jennífer garcía in the POST params, and setting encoding to UTF-8, the response that I get is not the same string. How to get the same name in the response object?
Below is how I set the request and the response thatI get:
httpClient = HttpClients.createDefault();
httpPost = new HttpPost(baseurl);
StringEntity input = new StringEntity(inputJSON, StandardCharsets.UTF_8);
input.setContentType("application/json");
//input.setContentType("application/json; charset=UTF-8");
httpPost.setEntity(input);
response = httpClient.execute(httpPost);
if (response.getStatusLine().getStatusCode() != 200) {
throw new RuntimeException("Failed : HTTP error code : " + response.getStatusLine().getStatusCode());
}
BufferedReader br = new BufferedReader(new InputStreamReader((response.getEntity().getContent())));
output = org.apache.commons.io.IOUtils.toString(br);
System.out.println(output);
Value of the name in output is : jenn�fer garc�a
This is a completely different charset from what I had sent in the request. How can I get the same charset as I had sent in request?
Secondly, I want the same code to work in both Java-6 and Java-7. The above code is using Java-7 only. How can I make the code work for both these versions?
I think the BufferedReader is breaking the UTF8 encoding, so this is actually pretty unrelated to HTTP. On a side note, the br may not be needed at all.
I have a problem with a WebService on Android. I am getting a 400 error but there is no information on the ErrorStream.
What I am trying to do is a POST request on a WCF Webservice using JSON.
I must add that I have includeExceptionDetailInFaults Enabled on my Service. The last time I got a 400 error, it was because I hadn't defined the RequestProperty. Now I don't get any error in the stream.
Here is the code:
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
try {
// In my last error I had not included these lines. Maybe they are still wrong?
urlConnection.setRequestProperty("Content-Type", "application/json");
urlConnection.setRequestProperty("Accept", "application/json");
urlConnection.setRequestMethod("POST");
urlConnection.setDoOutput(true);
urlConnection.setChunkedStreamingMode(0);
OutputStream out = new BufferedOutputStream(urlConnection.getOutputStream());
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(out);
outputStreamWriter.write(jsonObject.toString(), 0, jsonObject.length());
outputStreamWriter.flush();
//outputStreamWriter.close();
int code = urlConnection.getResponseCode();
System.out.println(code);
if(code == 400) {
BufferedInputStream errorStream = new BufferedInputStream(urlConnection.getErrorStream());
InputStreamReader errorStreamReader = new InputStreamReader(errorStream);
BufferedReader bufferedReader = new BufferedReader(errorStreamReader);
StringBuilder builder = new StringBuilder();
String aux = "";
while ((aux = bufferedReader.readLine()) != null) {
builder.append(aux);
}
String output = builder.toString(); // The output is empty.
System.out.print(output);
}
Check Retrofit library from Square it's more easy and thin for GET/POST request and especially for REST. I suggest you to try it. It will make your life easy.
You can use different JSON parsers, error handlers, etc. Very flexible.
POST request definition using retrofit it's simple like this:
An object can be specified for use as an HTTP request body with the #Body annotation.
#POST("/users/new")
void createUser(#Body User user, Callback<User> cb);
Methods can also be declared to send form-encoded and multipart data.
Form-encoded data is sent when #FormUrlEncoded is present on the method. Each key-value pair is annotated with #Field containing the name and the object providing the value.
#FormUrlEncoded
#POST("/user/edit")
User updateUser(#Field("first_name") String first, #Field("last_name") String last);
After you define method inside your Java interface like shown above instantiate it:
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint("https://api.soundcloud.com")
.build();
MyInterface service = restAdapter.create(MyInterface.class);
And then you can call your method synchronously or asynchronously (in case you pass Callback instance).
service.myapi(requestBody);
See Retrofit documentation (http://square.github.io/retrofit/javadoc/index.html) and samples on GitHub for more details.
A 400 error might be occuring (and usually occurs in my case) because of incorrect URL or bad JSON format in post. please check those two
Using an HttpPost object will make your job a lot easier in my opinion
HttpPost post = new HttpPost(url);
if(payload != null){
try {
StringEntity entity = new StringEntity(payload,HTTP.UTF_8);
entity.setContentType(contentType);
post.setEntity(entity);
} catch (UnsupportedEncodingException e) {
LOG.d(TAG, "post err url : " + url);
LOG.e(TAG, "post err url" , e);
throw new Exception(1, e);
}
}
HttpResponse response=executeRequest(owner, post);
I have been running into some issues with a small Android project for school. I need to request a password from an online database via a .php by sending it the username. It should return an encrypted password. But there seems to be something wrong with the method I use to connect to the database and receive the password. LogCat gives me these:
Error in HTTP connection java.net.UnknownHostException: boekenapp.atwebpages.com
Error converting result java.lang.NullPointerException
Error parsing data org.json.JSONException: end of input at character 0 of
So my question: What did I do wrong?/What do I need to change to make it work?
The code:
public static String phpconnect(String name, String value) {
String result = "";
InputStream is = null;
//variables to send to database
ArrayList<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
nameValuePairs.add(new BasicNameValuePair(name,value));
//HTTP post
try{
HttpClient httpclient = new DefaultHttpClient();
URI connection = new URI("http://boekenapp.atwebpages.com/phpscript.php");
HttpPost httppost = new HttpPost(connection);
httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
HttpResponse response = httpclient.execute(httppost);
HttpEntity entity = response.getEntity();
is = entity.getContent();
} catch(Exception e) {
Log.e("log_tag", "Error in HTTP connection "+e.toString());
}
//convert response to string
try{
BufferedReader reader = new BufferedReader(new InputStreamReader(is,"iso-8859-1"),8);
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
}
is.close();
result=sb.toString();
} catch(Exception e){
Log.e("log_tag", "Error converting result "+e.toString());
}
//parse JSON data
try{
JSONArray jArray = new JSONArray(result);
for(int i=0;i<jArray.length();i++){
JSONObject json_data = jArray.getJSONObject(i);
Log.i("log_tag","userid: "+json_data.getInt("userid")+", password: "+json_data.getString("password"));
}
} catch(JSONException e) {
Log.e("log_tag", "Error parsing data "+e.toString());
}
return "";
}
The second try block will always execute, as you only log in the first catch block.
You should consider a return statement or a throw statement. An alternative is to embed the second try block inside the first one, but that's less readable maybe.
In your case, the problem is that the connection itself fails. Are you sure you have network up ? Can you ping the host from your computer and from Android (you can use adb shell ping <host> on CLI).
And don't truncate the error stacks on their first line, a stack has to be read fully, top-down until you find your piece of code that is causing the bug.
First of all the URL from this code used in a browser redirects to www.alotspace.com/error-404/ Just so you know.
Without testing, just looking at the code, I would start with checking the status line of the response. This is how
StatusLine statusLine = response.getStatusLine();
int statusCode = statusLine.getStatusCode();
if (statusCode == 200) {
// here all is OK, you can check for 404 and so on
}
Also I see you handling the response with a buffered reader putting everything in a StringBuilder further in code. A better alternative would be using gson.
But focus on the initial network related exception first. The other exceptions are just a result of the separate try/catch blocks (as #Snicolas pointed out already). Unknownhost sounds like no network. Being redirect would just return different output than expected, not unkownhost. To verify that browse to the url from your android device you're testing on.
I'm trying to login to a webpage, but even before that, I'm loading the page using HttpGet, and this is one the lines that's being returned,
ÓA;
That's all I could put, won't let me paste any other characters. But they are all like that, like I'm somehow getting the wrong encoding? Here is the code I am using to GET
HttpGet httpget = new HttpGet(url);
if(headers == null) {
headers = getDefaultHeaders();
}
for(String s : headers.keySet()) {
httpget.addHeader(s, headers.get(s));
}
HttpResponse response = getClient().execute(httpget);
HttpEntity entity = response.getEntity();
System.out.println("Status Line: " + response.getStatusLine());
if (entity != null) {
InputStream input = entity.getContent();
BufferedReader reader = new BufferedReader(new InputStreamReader(input));
String ln = "";
while((ln = reader.readLine()) != null) {
System.out.println("During Get - " + ln);
}
}
What am I doing wrong?
Thanks for any help.
If you need any more information like headers, just ask.
The following line is possibly the cause of your problems:
BufferedReader reader = new BufferedReader(new InputStreamReader(input));
You are creating a reader using the default characterset of your platform, and completely ignoring any character set that may be specified in the HTTP response headers.
If you are getting the same problem when reading the content the correct way, then it is possible that the server is at fault for not setting the response header correctly.
DO the entity reading like this:
String content = org.apache.http.util.EntityUtils.toString( entity );
System.out.println(content);
This is going to read it all for you so you can check what's being really returned.
Make sure that you didn't accidentally go to port 443 with a simple HTTP connection. Because in that case you will get back the SSL handshake instead of an HTTP response.