java - 进行 JSON 调用的 Android 应用程序从服务器获取意外数据,但相同的调用在浏览器中有效
问题描述
我有一个 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>
难道是服务器太慢而无法返回字符串,这就是它变得空的原因吗?
它总是崩溃,结果字符串为空:
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
根据您的问题描述:
如果我在浏览器中输入 URL,服务器代码将返回正确的字符串。
我假设您使用的是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" });
从 Android 8 开始:不允许明文 HTTP 流量
您必须将 URL 从http更改为https或添加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
提供错误凭据后,您的服务器正在发送纯文本消息
没有这样的用户
需要用有效的 JSON 字符串替换。
从我的角度来看,您提供的代码在解决这些问题后可以正常工作。
推荐阅读
- c# - 加速 CSharpCompilation.Emit?
- mongodb - 使用 GraphiQL 的 MongoDB 领域:嵌套对象属性上的空值(仅当链接时)
- html - 如何使用 z-index 或任何其他方法在引导轮播中定位元素?
- python - Discord Bot 未上线
- c - 如果条件没有正确输出但它在 C 中编译
- python - 更新帖子“'bytes'对象没有属性'_committed'”时Django管理面板错误
- jquery - jQuery make child element and parent element show on hover and fade out when not
- python - 如何获取具有唯一列值的行数(按其他列值分组)?
- python - 如何在 BeautifulSoup find_all ResultSet 之外继续过滤?
- javascript - 如何创建一个显示所有已连接用户的光标位置的页面?Node.js、ws、快递