HTTP request with Retrofit2 get "CLEARTEXT communication not supported" Error - android

i have a request in my Api interface:
#FormUrlEncoded
#POST(ApiStatics.authorizeURL)
Call<Oauth2Model> authorizer(#Field("grant_type") String grantType,
#Field("username") String userName,
#Field("password") String password,
#Field("client_id") String clientID,
#Field("client_secret") String clientSecret);
whit this getApi method:
public static MyAPI getApi() {
if (api == null) {
OkHttpClient client;
if (GuildsApp.isDebug()) {
HttpLoggingInterceptor bodyInterceptor = new HttpLoggingInterceptor();
bodyInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
HttpLoggingInterceptor headerInterceptor = new HttpLoggingInterceptor();
headerInterceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS);
client = new OkHttpClient.Builder().readTimeout(5, TimeUnit.MINUTES).writeTimeout(5, TimeUnit.MINUTES).connectTimeout(5, TimeUnit.MINUTES).addInterceptor(bodyInterceptor).addInterceptor(headerInterceptor).build();
} else {
client = new OkHttpClient.Builder().readTimeout(5, TimeUnit.MINUTES).writeTimeout(5, TimeUnit.MINUTES).connectTimeout(5, TimeUnit.MINUTES).build();
}
Retrofit.Builder builder = new Retrofit.Builder();
builder.baseUrl(BASE_URL);
builder.addConverterFactory(GsonConverterFactory.create());
builder.client(client);
Retrofit retrofit = builder.build();
api = retrofit.create(MyAPI.class);
}
return api;
}
i use this api in my fragment with this method:
private void login(String user, String pass) {
ApiManager.getApi().authorizer("password", user, pass, ApiStatics.CID,ApiStatics.CSecret).enqueue(new Callback<Oauth2Model>() {
#Override
public void onResponse(Call<Oauth2Model> call, Response<Oauth2Model> response) {
Log.e("authorize token",""+response.body().access_token);
}
#Override
public void onFailure(Call<Oauth2Model> call, Throwable t) {
Log.e("Failure ",""+t.getLocalizedMessage());
}
});
}
when i run project, request call onFailure method with this Log:
E/Failure: CLEARTEXT communication not supported: [ConnectionSpec(cipherSuites=[TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_128_GCM_SHA256, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_3DES_EDE_CBC_SHA], tlsVersions=[TLS_1_2, TLS_1_1, TLS_1_0], supportsTlsExtensions=true), ConnectionSpec(cipherSuites=[TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_128_GCM_SHA256, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_3DES_EDE_CBC_SHA], tlsVersions=[TLS_1_0], supportsTlsExtensions=true)]
my BASE_URL is HTTP , not HTTPS.
my login method called with login("",""); that "" are accepted on server,
how can i solve this error?
i use Retrofit 2.1.0 , my test device is nexus 5x android N

Add this annotation at the top of the class.
#PowerMockIgnore("javax.net.ssl.*")
it will ignore SSL check.

by adding a simple getApi method inside my fragment instead of ApiManager, problem solved!
private MyAPI getApi(){
Retrofit.Builder builder = new Retrofit.Builder();
builder.baseUrl(ApiManager.BASE_URL);
builder.addConverterFactory(GsonConverterFactory.create());
Retrofit retrofit = builder.build();
return retrofit.create(MyAPI.class);
}
and my login method now is:
private void login(String user, String pass) {
getApi().authorizer("password", user, pass, ApiStatics.CID,ApiStatics.CSecret).enqueue(new Callback<Oauth2Model>() {
#Override
public void onResponse(Call<Oauth2Model> call, Response<Oauth2Model> response) {
Log.e("authorize token",""+response.body().access_token);
}
#Override
public void onFailure(Call<Oauth2Model> call, Throwable t) {
Log.e("Failure ",""+t.getLocalizedMessage());
}
});
}
can some body explain to me what is difference between implementing getApi method in my ApiManager and in my fragment?

Try adding this to your manifest at application level
android:usesCleartextTraffic="true"
here are more details about it
CLEARTEXT communication not supported on Retrofit

Related

How to get the request URL for a Retrofit object?

I need to log the request URL that Retrofit creates. I don't find any getter methods on Retrofit object or web interface that is generated via Retrofit. The following is my code, where I want to log the address of every request:
public void onRequestFoods() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Const.BASE_LOCAL)
.addConverterFactory(GsonConverterFactory.create())
.build();
FoodOrderInterface foodInterface = retrofit.create(FoodOrderInterface.class);
Log.d(TAG, "onRequestFoods: request url: ");
foodInterface.listFoods().enqueue(new Callback<FoodResponse>() {
#Override
public void onResponse(Call<FoodResponse> call, Response<FoodResponse> response) {
List<Food> foods = response.body().getBody().getFoods();
mPresenter.onResponse((ArrayList<Food>) foods);
}
#Override
public void onFailure(Call<FoodResponse> call, Throwable t) {
mPresenter.onRequestFailed(t.getMessage());
}
});
}
I think what you need is http logging interceptor the github repo has a straightforward example of how to get it up and running

How to send post request with basic auth in retrofit?

In my code, I want to send post request with basic auth.
Here is my postman screenshot :
here is my apiInterface class
#FormUrlEncoded
#POST("GetBarcodeDetail")
Call<PreliminaryGoodsAcceptResponse> PRELIMINARY_GOODS_ACCEPT_RESPONSE_CALL(#Field("ProcName") String procName, #Field("Barcode") String barcode, #Field("LangCode") String langCode);
here is my apiclient
public class ApiClient {
public static final String BASE_URL = "http://192.**********";
private static Retrofit retrofit = null;
private static OkHttpClient sClient;
public static Retrofit getClient() {
if(sClient == null) {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
sClient = new OkHttpClient.Builder()
.addInterceptor(new HttpLoggingInterceptor(HttpLoggingInterceptor.Logger.DEFAULT))
.addInterceptor(interceptor)
.build();
}
if (retrofit==null) {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(sClient)
.build();
}
return retrofit;
}
}
My question is how can i send post request,using header :
Header Username : EBA Token :
34242353453456563DSFS
This is so far the easiest method i have ever tried for "Basic Authentication".
Use the below code to generate the auth header (API/Repository class)
var basic = Credentials.basic("YOUR_USERNAME", "YOUR_PASSWORD")
Pass this as header to the webservice call (API/Repository class)
var retrofitCall = myWebservice.getNewsFeed(basic)
Add the basic header as parameter (Retrofit Webservice interface class)
#GET("newsfeed/daily")
fun getNewsFeed(#Header("Authorization") h1:String):Call<NewsFeedResponse>
Sorry, my code is in Kotlin, but can be easily translated to Java.
References: https://mobikul.com/basic-authentication-retrofit-android/
make header like this way..
private Retrofit getClient(final Context context) {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient.Builder client = new OkHttpClient.Builder();
client.readTimeout(60, TimeUnit.SECONDS);
client.writeTimeout(60, TimeUnit.SECONDS);
client.connectTimeout(60, TimeUnit.SECONDS);
client.addInterceptor(interceptor);
client.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
if (context == null) {
request = request
.newBuilder()
.build();
} else {
request = request
.newBuilder()
.addHeader("Authorization", "Bearer " + AppSetting.getStringSharedPref(context, Constants.USER_KEY_TOKEN, ""))
.build();
}
return chain.proceed(request);
}
});
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client.build())
.addConverterFactory(GsonConverterFactory.create())
.build();
return retrofit;
}
Use Header annotation
#FormUrlEncoded
#POST("GetBarcodeDetail")
Call<PreliminaryGoodsAcceptResponse> PRELIMINARY_GOODS_ACCEPT_RESPONSE_CALL(#Header("Authorization") token: String,#Field("ProcName") String procName, #Field("Barcode") String barcode, #Field("LangCode") String langCode);
Simple-Retrofit-API-request-and-Data-Loading Here I just add the project where create the API call to access data from database using retrofit library; which is leading library to access data on network. And display the accessed data in the List format. Create the Simple Android Studio Project with Empty Activity. Create the Adapter and activity item to show normal lists in android app. Now Create the App class extending Application, as Application class is a singleton that you can access from any activity or anywhere else you have a Context object.
You can check the more details about Application class from https://github.com/codepath/android_guides/wiki/Understanding-the-Android-Application-Class Why extend an Application class? https://developer.android.com/reference/android/app/Application.html
Add android:name=".YourApplication" i.e. class name extending the Application class in android. and class will be like public class YourApplication extends Application Init the Retrofit in Application class
//network code start
//init http logger
httpLoggingInterceptor = new HttpLoggingInterceptor();
httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
// init client client = new OkHttpClient.Builder()
.addInterceptor(httpLoggingInterceptor)
.addInterceptor(new Interceptor() {
#Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Request request2 = request.newBuilder().build();
return chain.proceed(request2);
}
}).connectTimeout(30, TimeUnit.SECONDS).writeTimeout(30, TimeUnit.SECONDS).readTimeout(30, TimeUnit.SECONDS).build();
Gson gson = new GsonBuilder().setLenient().create();
Retrofit mRetrofit = new Retrofit.Builder().baseUrl(Constants.API_BASE_URL).client(client).addConverterFactory(GsonConverterFactory.create(gson)).build();
mWebservice = mRetrofit.create(Webservice.class);
While Constants.API_BASE_URL is base url Create the Webervice.class where you can call the API with parameters e.g. In case of GET Method:
#GET("webservices/GetAllClientsDemoRetro.php")
Call updateChatStatus();
In case of POST method:
#FormUrlEncoded
#Headers({"Content-Type: application/x-www-form-urlencoded"})
#POST("webservices/GetAllClientsDemoRetro.php")
Call updateChatStatus();
You can See the more in details About Retrofit on Official API declaration here: http://square.github.io/retrofit/
We can parse the values with POJO i.e. Setter and Getter, using the Parceble class. Since parsing key name should be equal to the value we are receiving from the JSON response. POJO class should be declared like public class ClientData implements Parcelable { then declare the keys in the class, key values means
public class ClientData implements Parcelable
{
public String client_id;
public String company_name;
public String address_line;
public String city;
public String pincode;
public String state;
public String country;
}
Now using Alt+Enter i.e. select the option Add Parceble Implementation and press enter. Then automatically parceble class will be added. Also you have to add Setter and Getter method in class using Alt + Insert. Note: Don’t add the Setter and Getter methods for CREATER: Creater<> method If you want to use different key that JSON response key, then you should use Serialization. When I was using same key then its is like public String client_id; But when I am using the Serialization, then I can use like #Serializattion(“client_id”) public String ClientID; Now last but not a list, We call the API using retrofit, and use the response to view the Item in list-
RetroFitApplication.getWebservice().updateChatStatus().enqueue(new Callback() {
#Override public void onResponse(Call call, Response response) {
Log.d("retrofilt success", "" + response.body());
if (response.body() != null) {
clientResponceData = response.body();
Gson gson = new Gson();
String body = gson.toJson(response.body());
Log.d("retrofilt success2", "clientData" + clientResponceData.getResponse());
if (clientResponceData.getResponse() != null) {
initRV();
}
} else {
// Empty Client List Toast.makeText(ClientList.this, "Empty List", Toast.LENGTH_SHORT).show();
}
}
#Override public void onFailure(Call call, Throwable t) {
Log.d("retrofilt error", "" + t);
Toast.makeText(ClientList.this, "No Internet Connection", Toast.LENGTH_SHORT).show();
}
});
By using the Construction in Adapter, we can use the values from the response. Guys I added this repository to get the Entire idea of calling the API and get the response from server using the Retrofit Library. I write this entire documents in details with simple word.

My Retrofit en-queue failing every time

i have a Retrofit service which is calling the following API http://api2.bigoven.com/recipe/1962911?api_key=my_api_key
this is my Retrofit Singleton Interface
public interface RecipeAPI {
String BASE_URL = "http://api2.bigoven.com/";
#GET("recipe/{id}")
Call<Recipe> getRecipe(#Path("id") int ID,
#Query("api_key") String apiKey);
class RecipesFetcher{
private static RecipeAPI service;
public static RecipeAPI getInstance(){
if (service==null) {
Retrofit retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(BASE_URL)
.build();
service = retrofit.create(RecipeAPI.class);
return service;
} else {
return service;
}
}
}
}
this is the Full logcat:
http://codepad.org/lP1AhHCK
this is where i use the Retrofit interface but it always executes onFailure :
RecipeAPI.RecipesFetcher.getInstance().getRecipe(recipeID,API_KEY).enqueue(new Callback<Recipe>() {
#Override
public void onResponse(Call<Recipe> call, Response<Recipe> response) {
if (response.isSuccessful()){
//some code
}
else{
Toast.makeText(RecipeActivity.this, "Something went wrong!", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onFailure(Call<Recipe> call, Throwable t) {
Toast.makeText(RecipeActivity.this, "Check your Internet Connection!", Toast.LENGTH_SHORT).show();
}
});
}
i tried to get call.toString()
it give me this messageretrofit2.executorcalladapterfactory$executorcallbackcall#428dc288
what is wrong with my code ?
Are you able to provide the endpoint documentation (the contract).
Should you be providing the API key in the header?
If you shouldn't, have you checked that the API key isnt null?
--edit--
Could you change the protocol to https?
The below docs mention that you are required to use https instead of http.
http://api2.bigoven.com/web/documentation/authentication-process

Retrofit2 Replacing query parameters

I am using Retrofit2 and sending request with input parameters as follows. But retrofit automatically converts + symbol to %2B. How to encode this and send as + itself
Relevant code
1) Interface
#POST("/registrationapi.php")
Call<RegistrationPOJO> registrationResponse(#Query("firstname") String firstname , #Query("lastname") String lastname,
#Query("email") String email, #Query("password") String password,
#Query("uid") String uid, #Query("mobile") String mobile,
#Query("key") String key
);
2) RestClient
private APIInterface service;
public RestClient() {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient.addInterceptor(logging);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(AppConfiguration.BASEURL)
.addConverterFactory(GsonConverterFactory.create())
.client(httpClient.build())
.build();
service = retrofit.create(APIInterface.class);
}
public void getRegistrationInfo(final Registration context, String firstname, String lastname, String email,
String password, String uid, String mobile, String key
){
Call<RegistrationPOJO> reg =service.registrationResponse(firstname,lastname,email,password,uid,mobile,key);
reg.enqueue(
new Callback<RegistrationPOJO>() {
#Override
public void onResponse(Call<RegistrationPOJO> call, Response<RegistrationPOJO> response) {
success = response.isSuccessful();
if(success) {
//Handle success flow
} else {
//Handle error flow
}
}
#Override
public void onFailure(Call<RegistrationPOJO> call, Throwable t) {
//Handle error flow
}
}
);
}
My mobile number is having + symbol at the beginning. From the
retrofit logs, I can see this is converted like
mobile=%2B11111111111 while sending the request.
I am expecting encoding and making input parameter like
mobile=+11111111111
Corresponding gradle dependencies are
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.google.code.gson:gson:2.6.2'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
compile 'com.squareup.okhttp3:logging-interceptor:3.3.1'
As per anurag's suggestion. I have changed parameter to
#Query(value = "mobile" , encoded=true) String mobile
and its working as expected
Try using encoded = true in query params.
Call<ResponseBody> method(#Query(value = "+11111111111", encoded = true) String mobile) {
.....
}

How can I handle empty response body with Retrofit 2?

Recently I started using Retrofit 2 and I faced an issue with parsing empty response body. I have a server which responds only with http code without any content inside the response body.
How can I handle only meta information about server response (headers, status code etc)?
Edit:
As Jake Wharton points out,
#GET("/path/to/get")
Call<Void> getMyData(/* your args here */);
is the best way to go versus my original response --
You can just return a ResponseBody, which will bypass parsing the response.
#GET("/path/to/get")
Call<ResponseBody> getMyData(/* your args here */);
Then in your call,
Call<ResponseBody> dataCall = myApi.getMyData();
dataCall.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Response<ResponseBody> response) {
// use response.code, response.headers, etc.
}
#Override
public void onFailure(Throwable t) {
// handle failure
}
});
If you use RxJava, then it's better to use Completable in this case
Represents a deferred computation without any value but only indication for completion or exception. The class follows a similar event pattern as Reactive-Streams: onSubscribe (onError|onComplete)?
http://reactivex.io/RxJava/2.x/javadoc/io/reactivex/Completable.html
in the accepted answer:
#GET("/path/to/get")
Observable<Response<Void>> getMyData(/* your args here */);
If the endpoint returns failure response code, it will still be in the onNext and you will have to check the response code yourself.
However, if you use Completable.
#GET("/path/to/get")
Completable getMyData(/* your args here */);
you will have only onComplete and onError.
if the response code is success it will fire the onComplete else it will fire onError.
If you are using rxjava, use something like :
#GET("/path/to/get")
Observable<Response<Void>> getMyData(/* your args here */);
With kotlin, using the return type Call<Void> still throws IllegalArgumentException: Unable to create converter for retrofit2.Call<java.lang.Void>
Using Response instead of Call resolved the issue
#DELETE("user/data")
suspend fun deleteUserData(): Response<Void>
Here is an example Kotlin in MVVM with service, Repository and ViewModel:
Service:
#POST("/logout")
suspend fun logout(#Header("Authorization") token: String):Response<Unit>
Repository:
//logout
private val mLogoutResponse = MutableLiveData<String>()
val logoutResponse: LiveData<String>
get() {
return mLogoutResponse
}
suspend fun logout(token: String) {
try {
val result=quizzerProfileApi.logout(token)
if(result.code()!=0)
{
mLogoutResponse.postValue(result.code().toString())
}
} catch (e: Exception) {
Log.d("ProfileRepository", "logout: Error: $e")
}
}
ViewModel:
fun logout(token: String) {
viewModelScope.launch {
repository.logout(token)
}
}
val logoutResponseCd: LiveData<String>
get() = repository.logoutResponse
in Activity:
private fun logout() {
myViewModel.logout(token)
myViewModel.logoutResponseCd.observe(this, Observer {
if(it!="0"){
Log.d(TAG, "logout: code= $it")
finish()
}
else
Toast.makeText(this, "Error logging out: $it", Toast.LENGTH_SHORT).show()
})
}
Here is how I used it with Rx2 and Retrofit2, with PUT REST request:
My request had a json body but just http response code with empty body.
The Api client:
public class ApiClient {
public static final String TAG = ApiClient.class.getSimpleName();
private DevicesEndpoint apiEndpointInterface;
public DevicesEndpoint getApiService() {
Gson gson = new GsonBuilder()
.setLenient()
.create();
OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder();
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
okHttpClientBuilder.addInterceptor(logging);
OkHttpClient okHttpClient = okHttpClientBuilder.build();
apiEndpointInterface = new Retrofit.Builder()
.baseUrl(ApiContract.DEVICES_REST_URL)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build()
.create(DevicesEndpoint.class);
return apiEndpointInterface;
}
The interface:
public interface DevicesEndpoint {
#Headers("Content-Type: application/json")
#PUT(ApiContract.DEVICES_ENDPOINT)
Observable<ResponseBody> sendDeviceDetails(#Body Device device);
}
Then to use it:
private void sendDeviceId(Device device){
ApiClient client = new ApiClient();
DevicesEndpoint apiService = client.getApiService();
Observable<ResponseBody> call = apiService.sendDeviceDetails(device);
Log.i(TAG, "sendDeviceId: about to send device ID");
call.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer<ResponseBody>() {
#Override
public void onSubscribe(Disposable disposable) {
}
#Override
public void onNext(ResponseBody body) {
Log.i(TAG, "onNext");
}
#Override
public void onError(Throwable t) {
Log.e(TAG, "onError: ", t);
}
#Override
public void onComplete() {
Log.i(TAG, "onCompleted: sent device ID done");
}
});
}
You can try this one
Retrofit retrofit = new Retrofit.Builder().baseUrl(baseUrl)
.addConverterFactory(new NullOnEmptyConverterFactory())
.client(okHttpClient).build();
class NullOnEmptyConverterFactory extends Converter.Factory {
#Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
final Converter<ResponseBody, ?> delegate = retrofit.nextResponseBodyConverter(this, type, annotations);
return (Converter<ResponseBody, Object>) body -> {
if (body.source().exhausted()) return null;
return delegate.convert(body);
};
}
}
Kotlin, Retrofit
#POST
suspend fun accountVerification(
#Body requestBody: RequestBody
): Response<Unit>
and success can be check using
if (response.isSuccessful) { }

Categories

Resources