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...
Related
I am trying to update databse record using Retrofit library. The postman works fine with same data. GET data is also working fine but PATCH operation is returning Error 500 Internal server error.
Interface:
//#Headers("Content-Type: application/json")
#PATCH("usersapi/{id}")
Call<UserBank> updateUserBank(#Path("id") int id, #Body UserBank post);
Main activity:
retrofit = new Retrofit.Builder()
.baseUrl("https://<website>/api/")
// .addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
jsonCofyBizApi = retrofit.create(jsonCofyBizApi.class);
UserBank post = new UserBank(user_id, txtbankname.getText().toString());
Call<UserBank> call = jsonCofyBizApi.updateUserBank(user_id, post);
call.enqueue(new Callback<UserBank>() {
#Override
public void onResponse(Call<UserBank> call, Response<UserBank> response) {
UserBank postResponse = response.body();
and rest of the code...
Class/ Constructor UserBank:
public class UserBank {
#SerializedName("user_id")
int user_id;
#SerializedName("bank_name")
String bank_name;
public UserBank(int user_id, String bank_name)
{
this.user_id = user_id;
this.bank_name = bank_name;
}
rest of the code...
I saw other threads with the same topic but none of the solution is working for me. The url is getting properly generated.
Any help appreciated.
Regards,
PD
The problem is solved. Added '/' at the end of the specified endpoint
#PATCH("usersapi/{id}")
Call<UserBank> updateUserBank(#Path("id") int id, #Body UserBank post);
#PATCH("usersapi/{id}/")
Call<UserBank> updateUserBank(#Path("id") int id, #Body UserBank post);
I try to call this Request with Retrofit
my code :
Map<String, String> parameters = new HashMap<>();
Clientn client = new Clientn();
final WaselJsonPlaceHolderApi apiService = client.getClient().create(WaselJsonPlaceHolderApi.class);
Call<TokenModel> call = apiService.getLoginToken( "password", "ec_user","EC_P#ssw0rd" , "0500344253", "1993");
call.enqueue(new Callback<TokenModel>() {
#Override
public void onResponse(Call<TokenModel> call, Response<TokenModel> response) {
Log.e("TAG-TAG", ""+response.errorBody());
Log.e("TAG-TAG", ""+response.body());
}
#Override
public void onFailure(Call<TokenModel> call, Throwable t) {
}
});
the Interface :
#FormUrlEncoded
#POST("api/CustomerAccount/LoginUserByMobile")
Call<TokenModel> getLoginToken( #Field("grant_type") String title,
#Field("app_username") String body,
#Field("app_password") String password,
#Field("mobile_number") String userId,
#Field("ver_code") String code );
the Client
public class Clientn {
public static final String BASE_URL = "http://192.168.1.230/MagicWord.ECommercPlatform.API/";
public static Retrofit retrofit = null;
public static Retrofit getClient(){
if (retrofit == null){
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}
but i get the null response and the ErrorBody is E/TAG-TAG: okhttp3.ResponseBody$1#aa2472e
I think android stopped response for normal http in latest API(29), You can try with https and check the same issue is coming or not.
i think the issue is in your "ver_code which is int or use are taking string.is it string or int?
onFailure callback could be very useful, try to add t.printStacktrace() on it.
Also, don't pass an object as is with a string on Log, because it will just print an address that you don't need.
Keep field name and variable name same
Example:
#Field("grant_type") String grant_type, #Field("app_username") String app_username,#Field("app_password") app_password
so that you cannot get confused.
I think the request method should be POST
Because in code the request method is POST but in screenshot the request method is GET
I need to build in redundancy into my app where if a server is down it will try a backup redundancy server upon failure of the first request.
Aside from doing
Call<LoginResult> loginCall = apiInterface.login(....);
loginCall.enqueue(new Callback<LoginResult>() {
#Override
public void onResponse(Call<LoginResult> call, Response<LoginResult> response) {
if(response.isSuccessful){
//do normal stuff
}else{
//try second url
}
}
#Override
public void onFailure(Call<LoginResult> call, Throwable t) {
//Try second url
}
}
I don't see a clean way to do this. Creating another retrofit request inside the error block or non-successful block would add a lot of code complexity.
Is there an easier way to handle this in Retrofit or OkHttp?
I have here an option with OkHttp interceptors. The idea is that if the request fails you replace the url and execute the request again.
The following is an api client to the OpenWeather Api. If you want to try out the example you'll need to sign up and get an api key. It should be free so I hope this is ok.
I'll post here the full code and then walk you through it.
private final static String API_KEY = "<API KEY HERE>";
private static class Weather {
#SerializedName("id")
#Expose
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
private static final String GOOD_HOST = "api.openweathermap.org";
private static final String BAD_ENDPOINT = "https://api.aaaaaaaaaaa.org";
interface WeatherApiClient {
#GET("/data/2.5/weather")
Call<Weather> get(
#Query("q") String query,
#Query("appid") String apiKey);
}
private static class ReplicaServerInterceptor implements Interceptor {
#Override public okhttp3.Response intercept(Chain chain)
throws IOException {
try {
okhttp3.Response response = chain.proceed(chain.request());
return response;
} catch (IOException e) {
// Let's build a new request based on the old one
Request failedRequest = chain.request();
HttpUrl replicaUrl = failedRequest.url()
.newBuilder()
.host(GOOD_HOST)
.build();
okhttp3.Request request = failedRequest.newBuilder()
.url(replicaUrl)
.build();
return chain.proceed(request);
}
}
}
public static void main(String[] args) {
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.addInterceptor(new ReplicaServerInterceptor())
.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BAD_ENDPOINT)
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build();
WeatherApiClient weatherApiClient =
retrofit.create(WeatherApiClient.class);
weatherApiClient.get("Lisbon,pt", API_KEY)
.enqueue(new Callback<Weather>() {
#Override public void onResponse(
Call<Weather> call,
Response<Weather> response) {
// This might be null sometimes because
// the api is not super reliable, but I didn't
// add code for this
System.out.println(response.body().id);
}
#Override public void onFailure(
Call<Weather> call,
Throwable t) {
t.printStackTrace();
}
});
}
To be able to fake a server failure I prepare retrofit to call a non existent url - BAD_ENDPOINT. This will trigger the catch clause inside the interceptor.
The interceptor itself is obviously the key thing here. It intercepts every call from retrofit and executes the call. If the call throws an error because the server is down, then it will raise an IOException. Here I copy the request being made and change the url.
Changing the url means changing the host:
HttpUrl replicaUrl = failedRequest.url()
.newBuilder()
.host(GOOD_HOST)
.build();
If you just call url(<some url>) in the request builder, everything gets replaced. Query parameters, protocol, etc. This way, we preserve these from the original request.
(OkHttp offers newBuilder methods which copy the data from the current object and let you just edit what you want. Just like kotlin's copy. This is why we can simply change the url and be safe that everything else remains the same)
I then build the new request with the url and execute it:
okhttp3.Request request = failedRequest.newBuilder()
.url(replicaUrl)
.build();
return chain.proceed(request);
Interceptors work on a chain pattern, that's why calling proceed will call the next interceptor on the chain. In this case we just need to actually make the request.
I didn't bother copying the entire weather resource, so I'm just using the id. I think that's not the main focus of the question
As I said before, this is meant as a proof of concept. As you noticed I'm try-catching the execution of the call, but in your case it might be that the call actually succeeds executing, but the http response is not a 2XX. The okhttp response objects have methods that help you checking if the response was successful namely - isSuccessful(). The idea is the same - Build a new request and carry on if it's not successful.
I didn't bother treating any errors from the replica in this example. They'll just be forwarded to the retrofit client.
As you can see retrofit has no clue where the response is coming from. This might or not be good. Also, the response body needs to be the same from both servers, which I guess it's the case.
Lastly I'm sorry for the awkward okhttp3.Response name spacing there. I was using both Response from retrofit and okhttp and hence had to avoid the name clash.
Versions used for this example: Retrofit 2.3.0 and the okhttp bundled with that
I am using Retrofit2 for the first time and have a problem to get a simple Array in non JSON format.
Error: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 3 path $[0]
This means its not an JSON Object since it does not start with "{"
I tried adding the ScalarsConverter but it doesent seems to work.
Api: https://chasing-coins.com/api/v1/coins
Interface:
public interface Retro_coins {
#GET("api/v1/coins")
Call<List<Coinlist>> getCoinlist();
}
Class:
public class Coinlist {
private List coinlist;
public List getCoinlist() {
return coinlist;
}
}
Retrofit initialization and call:
String API_BASE_URL = "https://chasing-coins.com/";
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
Retrofit.Builder builder = new Retrofit.Builder()
.baseUrl(API_BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addConverterFactory(ScalarsConverterFactory.create())
;
Retrofit retrofit = builder.client(httpClient.build()).build();
Retro_coins client = retrofit.create(Retro_coins.class);
// Fetch list
Call<List<Coinlist>> call =
client.getCoinlist();
// Execute the call asynchronously. Get a positive or negative callback.
call.enqueue(new Callback<List<Coinlist>>() {
#Override
public void onResponse(Call<List<Coinlist>> call, Response<List<Coinlist>> response) {
// The network call was a success and we got a response
Log.w("Yes", response.toString());
}
#Override
public void onFailure(Call<List<Coinlist>> call, Throwable t) {
Log.w("no", t.toString());
}
});
Thanks!
When you are using private List coinlist;, Gson expects the object to be
{
"coinlist":"[]"
}
where as what you are providing is just
["String","String","String"]
furthermore when you use Call<List<Coinlist>> you are expecting the data to be
[
{
"coinlist":"[]"
}
]
Just change your call from Call<List<Coinlist>> to Call<List<String>>. That should fix your problem. Let me know if you need more clarification
Your request Returning String. So you need to Change the Response to String or Need to change your request Call to String.
All.
I am trying to use Retrofit to send raw json as a body in a POST Request to the server.
#POST("api/apps")
Call<List<GetApps>> getApp(#Body GetApps body);
I have built the model using jsontopojo for the responses that are coming back.
Inside my call, I have put the constructors and the getters and setters and also toString().
This is my retrofit call.
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(APIUrl.BASEURL)
.addConverterFactory(GsonConverterFactory.create())
.build();
APIService apiService = retrofit.create(APIService.class);
Call<List<GetApps>> call = apiService.getApp();
call.enqueue(new Callback<List<GetApps>>() {
#Override
public void onResponse(Call<List<GetApps>> call, Response<List<GetApps>> response) {
List<GetApps> GetApps2 = response.body();
Toast.makeText(getApplicationContext(),"success", Toast.LENGTH_SHORT).show();
}
#Override
public void onFailure(Call<List<GetApps>> call, Throwable t) {
Toast.makeText(getApplicationContext(),"Failure", Toast.LENGTH_SHORT).show();
}
});
I am getting an error on this line :
Call<List<GetApps>> call = apiService.getApp();
It says getApp(getApps) cannot be applied to (); Not sure what should go into the ();
You must pass GetApps object as parameter:
GetApps getApps = new GetApps();
//set all your data on getApps
//getApps.setYourData(yourData);
Call<List<GetApps>> call = apiService.getApp(getApps);
You make request
#POST("api/apps")
Call<List<GetApps>> getApp(#Body GetApps body);
which will accept body that will append to URL.
just add body in getApp(#Body GetApps body); // accept JSON string as a parameter.
Happy coding!!
At the top of Post just add #FormEncodedUrl and pass a GetApp model as body ...
Call<List<GetApps>> call = apiservice.getApp(getAppOne);