How to POST data using Retrofit2? - java

I'm trying Retrofit 2.4.0 in my android application. Where do I have a login activity. I'm trying POST request but it return response code 404 in onResponse method. But It is working perfectly with POSTMAN and I getting correct data in log using HttpLoggingInterceptor. I have tried many methods to overcome this problem. My service code is.
#POST("user_login")
#FormUrlEncoded
Call<ResponseBody> user_login(#Field("email") String email, #Field("password") String password);
#Multipart
#POST("user_login")
Call<ResponseBody> user_login(#PartMap() Map<String, RequestBody> bodyMap);
#Multipart
#POST("user_login")
Call<Status> user_login(#Part("email") RequestBody email, #Part("password") RequestBody password);
#POST("user_login")
Call<JSONObject> user_login(#Body String credentials);
#POST("user_login")
Call<User> user_login(#Body LoginCredentials credentials);
none of above methods aren't working.
I'm posting some other methods Which are I am using too.
#Provides
#Singleton
Cache provideOkHttpCache(TUK application) {
int cacheSize = 10 * 1024 * 1024; // 10 MB
return new Cache(application.getCacheDir(), cacheSize);
}
#Provides
#Singleton
Gson provideGson() {
GsonBuilder builder = new GsonBuilder();
// builder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES);
return builder.create();
}
#Provides
#Singleton
OkHttpClient provideOkHttpClient(Cache cache) {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.cache(cache);
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(message -> Logger.wtf("AppModule", message));
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
builder.addInterceptor(interceptor);
return builder.build();
}
#Provides
#Singleton
Retrofit provideRetrofit(Gson gson, OkHttpClient client) {
return new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create(gson))
.baseUrl(TUK.BASE_URL)
.client(client)
.build();
}
but I'm trying other URLs too with GET method are working perfectly.
my base url is like "http://www.example.com/api/"
and my #PartMap() code is
public static Map<String, RequestBody> getMap(LoginCredentials credentials) {
Map<String, RequestBody> map = new HashMap<>();
map.put("email", RequestBody.create(MultipartBody.FORM, credentials.getEmail()));
map.put("password", RequestBody.create(MultipartBody.FORM, credentials.getPassword()));
return map;
}
my HttpLoggingInterceptor logging data is
<-- 404 https://www.example.com/api/user_login (718ms)
content-type: application/json; charset=utf-8
.
.
.
.
{"status":false,"message":"Invalid User Email Or Password."}
<-- END HTTP (60-byte body)
and my onResponse methods logging data is
Response{protocol=h2, code=404, message=, url=https://www.example.com/api/user_login}
At the end my question is how to POST data using Retrofit.
I have stuck at this point. I have tried other solutions from stackoverflow but no help. Please guys help me out this problem. Any help will be appreciated. Thank you.

You dont need to use the multipart body for POST request and it can be easily done by
below code. For example I have an base URL like http://example.com/api/
#FormUrlEncoded
#POST("login")
Call<LoginResponse> login(#Field("email") String email,
#Field("password") String password);
The final call will be redirected to http://example.com/api/login in above case.
So in this case my call will be directed to http://example.com/api/login.
Or the other problem can be in your URL. As your base url is this "http://www.example.com/api/" and by joining the API which API name, it becomes this "http://www.example.com/api//api/user_login" which contains two "//" that can cause error in finding your API because of that it throws 404( which is not found). So please adjust your base URL accordingly. Hope that helps somehow.

Looks like a server mistake, 404 is the error code returned by server. Retrofit has requested success. But any request that the return code is not 200 will be considered a failed request. Retrofit will callback onFailure() method, you can get response in the following way.
#Override
public void onFailure(Call<News> call, Throwable t) {
if(t instanceof HttpException){
ResponseBody body=((HttpException) t).response().errorBody();
}
}
Tell your back end the meaning of 404,it should not be returned here

I have solved this problem by setting builder.setLenient();
complete code is
#Provides
#Singleton
Gson provideGson() {
GsonBuilder builder = new GsonBuilder();
builder.setLenient(); // this line help me to get response.
// builder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES);
return builder.create();
}
thanks for your help guys.

Related

Is it possible to pass a string to a interface? Or at lest fetch from SharedPreferences inside a interface?

I'm trying to fetch a stored JWT from the SharedPreferences so I can send them as a Header in my requrest but I'm not able to get that data inside the API interface. Is this possible?
Thanks
SOLVED:
For anyone looking for this: you can pass a Header as a param, ex.:
#FormUrlEncoded
#POST("users/getUser")
Call<String> getUser(
#Header("Token") String token,
#Field("user") String user
);
also you can use an integrated Interceptor to adding this token on ALL requests like this:
public class AuthInterceptor
implements Interceptor {
#Override
public Response intercept(Chain chain)
throws IOException {
Request request = chain.request();
request = request.newBuilder()
.addHeader("Token", new MySharedPref().getToken())
.build();
return chain.proceed(request);
}
}
and after that add an instance of it on your OkHttpClient :
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient.addNetworkInterceptor(new AuthInterceptor());
Retrofit retrofit = new Retrofit.Builder()
...
.client(httpClient.build())
.build();

Retrofit unable to set new Token to Header of Requests

I have a retrofit Client that helps me set a header for any requests i make to my REST APIs. On user login i get the token from the server and set this token to the header of the requests. I save this token to SharedPreferences so that i can get it anytime i need to make requests to my REST APIs. The problem is that anytime i set a new token to my SharedPreferences file when a new user signs in, it still gets the old token instead of saving this new token to use for future requests.
This is my Retrofit Client below:
public class RetrofitClient {
private static Retrofit retrofit = null;
public static Retrofit getClient(String token) {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient okClient = new OkHttpClient();
Gson gson = new GsonBuilder()
.setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
.create();
okClient.interceptors().add(chain -> chain.proceed(chain.request()));
okClient.interceptors().add(chain -> {
Request original = chain.request();
Request request = original.newBuilder()
.header(Config.X_AUTH_TOKEN, "Bearer" + " " + token)
.method(original.method(), original.body())
.build();
Log.d("Authorization", token);
return chain.proceed(request);
});
okClient.interceptors().add(logging);
if (retrofit==null) {
retrofit = new Retrofit.Builder()
.baseUrl(Config.BASE_URL1)
.client(okClient)
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
}
return retrofit;
}
}
this is my codes for setting and getting the token
public String getToken() {
return prefs.getString(AuthUser.USER_TOKEN, "");
}
public void setToken(String token) {
SharedPreferences.Editor editor = prefs.edit();
editor.putString(AuthUser.USER_TOKEN, token);
editor.apply();
}
this is where i call my set token method to save the new token to SharedPreference
authUser.setToken(token);
I completely don't see how this is surprising. Your RetrofitClient is a confusingly (and arguably badly written) singleton. Let's go through a typical situation where this will fail.
You launch your app with a previously saved token. At first everything works fine. At some point you call RetrofitClient.getClient(token) and all requests succeed. After some time the server invalidates the token. You probably get a 403 response from your server, lauch the login screen again and update your token in your SharedPreferences. Here is where your problems begin. Although you saved your new token correctly, your RetrofitClient will do what singletons do and continue to return the first instantiation of itself stored in the private static Retrofit retrofit filed.
A quick workaround would be to add an invalidate method to your RetrofitClient. Something like.
public static void invalidate() {
this.retrofit = null;
}
Call it when you get your 403 response, or when you logout.
PS: Please move the following line if (retrofit==null) { at the beginning of your getClient method. Creating a new okHttp client, for nothing, every time someone calls getClient is just wasteful.
You can write intercepter to execute each time before network request happen.Create new file as HeaderIntercepter
public class HeaderIntercepter implements Interceptor {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
String token = context.getSharedPreferences(FILENAME, MODE_PRIVATE).getString("TOKEN","");
Request tokenRequest = request.newBuilder()
.addHeader("Authorization", "Bearer " + token)
.addHeader("Content-Type", "application/json")
.build();
return chain
.proceed(tokenRequest);
}
}
Add intercepter to okHttpclient
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.addInterceptor(new HeaderIntercepter());

Send a Post with url parameter with Retrofit 2

I want to send a POST with Retrofit 2. The url has some parameters:
#Headers({
"Accept: application/x-www-form-urlencoded;",
"User-Agent: my-app"
})
#FormUrlEncoded
#POST("server/directory/location.type")
`public Call<POJOStringValue> dataWithUr(#Path("arg1") String arg1, #Path("arg2"), String arg2);
The url looks like this
www.website.com/server/directory/location.type?arg1=value1&arg2=value2
I was requested to use a POST request. The values (value1 and value2) are dynamic at runtime. I started the project with Xamarin using HttpClient and now I'm rewriting it in Java native. In C# all I had to do was to concact the strings and send the resulting string in a single Post.
I tried to use #Path and the error was :
"server/directory/location.type" does not contain "{arg1}". (parameter #1)
Then, I tried to use #Query and the error was:
java.lang.IllegalArgumentException: Form-encoded method must contain at least one #Field.
Finally I tried with #Field the request never gets any response (I sette the connection timeout to 5 seconds)
Please help me, or tell me if I have to don't have any other choice but to use a GET request.
((EDIT))
Here is my code for the setup of the client:
private static void setupClient(){
final OkHttpClient client = new okhttp3.OkHttpClient.Builder()
.connectTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)
.retryOnConnectionFailure(false)
.build();
//define retrofit
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(iXUtils.getUrl_())
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
this.client_ = retrofit.create(RequestInterface.class);
}
The get() method:
public static RequestInterface get(){
return this.client_;
}
Here is how I call it:
public String callFunctionDB(String arg1, String arg2){
setupClient();
Call<POJOStringValue> call = get().dataWithUrlString(arg1, arg2);
try {
POJOStringValue response = call.execute().body();
String value = response.getValue();
int test = 0;
} catch (IOException e) {
String value = "it failded";
e.printStackTrace();
}
return "test";
}
I put the test=0 to be able to put a breaking point, it never gets there. Plus I called the method "callFunctionDB" in a doInbackground to avoid the android.os.NetworkOnMainThreadException.
Retrofit requires you to have at least one form parameter if you request form encoding. You have answered your own question -- you are using query parameters instead of POST fields, so that annotation is not necessary. Remove the #FormUrlEncoded annotation, and change your parameters to #Query annotations.

Retrofit get a parameter from a redirect URL

I am using Retrofit.
I have an endpoint that redirects to another endpoint. The latter (the endpoint that I end up at) has a parameter in its URL that I need. What is the best way to get the value of this parameter?
I cannot even figure out how to get the URL that I am redirected to, using Retrofit.
OkHttp's Response will give you the wire-level request (https://square.github.io/okhttp/3.x/okhttp/okhttp3/Response.html#request--). This will be the Request that initiated the Response from the redirect. The Request will give you its HttpUrl, and HttpUrl can give you its parameters' keys and values, paths, etc.
With Retrofit 2, simply use retrofit2.Response.raw() to get the okhttp3.Response and follow the above.
I am using retrofit. And I can get the redirect url following this way :
private boolean handleRedirectUrl(RetrofitError cause) {
if (cause != null && cause.getResponse() != null) {
List<Header> headers = cause.getResponse().getHeaders();
for (Header header : headers) {
//KEY_HEADER_REDIRECT_LOCATION = "Location"
if (KEY_HEADER_REDIRECT_LOCATION.equals(header.getName())) {
String redirectUrl = header.getValue();
return true;
}
}
}
return false;
}
Hope it could help someone.
Solution for this would be to use an interceptor e.g.
private Interceptor interceptor = new Interceptor() {
#Override
public okhttp3.Response intercept(Chain chain) throws IOException {
okhttp3.Response response = chain.proceed(chain.request());
locationHistory.add(response.header("Location"));
return response;
}
};
Add the interceptor to your HttpClient and add that to Retrofit(using 2.0 for this example)
public void request(String url) {
OkHttpClient.Builder client = new OkHttpClient.Builder();
client.followRedirects(true);
client.addNetworkInterceptor(interceptor);
OkHttpClient httpClient = client.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create())
.client(httpClient)
.build();
}
Now you have full access the the entire redirect history.

Android Retrofit Parameterized #Headers

I am using OAuth and I need to put the OAuth token in my header every time I make a request. I see the #Header annotation, but is there a way to make it parameterized so i can pass in at run time?
Here is the concept
#Header({Authorization:'OAuth {var}', api_version={var} })
Can you pass them in at Runtime?
#GET("/users")
void getUsers(
#Header("Authorization") String auth,
#Header("X-Api-Version") String version,
Callback<User> callback
)
Besides using #Header parameter, I'd rather use RequestInterceptor to update all your request without changing your interface. Using something like:
RestAdapter.Builder builder = new RestAdapter.Builder()
.setRequestInterceptor(new RequestInterceptor() {
#Override
public void intercept(RequestFacade request) {
request.addHeader("Accept", "application/json;versions=1");
if (isUserLoggedIn()) {
request.addHeader("Authorization", getToken());
}
}
});
p/s : If you are using Retrofit2, you should use Interceptor instead of RequestInterceptor
Since RequestInterceptor is not longer available in Retrofit 2.0
Yes, you can pass them in runtime. As a matter of fact, pretty much exactly as you typed it out. This would be in your API interface class, named say SecretApiInterface.java
public interface SecretApiInterface {
#GET("/secret_things")
SecretThing.List getSecretThings(#Header("Authorization") String token)
}
Then you pass the parameters to this interface from your request, something along those lines: (this file would be for example SecretThingRequest.java)
public class SecretThingRequest extends RetrofitSpiceRequest<SecretThing.List, SecretApiInteface>{
private String token;
public SecretThingRequest(String token) {
super(SecretThing.List.class, SecretApiInterface.class);
this.token = token;
}
#Override
public SecretThing.List loadDataFromNetwork() {
SecretApiInterface service = getService();
return service.getSecretThings(Somehow.Magically.getToken());
}
}
Where Somehow.Magically.getToken() is a method call that returns a token, it is up to you where and how you define it.
You can of course have more than one #Header("Blah") String blah annotations in the interface implementation, as in your case!
I found it confusing too, the documentation clearly says it replaces the header, but it DOESN'T!
It is in fact added as with #Headers("hardcoded_string_of_liited_use") annotation
Hope this helps ;)
The accepted answer is for an older version of Retrofit. For future viewers the way to do this with Retrofit 2.0 is using a custom OkHttp client:
OkHttpClient httpClient = new OkHttpClient.Builder()
.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Builder ongoing = chain.request().newBuilder();
ongoing.addHeader("Accept", "application/json;versions=1");
if (isUserLoggedIn()) {
ongoing.addHeader("Authorization", getToken());
}
return chain.proceed(ongoing.build());
}
})
.build();
Retrofit retrofit = new Retrofit.Builder()
// ... extra config
.client(httpClient)
.build();
Hope it helps someone. :)
Retrofit 2.3.0
OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder();
okHttpClientBuilder
.addInterceptor(new Interceptor() {
#Override
public okhttp3.Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Request.Builder newRequest = request.newBuilder().header("Authorization", accessToken);
return chain.proceed(newRequest.build());
}
});
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(GithubService.BASE_URL)
.client(okHttpClientBuilder.build())
.addConverterFactory(GsonConverterFactory.create())
.build();
I am using this to connect to GitHub.

Categories