chntgomez chntgomez - 3 months ago 10
Android Question

Whats wrong with this retrofit call?

Im trying to send a POST and build a response using retrofit with Android.

I have managed to send GET with no problems but now I need to send a POST with some body elements.

public static <S> S createAccessService(Class<S> serviceClass, String code, String redirectUri,
String clientId, String clientSecret) {
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
String basicCredentials = clientId+":"+clientSecret;
byte[] encodeBytes = Base64.encode(basicCredentials.getBytes(), Base64.NO_WRAP);
httpClient.addInterceptor(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request original = chain.request();
RequestBody body = new FormBody.Builder()
.add("grant_type", "authorization_code")
.add("code", code)
.add("redirect_uri", redirectUri).build();
Request request = original.newBuilder()
.addHeader("Authorization", "Basic "+new String(encodeBytes))
.method(original.method(), original.body())
.put(body)
.build();
return chain.proceed(request);
}
});

OkHttpClient client = httpClient.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(API_BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();

return retrofit.create(serviceClass);


The POST I'm trying to build looks like this one:

POST /api/token HTTP/1.1
Host: accounts.spotify.com
Authorization: Basic ***********************************
Cache-Control: no-cache
Postman-Token: 99177da6-1606-3145-689d-bc4b09b3f212
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&redirect_uri=http%3A%2F%2Flocalhost%3A8888%2Fcallback&code*********************************************************************************************************************************************************


Obviously the Keys are hidden for security.

And this is the POJO that Im using to store the response.

public class Session {

private String scope;

private String expires_in;

private String token_type;

private String refresh_token;

private String access_token;
private String error;

public String getError_description() {
return error_description;
}

public void setError_description(String error_description) {
this.error_description = error_description;
}

public String getError() {
return error;
}

public void setError(String error) {
this.error = error;
}

private String error_description;

public String getScope ()
{
return scope;
}

public void setScope (String scope)
{
this.scope = scope;
}

public String getExpires_in ()
{
return expires_in;
}

public void setExpires_in (String expires_in)
{
this.expires_in = expires_in;
}

public String getToken_type ()
{
return token_type;
}

public void setToken_type (String token_type)
{
this.token_type = token_type;
}

public String getRefresh_token ()
{
return refresh_token;
}

public void setRefresh_token (String refresh_token)
{
this.refresh_token = refresh_token;
}

public String getAccess_token ()
{
return access_token;
}

public void setAccess_token (String access_token)
{
this.access_token = access_token;
}




}


I have changed a lot of things but no matter what, the response cannot instanciate a new Session object, returning null always.

Added:

This is the interface Im using:

@FormUrlEncoded
@POST("/api/token")
Call<Session> getSession();


What am I doing wrong?

Thanks

Answer

Well, if you try to send a POST request with retrofit2, you should try this:

First, create a Interface, in this interface you declare the body contents for POST request:

public interface ISesion {

    @FormUrlEncoded
    @POST("/users/login")
    Call<Sesion> inSesion(@Field("username") String usuario, @Field("password") String password);
}

The parameter @Field is the body content that you use in your POST request

Now, you can make this in your MainActivity:

public class MainActivity extends AppCompatActivity {
    private Retrofit retrofit; //your Retrofit Object
    private ISesion iSesion; //your interface

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    retrofit = new Retrofit.Builder()
            .baseUrl(getString(R.string.url_base)) //your base URL ("http://www.myexample.com")
            .addConverterFactory(GsonConverterFactory.create())
            .build();

    iSesion = retrofit.create(ISesion.class);
    Call<Sesion> call = iSesion.inSesion(usuario, contrasena); //you get this fields from editText, for example, and you pass it to the method in your interface
        call.enqueue(new Callback<Sesion>() {
            @Override
            public void onResponse(Call<Sesion> call, Response<Sesion> response) {
                code = response.code();
                switch (code) {
                    case 200:
                        Intent intent = new Intent(getActivity(), MainActivity.class);
                        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
                        startActivity(intent);

                        break;
                    case 401:
                        error = response.body().isError();
                        mensaje = response.body().getMensaje();
                        if (error) {
                            Snackbar.make(view, mensaje, Snackbar.LENGTH_LONG).show();
                        }
                        break;
                }
            }

            @Override
            public void onFailure(Call<Sesion> call, Throwable t) {
                t.printStackTrace();
                Snackbar.make(view, getString(R.string.error_general), Snackbar.LENGTH_LONG).show();
            }
        });
    }
}

The model used in the method Call<Sesion> is the next:

public class Sesion {

    @SerializedName("error")
    private boolean error;

    @SerializedName("message")
    private String mensaje;

    public boolean isError() {
        return error;
    }

    public String getMensaje() {
        return mensaje;
    }
}  

EDIT:

If you want to send your token in the body content you couldtry this:

Call<Sesion> call = iSesion.inSesion("Basic " + generatedToken); // here is the authentication token
        call.enqueue(new Callback<Sesion>() {
            @Override
            public void onResponse(Call<Sesion> call, Response<Sesion> response) {
                code = response.code();
                switch (code) {
                    case 200:
                        Intent intent = new Intent(getActivity(), MainActivity.class);
                        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
                        startActivity(intent);

                        break;
                    case 401:
                        error = response.body().isError();
                        mensaje = response.body().getMensaje();
                        if (error) {
                            Snackbar.make(view, mensaje, Snackbar.LENGTH_LONG).show();
                        }
                        break;
                }
            }

            @Override
            public void onFailure(Call<Sesion> call, Throwable t) {
                t.printStackTrace();
                Snackbar.make(view, getString(R.string.error_general), Snackbar.LENGTH_LONG).show();
            }
        });
    }

and this is the Interface:

public interface ISesion {

    @FormUrlEncoded
    @POST("/users/login")
    Call<Sesion> inSesion(@Field("token") String generatedToken);
}