android - 改造 Android 响应 - 在 POST 400 上返回 HTML 而不是 JSON
问题描述
我是安卓新手。我有一个我在 Django 中开发的休息 API,我希望我的 Android 应用程序与之交互。我在 Android Studio 中使用 Retrofit 为了通过 API 保存新用户,我将四个字段(staff_id、email、password、password2)传递给我的 API。
它在 Postman 中一切正常,并为成功(HTTP 200)和错误请求(HTTP 400)返回适当的响应
这是正确的 Android 应用程序上的 201 响应的图像
但是,Android 中的 HTTP 400 响应不返回 JSON。在 Postman 中,我得到了 HTTP 400 { "error_message": "that email is already in use.", "response": "Error" }
但在 Android 中,我没有从 response.errorBody() 获得相同的 JSON 响应。相反,我得到这样的 HTML 响应:
<DOCTYPE HTML PUBLIC"-//W3C//DTD HTML 4.0 transitional//EN>
<html>
<head>
<title>400</title>
<meta http-eqiv="Cache-Control"
content ="no-cache"/>
</head>
<body>
<p>
Bad Request
</p>
</body>
</html>
这是我的 Android Studio 代码 Api.java:`
package nkemokongwu.com.ng.leave.activities.api;
import nkemokongwu.com.ng.leave.activities.models.NewStaff;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.Header;
import retrofit2.http.Headers;
import retrofit2.http.POST;
public interface Api {
@FormUrlEncoded
@POST("authenticate/api/api_signup/")
Call<ResponseBody> signup(
@Field("staff_id") int staff_id,
@Field("email") String email,
@Field("password") String password,
@Field("password2") String password2
);
@FormUrlEncoded
@POST("authenticate/api/api_login")
Call<ResponseBody> userLogin(
@Field("email") String email,
@Field("password") String password
);
}
改造客户端.Java
package nkemokongwu.com.ng.leave.activities.api;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class RetrofitClient {
private static final String BASE_URL = "http://nsitf.pythonanywhere.com/";
private static RetrofitClient mInstance;
private Retrofit retrofit;
private RetrofitClient() {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
public static synchronized RetrofitClient getInstance() {
if (mInstance == null) {
mInstance = new RetrofitClient();
}
return mInstance;
}
public Api getApi() {
return retrofit.create(Api.class);
}
}
SignUpActivity.java
package nkemokongwu.com.ng.leave.activities.authentication;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.util.Patterns;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import java.io.IOException;
import java.util.Objects;
import nkemokongwu.com.ng.leave.R;
import nkemokongwu.com.ng.leave.activities.api.RetrofitClient;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class SignUpActivity extends AppCompatActivity implements View.OnClickListener {
private EditText editTextstaff_id,editTextemail,editTextpassword,editTextpassword2;
private static final String TAG = "MyActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sign_up);
editTextstaff_id = findViewById(R.id.editTextstaff_id);
editTextemail = findViewById(R.id.editTextemail);
editTextpassword = findViewById(R.id.editTextpassword);
editTextpassword2 = findViewById(R.id.editTextpassword2);
findViewById(R.id.text_already_have_account).setOnClickListener(this);
findViewById(R.id.buttonSignUp).setOnClickListener(this);
}
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
private void userSignUp() {
String temp = editTextstaff_id.getText().toString().trim();
int staff_id = 0;
if (!"".equals(temp)) {
staff_id = Integer.parseInt(temp);
} else {
editTextstaff_id.setError("Staff ID Is Required");
editTextstaff_id.requestFocus();
return;
}
String email = editTextemail.getText().toString().trim();
String password = editTextpassword.getText().toString().trim();
String password2 = editTextpassword2.getText().toString().trim();
if (email.isEmpty()) {
editTextemail.setError("Email is required");
editTextemail.requestFocus();
return;
}
if (!Patterns.EMAIL_ADDRESS.matcher(email).matches()) {
editTextemail.setError("Enter a valid email");
editTextemail.requestFocus();
return;
}
if (password.isEmpty()) {
editTextpassword.setError("Password required");
editTextpassword.requestFocus();
return;
}
if (password2.isEmpty()) {
editTextpassword2.setError("Confirmation Password Required");
editTextpassword2.requestFocus();
return;
}
if (!Objects.equals(password, password2)) {
editTextpassword.setError("Passwords Do Not Match");
editTextpassword.requestFocus();
return;
}
Call <ResponseBody> call =RetrofitClient
.getInstance()
.getApi()
.signup(staff_id,email,password,password2);
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if (response.code() == 201){
try {
Toast.makeText(SignUpActivity.this,response.body().string(),Toast.LENGTH_LONG).show();
} catch (IOException e) {
e.printStackTrace();
}
} else if (response.code() == 400){
try {
Toast.makeText(SignUpActivity.this,response.errorBody().string(), Toast.LENGTH_LONG).show();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Toast.makeText(SignUpActivity.this,t.toString(),Toast.LENGTH_LONG).show();
}
});
//
}
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public void onClick(View v) {
switch (v.getId()) {
case R.id.buttonSignUp:
userSignUp();
break;
case R.id.text_already_have_account:
startActivity(new Intent(this, LoginActivity.class));
break;
}
}
} `
我尝试了以下方法;1. 使用 @Headers 注释 Signup 调用以指定 Accept 和 Content-Type 2. 修改我的 Django API 代码以包含内容类型。这些都没有帮助。我还搜索了 Stack Overflow 以找到解决方案,但我尝试过的所有方法仍然无效。我将不胜感激任何帮助指出我正确的方向。是我的 android 代码还是我必须修改 Django Rest API?鉴于我在 Postman 上得到了正确的回应,后者不太可能。感谢您提供的任何帮助。
解决方案
我想到了!对于任何面临这个问题的人来说,问题在于 API 本身。在我的 Django Rest API Serializer 中,我有一些回复,例如:
return Response(status=status.HTTP_400_BAD_REQUEST)
return Response(status=status.HTTP_401_UNAUTHORIZED)
现在这是 Retrofit 的问题,因为它将响应从成功响应(代码 200)更改为错误响应,这反过来又在尝试查看改造中的response.body()时返回错误。响应仅在response.errorbody()中可用,它返回一些奇怪的 okhttp 东西,根本没有帮助!相反,我应该在我的 API 中做的是:首先定义一个空列表:
context={}
然后添加错误值
context['status'] = 'Error'
context['message'] = 'Invalid credentials'
context['code']=status.HTTP_401_UNAUTHORIZED
return Response(data=context)
这将返回 Postman Image Of Response
这是完美的,可以根据需要轻松建模为 Java 类或 Pojo。由于 API 的实际响应码为 200,因此 response.body() 可用且不会返回错误。这就是问题所在!
推荐阅读
- python - 失败后Odoo删除工作?
- bash - 在许多文件中切割列 - Bash - 如何将结果保存在不同的文件中?
- swift - 使用 NSFetchResultsController 时 TableView 不显示任何内容
- php - GCP PHP移动上传的文件不起作用
- docker - 如何在 Kubernetes 中管理 nginx 负载?
- java - 在 Jenkins 上使用 maven 运行 selenium 脚本时看到 Maven 编译器错误
- django - 如何在 Django 中重置密码
- php - php-输出目录不存在
- java - Firebase 谷歌身份验证两步
- laravel - 从 laravel 下载 Dropbox 文件的问题