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) {
.....
}
Related
I am using the retrofit library for API call and I want to send the parameter to my server using the "form-data" method. I found this question on StackOverflow, but there is no solution yet. Please guide me and let me know if I can provide more details for the same. Thank you
Why don't you use Multipart?
This is an example of using it for a simple user info with phone number, password and a prfile pic:
In your Activity:
final RequestBody rPhoneNumber = RequestBody.create(MediaType.parse("text/plain"), "sample phone number");
final RequestBody rPassword = RequestBody.create(MediaType.parse("text/plain"), "sample phone password");
final MultipartBody.Part rProfilePicture = null;
Retrofit.Builder builder = new Retrofit.Builder().addConverterFactory(GsonConverterFactory.create()).baseUrl(baseUrl).client(Cookie.cookie.build());
Retrofit retrofit = builder.build();
final RequestHandler requestHandler = retrofit.create(RequestHandler.class);
rProfilePicture = MultipartBody.Part.createFormData("file", file.getName(), RequestBody.create(MediaType.parse("image/*"),file)); //sample image file that you want to upload
Call<ServerMessage> call; //ServerMessage is a class with a String to store and convert json response
call = requestHandler.editProfile(rPhoneNumber, rPassword, rProfilePicture); //editProfile is in RequestHandler interface
call.enqueue(new Callback<ServerMessage>() {
#Override
public void onResponse (Call < ServerMessage > call2, Response < ServerMessage > response){
//your code here
}
#Override
public void onFailure (Call < ServerMessage > call, Throwable t) {
//your code here
}
});
In RequestHandler.java interface:
#Multipart
#POST("/api/change-profile")
Call<ServerMessage> editProfile(#Part("phoneNumber") RequestBody rPhoneNumber,
#Part("oldPassword") RequestBody rPassword,
#Part MultipartBody.Part rProfilePicture);
In ServerMessage.java:
public class ServerMessage {
private String message;
public String getMessage() {
return message;
}
}
This sample should help:
public interface AuthService {
#POST("register")
#Headers("Content-Type:application/x-www-form-urlencoded")
#FormUrlEncoded
Call<LoginResponse> loginSocial(#Field("provider") String provider, #Field("access_token") String accessToken }
I know this might be late. I came this same challenge and this is what works for me
val requestBody: RequestBody = MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("avatar", imageFile.toString())
.build()
#POST("avatar")
fun uploadProfilePicture(
#Header("Authorization") header: String?,
#Body avatar:RequestBody
): Call<UserResponse>
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.
I am trying to make a post request through Retrofit2 in which email is sent as key value pair (not as POJO like using #Body with Retrofit2), by using this #FormUrlEncoded I am able to hit the API but "#" sign gets converted to "%40" and same case for any other special symbols. Can anyone help sending the email to API server using Retrofit2 without using the #Body in Retrofit2.
My code is as below:
public RetroWrapper (Context context, Object listener) {
this.context = context;
this.listener = listener;
OkHttpClient.Builder builder = new OkHttpClient().newBuilder();
builder.readTimeout(40, TimeUnit.SECONDS);
builder.connectTimeout(20, TimeUnit.SECONDS);
if (BuildConfig.DEBUG) {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
builder.addInterceptor(interceptor);
}
builder.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
// Request request = chain.request().newBuilder().addHeader("Content-Type", "text/json").build();
Request request = chain.request().newBuilder().addHeader("Content-Type", "application/x-www-form-urlencoded").build();
return chain.proceed(request);
}
});
OkHttpClient client = builder.build();
retrofit= new Retrofit.Builder()
.baseUrl(BuildConfig.WEBSERVICE_BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addConverterFactory(ScalarsConverterFactory.create())
.client(client).build();
}
public void loginCheckDirectPost(Map<String,String> stringStringMap){
RetroServices.RetroServicePostLoginReqDirectPost retroSrvcGetFeeds = retrofit.create(RetroServices.RetroServicePostLoginReqDirectPost.class);
Call<PostLoginResp> getFeedsCall = retroSrvcGetFeeds.CALL(stringStringMap);
getFeedsCall.enqueue((Callback<PostLoginResp>) listener);
}
public interface RetroServicePostLoginReqDirectPost{
#FormUrlEncoded
#POST("token")
Call<PostLoginResp> CALL(#FieldMap Map<String, String> params);
}
Your content is being URL encoded , so "#" becomes %40.
You will need to modify it at the server to decode %40 into "#";
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
I have next interface declaration:
public interface FundaService
{
#GET( "/feeds/Aanbod.svc/json/{key}" )
Observable<JsonResponse> queryData( #Path( "key" ) String key, #Query("type" ) String type, #Query( "zo" ) String search, #Query( "page" ) int page, #Query( "pagesize" ) int pageSize );
}
That I use after with Retrofit. What would be an elegant way of testing that I didn't make mistakes in URL declaration and query parameters?
I see that I can mock web layer and check urls with parameters.
UPDATE
I modified it:
public interface FundaService
{
String KEY_PATH_PARAM = "key";
String FEED_PATH = "/feeds/Aanbod.svc/json/{" + KEY_PATH_PARAM + "}";
String TYPE_QUERY_PARAM = "type";
String SEARCH_QUERY_PARAM = "zo";
String PAGE_QUERY_PARAM = "page";
String PAGESIZE_QUERY_PARAM = "pagesize";
#GET( FEED_PATH )
Observable<JsonResponse> queryData( #Path( KEY_PATH_PARAM ) String key, #Query( TYPE_QUERY_PARAM ) String type,
#Query( SEARCH_QUERY_PARAM ) String search, #Query( PAGE_QUERY_PARAM ) int page,
#Query( PAGESIZE_QUERY_PARAM ) int pageSize );
}
And partially testing it, like:
public class FundaServiceTest
{
#Test
public void PathKeyIsCorrect()
throws Exception
{
assertThat( FundaService.KEY_PATH_PARAM ).isEqualTo( "key" );
}
#Test
public void FeedPathIsCorrect()
throws Exception
{
assertThat( FundaService.FEED_PATH ).isEqualTo( "/feeds/Aanbod.svc/json/{key}" );
}
}
You can use an okhttp interceptor to inspect the final request built by retrofit without using a mock http server. It gives you a chance to inspect the request a bit earlier. Suppose we want to test the following interface -
public interface AwesomeApi {
#GET("/cool/stuff")
Call<Void> getCoolStuff(#Query(("id"))String id);
}
The first test runs 'validateEagerly` to do a validation of the entire interface. Useful to have in case your other test cases don't touch all the interface methods. The second test is an example of how you might verify a specific call is generating the expected url.
public class AwesomeApiTest {
#Test
public void testValidInterface() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://www.example.com/")
.addConverterFactory(GsonConverterFactory.create())
// Will throw an exception if interface is not valid
.validateEagerly()
.build();
retrofit.create(AwesomeApi.class);
}
#Test(expected = NotImplementedException.class)
public void testCoolStuffRequest() throws Exception {
OkHttpClient client = new OkHttpClient();
client.interceptors().add(new Interceptor() {
#Override
public com.squareup.okhttp.Response intercept(Chain chain) throws IOException {
final Request request = chain.request();
// Grab the request from the chain, and test away
assertEquals("HTTP methods should match", "GET", request.method());
HttpUrl url = request.httpUrl();
// Test First query parameter
assertEquals("first query paramter", "id", url.queryParameterName(0));
// Or, the whole url at once --
assertEquals("url ", "http://www.example.com/cool/stuff?id=123", url.toString());
// The following just ends the test with an expected exception.
// You could construct a valid Response and return that instead
// Do not return chain.proceed(), because then your unit test may become
// subject to the whims of the network
throw new NotImplementedException();
}
});
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://www.example.com/")
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
AwesomeApi awesomeApi = retrofit.create(AwesomeApi.class);
awesomeApi.getCoolStuff("123").execute();;
}
}
I got this idea from browsing retrofit's own tests. Other people's tests are often great inspiration!