简体   繁体   中英

Retrofit POST method returns error 400

I would like to call a POST Method(Django REST framework) in Retrofit with a For that, I call as follow from the postman and works fine for me.

邮递员 I have done the Android part as follows:

API INTERFACES:

public interface SOService {

   @FormUrlEncoded
@POST("/api-auth/")
Call<Tokken> get_tokken(@Field("username") String username, @Field("password") String password);

}

APIUtility class as

public class ApiUtils {

    public static final String BASE_URL = "http://10.0.2.2:8000/";

    public static SOService getSOService() {
        return RetrofitClient.getClient(BASE_URL).create(SOService.class);
    }
}

RetrofitClient as :

public class RetrofitClient {

    private static Retrofit retrofit = null;

    public static Retrofit getClient(String baseUrl) {

        OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();



        if (retrofit==null) {


            retrofit = new Retrofit.Builder()
            .baseUrl(baseUrl)
            .client(clientBuilder.build())
            .addConverterFactory(GsonConverterFactory.create())
            .build();
        }
        return retrofit;
    }
}

And finally, call the function as following

private void signIn() {
    boolean isValid = validate();
    if (isValid) {

        mService.get_tokken(user.getText().toString(), password.getText().toString()).enqueue(new Callback<Tokken>() {
            @Override
            public void onResponse(Call<Tokken> call, Response<Tokken> response) {
                if (response.isSuccessful()) {

                    System.out.println("Te lo imprimo");
                    System.out.println(response.body().toString());
                    Toast.makeText(LoginActivity.this, "tokken recibido", Toast.LENGTH_SHORT).show();
                } else {
                    int statusCode = response.code();
                    // handle request errors depending on status code
                }
            }

            @Override
            public void onFailure(Call<Tokken> call, Throwable t) {
                Toast.makeText(LoginActivity.this, "Error al recibir tokken", Toast.LENGTH_SHORT).show();

                t.printStackTrace();
                Log.d("MainActivity", "error loading from API");

            }
        });
        /*startActivity(new Intent(this, RolSelection.class));*/
    }
}

And the final result is always the same, I get this message in the server:

[17/Apr/2018 01:12:33] "POST /api-auth/ HTTP/1.1" 200 52

But in Android I get this:

I/System.out: Te lo imprimo Tokken{tokken='null'}

Any idea why the tokken is null? When I use postman I get a result.

UPDATE with the Tokken class:

public class Tokken {

    @SerializedName("tokken")
    @Expose
    private String tokken;

    public String getTokken() {
        return tokken;
    }

    public void setTokken(String tokken) {
        this.tokken = tokken;
    }

    @Override
    public String toString() {
        return "Tokken{" +
                "tokken='" + tokken + '\'' +
                '}';
    }
}

UPDATE: change field name in Tokken class from @SerializedName("tokken") into @SerializedName("token") as on attached screenshot from Postman in response you get "token" key but not "tokken" .

Maybe it's not the root cause but one of the problem for sure: you didn't configure your retrofit client properly, you missed to add http client:

retrofit = new Retrofit.Builder()
                .baseUrl(baseUrl)
                .client(clientBuilder.build())
                .addConverterFactory(GsonConverterFactory.create())
                .build();

I think your problem is that your bean object ( Token ) has a GSON annotation to serialize as tokken .

if you change your @SerializedName("tokken”) to @SerializedName("token”) , and assuming the request has no other problems, that should correctly deserialize.

I don't know why you call it tokken in one side and token in another, but I would avoid it if possible, unless you're planning on adding unit test to ensure future you doesn't forget about this.

Another way to pass a Body (which may be easier, depending…) is to use a HashMap:

@POST("/api-auth/“) 
Call<Tokken> get_tokken(@Body HashMap<String, Object> body);

Then you can pass a map containing:

[“username”, “YourUsername”],
[“password”, “YourPassword”]

But without extra logging (or a proxy in-between to see what is actually going on), it's very hard to debug.

Remember that your question's title is "Retrofit POST method returns error 400”. But your token being null is a side-effect of your API returning 400 error. 400 Error is “BAD REQUEST”, so something is not what your server expects.

nitpick : I'd rename your API method to getToken(…) or fetchToken(…) to be more Java/Kotlin/Android friendly and also more self-explanatory.

Two changes

  1. change tokken to token
@SerializedName("tokken")
@Expose
private String tokken;

to

@SerializedName("token")
@Expose
private String tokken;
  1. remove the extra \\ in SOService , it should be
@FormUrlEncoded
@POST("api-auth/")

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM