This is my UserService Interface
#GET(Constants.Api.URL_LOGIN)
String loginUser(#Field("email") String email, #Field("password") String pass, #Field("secret") String secret, #Field("device_id") String deviceid, #Field("pub_key") String pubkey, #Field("device_name") String devicename);
In the activity I am calling
retrofit = new Retrofit.Builder()
.baseUrl(Constants.Api.URL_BASE)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
service = retrofit.create(UserService.class);
String status = service.loginUser(loginedt.getText().toString(), passwordedt.getText().toString(), secret, device_id, pub_key, device_name);
This creates an exception
java.lang.IllegalArgumentException: Unable to create call adapter for class java.lang.String
for method UserService.loginUser
What am I doing wrong?
Gradle :
compile 'com.squareup.retrofit:retrofit:2.+'
compile 'com.squareup.retrofit:adapter-rxjava:2.0.0-beta1'
compile 'com.squareup.retrofit:converter-gson:2.0.0-beta1'
Since you have included addCallAdapterFactory(RxJavaCallAdapterFactory.create()), you are looking to use Observable's to manage your calls. In your interface, explicitly give the parameterized Observable instead of a Call --
#GET(Constants.Api.URL_LOGIN)
Observable<String> loginUser(#Field("email") String email, #Field("password") String pass, #Field("secret") String secret, #Field("device_id") String deviceid, #Field("pub_key") String pubkey, #Field("device_name") String devicename);
and then your service methods create observables for you that you can subscribe to or use as the start of an observable pipeline.
Observable<String> status = service.loginUser(loginedt.getText().toString(), passwordedt.getText().toString(), secret, device_id, pub_key, device_name);
status.subscribe(/* onNext, onError, onComplete handlers */);
Aleksei, if you need the most simple solution to get String result from Retrofit library, than you have to do this several calls:
At first, Gradle dependecies:
compile 'com.squareup.retrofit2:retrofit:2.0.0-beta4'
compile 'com.squareup.retrofit2:converter-scalars:2.0.0-beta4'
Your modified UserService Interface
#GET(Constants.Api.URL_LOGIN)
Call< String> loginUser(#Field("email") String email, #Field("password") String pass, #Field("secret") String secret, #Field("device_id") String deviceid, #Field("pub_key") String pubkey, #Field("device_name") String devicename);
Service client creation code:
static UserService SERVICE_INSTANCE = (new Retrofit.Builder()
.baseUrl(Constants.Api.URL_BASE)
.addConverterFactory(ScalarsConverterFactory.create())
.build()).create(UserService.class);
Calling the request:
SERVICE_INSTANCE.loginUser(*all your params*).execute().body();
I hope, the solution is clear and shows simple String receive approach. If you need some another data parser, please, take a look at the conversters list here Retrofit CONVERTERS.
Related
I want to ask that do I need to create new Interfaces for every POST GET request I make which have different URL .
For ex
I made 1 interface for register and other for Login other for getting Friends. Cant I just make 1 general post and get method where I can send URL , params to send and record response?
No you don't need to create new interface or new client for each request!
Inside a interface you can create multiple method as you want and as your requirement.
For Login and fro Registration method name will be different, your parameter will not same. So you can create method as you need.
//When Base Url like "http://exmaple.com/"
#GET("Service/registration")
Call<RegResult> getRegistered(#Query("name") String name,
#Query("email") String email,
#Query("dob") String dob,
#Query("name") String name
);
#GET("Service/login")
Call<LoginResult> getLogin(#Query("username") String username,
#Query("pass") String pass
);
#GET("Service/profile")
Call<ProfileResult> getProfile(#Query("userid") String userid
);
You can also use same client because your base url is same.
If base url is diffrent you can also use same client like this..
public class ApiClient {
private static Retrofit retrofit = null;
public static Retrofit getClient(String base_url) {
if (retrofit==null) {
retrofit = new Retrofit.Builder()
.baseUrl(base_url)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}
Now you can set different base url.
Creating object of interface...
String BASE_URL = "http://exmaple.com/";
ApiInterface apiService = ApiClient.getClient(BASE_URL).create(ApiInterface.class);
Calling method..
String user_id = "1";
Call< ProfileResult > call = apiService.getProfile(user_id);
Getting result
call.enqueue(new Callback< ProfileResult >() {
#Override
public void onResponse(Call< ProfileResult >call, Response< ProfileResult > response) {
Profile profile = response.body().getResults();
}
#Override
public void onFailure(Call< ProfileResult >call, Throwable t) {
// Log error here since request failed
Log.e(TAG, t.toString());
}
});
Hop you got your answer .... for farther query fill free to ask...
I'm familiar with how to use dynamic URLs with Retrofit2 but having issue sending username & password in the request. The webAPI works by entering a URL, a login prompt appears on the screen for a username & password, after authentication a JSON response is displayed. The interface is defined for a dynamic URL:
#GET
public Call<User> getJSON(#Url String string);
My request is as follows:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(API_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
LoginService service = retrofit.create(LoginService.class);
Call<User> call = service.getJSON("https://username:password#api.url.com/");
call.enqueue(new Callback<User>() {
#Override
public void onResponse(Call<User> call, retrofit2.Response<User> response) {
System.out.println("Response status code: " + response.code());
I'm certain the URL is correct as it works in the browser & but I keep getting error the username & password aren't correct?
I/System.out: Response status code: 401
Also, as far as I can tell I can only use #GET rather than #POST because whenever I try #POST the response code is:
I/System.out: Response status code: 405
At first I tried to follow something similar to this post using an encoded flag because it's an example of how to use #PATH & #URL with Retrofit2 but didn't have any success. That's why I tried the username:password# prepend to the URL. Most of the other examples all use the #POST method.
Any feedback or ideas on how I can authenticate? Thanks
Not sure how to do it in retrofit, but you can add it via an OkHttp interceptor --
OkHttpClient client = new OkHttpClient().newBuilder().addNetworkInterceptor(
new Interceptor() {
#Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request request = chain.request();
HttpUrl url = request.url();
url = url.newBuilder().username("username").password("password").build();
Request newRequest = request.newBuilder().url(url).build();
return chain.proceed(newRequest);
}
}
).build();
be sure to add this client to your retrofit instance --
Retrofit retrofit = new Retrofit.Builder()
.client(client)
.baseUrl(API_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
Another way to use basic authentication with Retrofit2 would be to pass the authentication string as an argument to your interface method.
So you would change the method signature to:
#GET
public Call<User> getJSON(#Url String string, #Header("Authorization") String myAuthString);
And then call it like this:
Call<User> call = service.getJSON("https://api.url.com/", "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==");
Where you substitute QWxhZGRpbjpvcGVuIHNlc2FtZQ== for your Base64-encoded username:password string.
If you need to pass the username and password for every API call and want to keep the method signatures clean, it might be better to use the custom OkHttpInterceptor method instead.
i am trying to learn Retrofit 2.0 and tried example at http://tutorialwing.com/android-retrofit-library/
but as i go through tutorial i found that it is saying,
base url (https://api.stackexchange.com) + end url("/2.2/questions?order=desc&sort=creation&site=stackoverflow"") = final url ("https://api.stackexchange.com/2.2/search?order=desc&sort=activity&tagged=android&site=stackoverflow".
)
but how search is appended automatically although it is not present in end url.below is my code for making request to API.
public class ApiClient {
public static final String API_BASE_URL = "https://api.stackoverflow.com";
private static OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
private static Retrofit.Builder builder =
new Retrofit.Builder()
.baseUrl(API_BASE_URL)
.addConverterFactory(GsonConverterFactory.create());
public static <S> S createService(Class<S> serviceClass) {
HttpLoggingInterceptor interceptor=new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BASIC);
httpClient.addInterceptor(interceptor);
Retrofit retrofit = builder.client(httpClient.build()).build();
return retrofit.create(serviceClass);
}
}
APIinterface.java
#GET("/2.2/search/{order}/{sort}/{tagged}&site=stackoverflow")
Call<ArrayList<Questions>>loadQuestions(#Query("order")String order, #Query("sort")String sort, #Query("tagged")String tag);
but i am not getting any result. after placing interceptor i was able to see url of request is : https://api.stackoverflow.com/2.2/search/%7Border%7D/%7Bsort%7D/%7Btagged%7D&site=stackoverflow?order=desc&sort=activity&tagged=android and after hitting this url it redirect me to this url : https://api.stackexchange.com/docs/api-v1-shutdown?order=desc&sort=activity&tagged=android i am not able to find where i was made mistake. any help is appreciated.
order, sort and tagged are part of the Path of the url, so you need to use the #Path annotation for that. The only query is site=stackoverflow.
#GET("/2.2/search/{order}/{sort}/{tagged}&site=stackoverflow")
Call<ArrayList<Questions>>loadQuestions(#Path("order")String order, #Path("sort")String sort, #Path("tagged")String tag, #Query("site") siteName);
should yeld the url you want.
Edit: if you want to query https://api.stackexchange.com/2.2/search?order=desc&sort=activity&tagged=android&site=stackoverflow then your definition should be
#GET("/2.2/search")
Call<ArrayList<Questions>>loadQuestions(#Query("order")String order, #Query("sort")String sort, #Query("tagged")String tag, #Query("site") siteName);
or alternatively you can use #QueryMap to provide a single argument
I am just starting out with retrofit for android. I am getting an error when I try to specify 2 fields for the body of a post request:
Multiple #Body method annotations found. (parameter #2) for method
The Call is defined in my API interface file as:
#POST("auth/login")
Call<UserData> login(#Body String username, #Body String password);
And I create the call with:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(baseURL)
.addConverterFactory(GsonConverterFactory.create())
.build();
APIService service = retrofit.create(APIService.class);
Call<UserData> call = service.login(username, password);
error is generated when the call is created (do not have a chance to execute it).
When I remove one of the body fields it seems to work fine.
Any Ideas?
Using multiple #Body is bad idea, because #Body here means message Body of HTML POST.
(Detail: How to send data in the HTTP request body when using an HTML form?)
I suggest you to define a class containing both username and password, like below.
public class LoginInformation {
String username;
String password;
}
And, fill your information on that class instance, and use that.
#POST("auth/login")
Call<UserData> login(#Body LoginInformation loginInformation);
Any HTTP request is allowed to contain only one message body , if you try adding two #Body parameters you will get this message "Multiple #Body method annotations found."
And you can fix it by following this:
You can send multiple or different type of objects at the same time using HashMap or single type, for example:
HashMap<String, Object> map = new HashMap<>();
map.put("password", "123456");
map.put("username", "Jake Warthon");
or
public class User(){
private String username;
private String password;
public void setUsername(String username){
this.username = username;
}
public void setPassword(String password){
this.password = password;
}
}
User user = new User();
user.setUsername("Jake Warthon")
user.setPassword("123456");
map.put("user", user);
You can append more data (like different type of objects) in a single body if you want (optional)
map.put("user", user);
map.put("authorization", "12uh3u12huhcd2");
map.put("something", someobject);
You have to change the body type of the request to receive a Hashmap or User
#POST("auth/login")
Call<UserData> login(#Body HashMap map);
or
#POST("auth/login")
Call<UserData> login(#Body User user);
Finally you pass the data to service like you already did.
Call<UserData> call = service.login(map);
or
Call<UserData> call = service.login(user);
And remember, the server side have to implement it correctly to receive the data as a map.
I wanna send a list of integer with userName and password to WebService some thing like bellow request
UpdateDocumentState(List<int> documentIds, string userName, string password)
But I don't know How to do that ? Use #Post Or #Put ? use #Query Or #Field ? I googled but didn't find any good example or tutorial which explained these well. ( All tutorial I found was about #GET )
could anyone give me some piece of code , how to do that ?
About the use of #PUT or #POST I think you had to get this information from the WebService developers.
Anyway, here sample code for both of Retrofit annotations with or without Callback response.
#POST("your_endpoint")
void postObject(#Body Object object, Callback<Response> callback);
#PUT("/{path}")
String foo(#Path("path") String thePath);
EDIT:
Object is a custom class which represent the data you had to send to the WebService.
public class DataToSend {
public List<Int> myList;
public String username;
public String password;
}
For example when the #POST annotation declaration will be:
#POST
void postList(#Body DataToSend dataToSend, Callback<Response> callback);
and then you call the method using Retrofit service
yourService.postList(myDataToSend, postCallback);