首页 > 解决方案 > 改造 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 上得到了正确的回应,后者不太可能。感谢您提供的任何帮助。

标签: androidretrofitrest

解决方案


我想到了!对于任何面临这个问题的人来说,问题在于 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() 可用且不会返回错误。这就是问题所在!


推荐阅读