我有一个 Android 应用程序,它进行服务器调用并返回 JSON。
如果我在浏览器中输入 URL,服务器代码将返回正确的字符串。但它会在 Java 应用程序中创建异常(http 和 https 服务器调用存在不同的问题)。
https://www.problemio.com/auth/mobile_login.php?login=test.name@gmail.com&password=130989
返回此字符串:
[{"user_id":"1601470","email":"test.name@gmail.com","first_name":"TestName","last_name":null}]
这是解析 JSON 但给出异常的 Java 调用:
@Override
protected void onPostExecute(String result)
{
try {
dialog.dismiss();
} catch (Exception ee) {
// nothing
}
if ( connectionError == true )
{
Toast.makeText(getApplicationContext(), "Please try again. Possible Internet connection error.", Toast.LENGTH_LONG).show();
}
if ( result != null && result.equals( "no_such_user") )
{
Toast.makeText(getApplicationContext(), "Your email and password do not match out records. Please try again or create and account.", Toast.LENGTH_LONG).show();
//final TextView login_error = (TextView) findViewById(R.id.login_error);
}
else
{
Log.d( "CONNECTION*** ERRORS: " , "ok 3 and result length: " + result.length() );
String firstName = null;
String lastName = null;
String email = null;
String user_id = null;
try
{
JSONArray obj = new JSONArray(result);
JSONObject o = obj.getJSONObject(0);
firstName = o.getString("first_name");
lastName = o.getString("last_name");
email = o.getString("email");
user_id = o.getString("user_id");
}
catch ( Exception e )
{
Log.d( "JSON ERRORS: " , "something happened ****" + e.getMessage() );
}
// 1) First, write to whatever local session file that the person is logged in
// - I just really need user id and name and email. And store that.
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(
LoginActivity.this);
if ( user_id != null && user_id.trim().length() > 0 && !user_id.trim().equals("null") )
{
prefs.edit()
.putString("first_name", firstName)
.putString("last_name", lastName)
.putString("email", email)
.putString("user_id", user_id)
.putBoolean("working", true)
.commit();
if ( user_id.equals("1"))
{
prefs.edit()
.putString("community_subscription", "1")
.commit();
}
}
}
}
}
这是异常消息:
End of input at character 0 of
看起来字符串长度为 0 个字符。
知道为什么会发生这种情况吗?在我将网站切换到 https 之前,此调用可以正常工作。
服务器还进行 http 调用。如果我将其更改为 https,它会返回一大堆 HTML,这很奇怪,因为我实际上并没有将其发回。
这是我的 doInBackground 方法:
@Override
protected String doInBackground(String... theParams)
{
String myUrl = theParams[0];
final String myEmail = theParams[1];
final String myPassword = theParams[2];
String charset = "UTF-8";
Authenticator.setDefault(new Authenticator()
{
@Override
protected PasswordAuthentication getPasswordAuthentication()
{
return new PasswordAuthentication( myEmail, myPassword.toCharArray());
}
});
编辑
如果我的 doInBackground 方法位于
public class DownloadWebPageTask extends AsyncTask<String, Void, String>
是否是服务器返回字符串的速度太慢,导致字符串变为 null?
它总是崩溃,结果字符串为空:
JSONArray obj = new JSONArray(result);
编辑2
完整代码如下:
package com.problemio;
import java.io.InputStream;
import java.net.Authenticator;
import java.net.HttpURLConnection;
import java.net.PasswordAuthentication;
import java.net.URL;
import java.net.URLEncoder;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import org.json.JSONArray;
import org.json.JSONObject;
import com.flurry.android.FlurryAgent;
import utils.SendEmail;
import android.app.Dialog;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
public class LoginActivity extends BaseActivity
{
//private TextView textView;
private Dialog dialog;
public static final String REQUEST_METHOD = "GET";
public static final int READ_TIMEOUT = 15000;
public static final int CONNECTION_TIMEOUT = 15000;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
FlurryAgent.onStartSession(this, "8CA5LTZ5M73EG8R35SXG");
setContentView(R.layout.login);
//final TextView emailask = (TextView) findViewById(R.id.email_ask);
// Show form for login_email
final EditText loginEmail = (EditText) findViewById(R.id.login_email);
//String name = loginEmail.getText().toString();
// Show field for password
final EditText password = (EditText) findViewById(R.id.password);
//String text = password.getText().toString();
//Log.d( "First parameters: " , "Login email: " + loginEmail + " AND login password: " + text);
// Show button for submit
Button submit = (Button)findViewById(R.id.submit);
submit.setOnClickListener(new Button.OnClickListener()
{
public void onClick(View v)
{
String email = loginEmail.getText().toString();
String pass = password.getText().toString();
//Set the email pattern string
// Pattern pattern = Pattern.compile(".+@.+\\.[a-z]+");
// //Match the given string with the pattern
// Matcher m = pattern.matcher(email);
// //check whether match is found
// boolean matchFound = m.matches();
// TODO: VALIDATE!!!
if ( email == null || email.trim().length() < 2 )
{
Toast.makeText(getApplicationContext(), "Please enter a valid email address.", Toast.LENGTH_LONG).show();
}
else
if ( pass == null || pass.trim().length() < 2 )
{
Toast.makeText(getApplicationContext(), "Please enter a correct password.", Toast.LENGTH_LONG).show();
}
else
{
sendFeedback(pass, email);
}
}
});
// Show button for submit
Button forgot_password = (Button)findViewById(R.id.forgot_password);
forgot_password.setOnClickListener(new Button.OnClickListener()
{
public void onClick(View v)
{
Toast.makeText(getApplicationContext(), "Please wait...", Toast.LENGTH_LONG).show();
Intent intent = new Intent(LoginActivity.this, ForgotPasswordActivity.class);
LoginActivity.this.startActivity(intent);
}
});
// Now add messaging for creating a profile
final TextView create_profile_message = (TextView) findViewById(R.id.create_profile_message);
Button create_profile = (Button)findViewById(R.id.create_profile);
create_profile.setOnClickListener(new Button.OnClickListener()
{
public void onClick(View v)
{
//sendEmail("Create Profile Clicked", "From Login screen, someone clicked on the create profile button" );
Intent myIntent = new Intent(LoginActivity.this, CreateProfileActivity.class);
LoginActivity.this.startActivity(myIntent);
}
});
}
public void sendFeedback(String pass , String email)
{
String[] params = new String[] { "http://www.problemio.com/auth/mobile_login.php", email, pass };
DownloadWebPageTask task = new DownloadWebPageTask();
task.execute(params);
}
// Subject , body
public void sendEmail( String subject , String body )
{
String[] params = new String[] { "http://www.problemio.com/problems/send_email_mobile.php", subject, body };
SendEmail task = new SendEmail();
task.execute(params);
}
public class DownloadWebPageTask extends AsyncTask<String, Void, String>
{
private boolean connectionError = false;
@Override
protected void onPreExecute( )
{
dialog = new Dialog(LoginActivity.this);
dialog.setContentView(R.layout.please_wait);
dialog.setTitle("Logging You In");
TextView text = (TextView) dialog.findViewById(R.id.please_wait_text);
text.setText("Please wait while you are being logged in...");
dialog.show();
}
// orig
@Override
protected String doInBackground(String... theParams)
{
String myUrl = theParams[0];
final String myEmail = theParams[1];
final String myPassword = theParams[2];
String charset = "UTF-8";
Authenticator.setDefault(new Authenticator()
{
@Override
protected PasswordAuthentication getPasswordAuthentication()
{
return new PasswordAuthentication( myEmail, myPassword.toCharArray());
}
});
String response = null;
String stringUrl = "https://www.problemio.com/auth/mobile_login.php?login=test.name@gmail.com&password=130989";
String result = "";
String inputLine;
try
{
String query = String.format("login=%s&password=%s",
URLEncoder.encode(myEmail, charset),
URLEncoder.encode(myPassword, charset));
final URL url = new URL( myUrl + "?" + query );
final HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setDoOutput(true);
conn.setRequestMethod("POST");
conn.setRequestProperty("login", myEmail);
conn.setRequestProperty("password", myPassword);
conn.setDoOutput(true);
conn.setUseCaches(false);
conn.connect();
final InputStream is = conn.getInputStream();
final byte[] buffer = new byte[8196];
int readCount;
final StringBuilder builder = new StringBuilder();
while ((readCount = is.read(buffer)) > -1)
{
builder.append(new String(buffer, 0, readCount));
}
response = builder.toString();
}
catch (Exception e)
{
sendEmail ( "Login Activity 1 Network Error" , "Error: " + e.getMessage() );
}
return response;
}
@Override
protected void onPostExecute(String result)
{
super.onPostExecute(result);
try {
dialog.dismiss();
} catch (Exception ee) {
// nothing
}
if ( connectionError == true )
{
Toast.makeText(getApplicationContext(), "Please try again. Possible Internet connection error.", Toast.LENGTH_LONG).show();
}
if ( result != null && result.equals( "no_such_user") )
{
Toast.makeText(getApplicationContext(), "Your email and password do not match out records. Please try again or create and account.", Toast.LENGTH_LONG).show();
//final TextView login_error = (TextView) findViewById(R.id.login_error);
}
else
{
String firstName = null;
String lastName = null;
String email = null;
String user_id = null;
try
{
JSONArray obj = new JSONArray(result);
Log.d( "CONNECTION*** ERRORS: " , ".....5" );
JSONObject o = obj.getJSONObject(0);
firstName = o.getString("first_name");
lastName = o.getString("last_name");
email = o.getString("email");
user_id = o.getString("user_id");
}
catch ( Exception e )
{
Log.d( "JSON ERRORS: " , "some crap happened ****" + e.getMessage() );
}
// 1) First, write to whatever local session file that the person is logged in
// - I just really need user id and name and email. And store that.
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(
LoginActivity.this);
if ( user_id != null && user_id.trim().length() > 0 && !user_id.trim().equals("null") )
{
prefs.edit()
.putString("first_name", firstName)
.putString("last_name", lastName)
.putString("email", email)
.putString("user_id", user_id)
.putBoolean("working", true)
.commit();
if ( user_id.equals("1"))
{
prefs.edit()
.putString("community_subscription", "1")
.commit();
}
}
}
}
}
// TODO: see if I can get rid of this
public void readWebpage(View view)
{
DownloadWebPageTask task = new DownloadWebPageTask();
task.execute(new String[] { "http://www.problemio.com/auth/mobile_login.php" });
}
@Override
public void onStop()
{
super.onStop();
}
}
最佳答案
问题#1
根据您的问题描述:
The server code returns the right string if I enter the URL into a browser.
我假设您正在使用HTTP GET。但是,您在代码中使用了 HTTP POST:
String query = String.format("login=%s&password=%s",
URLEncoder.encode(myEmail, charset),
URLEncoder.encode(myPassword, charset));
final URL url = new URL( myUrl + "?" + query );
final HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setDoOutput(true);
conn.setRequestMethod("POST"); // <----------- replace with "GET"
问题#2
conn.setDoOutput(true);
当设置为 true 时,请求方法将更改为 POST,因为 GET 或 DELETE 不能有请求正文。要继续执行 GET 请求,您必须设置 conn.setDoOutput(false);
另外,注释掉这些行:
//conn.setRequestProperty("login", myEmail);
//conn.setRequestProperty("password", myPassword);
//conn.setDoOutput(true);
问题 #3
task.execute(new String[] { "http://www.problemio.com/auth/mobile_login.php" });
From Android 8: Cleartext HTTP traffic not permitted
您必须将 URL 从 http 更改为 https 或在 list 中添加 android:usesCleartextTraffic="true"
。这只会影响运行 API 级别 23+ 的设备。 23+之前默认允许http。
<?xml version="1.0" encoding="utf-8"?>
<manifest ...>
<uses-permission android:name="android.permission.INTERNET" />
<application
...
android:usesCleartextTraffic="true"
...>
...
</application>
</manifest>
对我来说,使用 https 工作正常。
问题#4
提供错误的凭据后,您的服务器将发送纯文本消息
no_such_user
需要将其替换为有效的 JSON 字符串。
从我看来,您提供的代码在解决这些问题后可以正常工作。
关于java - Android 应用程序进行 JSON 调用从服务器获取意外数据,但相同的调用在浏览器中有效,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58714758/