I am trying to consume my own REST call using retrofit. My only clue on why it doesn't work is that it says that the type of information is "text/html" even though I am sure it's json. That being said I haven't found any answers that solve my problem.
Payload:
[
{
"user_id": "32",
"username": "Marko",
"last_activity": "2020-04-26 20:44:00",
"user_image": "slika2"
},
{
"user_id": "33",
"username": "dejan",
"last_activity": "2020-04-26 20:44:00",
"user_image": "slika3"
}
]
My chat class:
public class Chat {
#SerializedName("user_id")
private String userId;
private String username;
#SerializedName("last_activity")
private String lastActivity;
#SerializedName("user_image")
private String userImage;\
...constructor/getters
}
Api Interface:
#FormUrlEncoded
#POST("api_get_all_chats.php")
Call<List<Chat>> getChats(
#Field("id") String id
);
Api client:
public static Retrofit getClient() {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.level(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(interceptor).build();
retrofit = new Retrofit.Builder()
.baseUrl("http://MYIP/emob%20projekat/api/")
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
return retrofit;
}
And the whole request:
apiInterface = ApiClient.getClient().create(userApi.class);
Call<List<Chat>> call = apiInterface.getChats(u.getUserInfo().getUserId());
call.enqueue(new Callback<List<Chat>>() {
#Override
public void onResponse(Call<List<Chat>> call, Response<List<Chat>> response) {
chatList = new ArrayList<>(response.body());
chatAdapter = new ChatAdapter(getApplication().getApplicationContext(), R.layout.user_view, chatList);
listView.setAdapter(chatAdapter);
}
#Override
public void onFailure(Call<List<Chat>> call, Throwable t) {
Toast.makeText(getApplication().getApplicationContext(), "ne valja" , Toast.LENGTH_LONG).show();
}
});
Any type of a clue where I am messing up would be much appreciated.
You base url has space when I decoded it I got
http://MYIP/emob projekat/api/
i recommend you to put base URL without encoding
How about cleartextTrafficPermitted="true" on your AndroidManifest. I see you code is fine and I think maybe the problem about your URL with a http scheme.
You can try to specify what response your client expects with Accept:application/json either in you API Interface with:
#FormUrlEncoded
#Headers({"Accept:application/json"})
#POST("api_get_all_chats.php")
Call<List<Chat>> getChats(
#Field("id") String id
);
Either in your API client with new Retrofit.Builder().addHeader("Accept","application/json");.
This issue in square/retrofit is very similar to yours (only the inverse, since in the issue it is the server that does not accepts text/html while in your case it seems to be your client. Maybe, as in the issue, you are employing gson as well which cannot parse the text/html flavored response)
You need an interceptor like this.
public class CustomInterceptor extends Interceptor {
#override
public Response intercept(Interceptor.Chain chain) {
return chain.proceed(chain.request().newBuilder().addHeader("Content-Type", "application/json").addHeader("Accept", "application/json").build())
}
}
First try to log the request, to see exactly what is sent, with headers, url, body and response : https://github.com/square/okhttp/tree/master/okhttp-logging-interceptor
Secondly, are you sure that you have to make a POST request, a GET seems better ?
Related
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
My Url is
http://sales.xxxxx.com/api/v2/customers/5101117835
When I use this code the response code is always 500.
Interface Code
#Headers({ "Content-Type: application/json;charset=UTF-8"})
#PATCH("customers/{custId}")
Call<GeoTag> postGeoTagData (#Body geoTagUserData geoTag, #Path("custId") String id, #Header("Authorization") String auth);
In Activity
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(" http://sales.erprnd.com/api/v2/")
.addConverterFactory(GsonConverterFactory.create())
.build();
ApiInterface apiInterface = retrofit.create(ApiInterface.class);
Call<GeoTag> geoTagCall = apiInterface.postGeoTagData(geoTagUserData, CustCode, "Bearer "+body.getToken() );
geoTagCall.enqueue(new Callback<GeoTag>() {
#Override
public void onResponse(Call<GeoTag> call, Response<GeoTag> response) {
Toast.makeText(GeoTaggingActivity.this, ""+response.code(), Toast.LENGTH_SHORT).show();
}
#Override
public void onFailure(Call<GeoTag> call, Throwable t) {
Toast.makeText(GeoTaggingActivity.this, "Failed", Toast.LENGTH_SHORT).show();
}
});
When I use Postman the response is 201 AND Response IS {} which is ok. My jsonObject is
{
"latitude":11.22,
"longitude":22.11,
"alt_address":"test address",
"height":"12 ft",
"width":"24 ft",
"photo": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMAAAAD"
}
I Have created another class where i get all information from user which is
geoTagUserData i get all the info perfectly
Please Help me find the error .
Finally I got The Solution which is, When I Send Data to Send Data to server from My App it just get the value against every field form geoTagUserData Class it doesn't
#post Converted Data in JSON Format. When My Server Find That I just Values Not In JSON Format
Then It Just Rejected And Return 500 error
So, My solution is just Using Gson Converter get The value form
user and Converted it to JSON object. and Send That to Server Which will Return 201
I need to get the XML file from the site. I'm learning to use Retrofit.
I need to make a request and attach my API key via the "X-AppId" header. It should look like this:
X-AppId: my key.
If I do this from the browser, I get the answer.
Through the retrofit I get the access
error 403 Forbidden code = 403, message = Forbidden, url = https: //
Tell me how it is implemented properly to receive an answer from the server code = 200
Here is my implementation:
public interface myAPIinterface {
#GET("/api/ru/index/route/?from=Minsk&to=Warsaw")
Call<Routes> getProducts();
}
This is the activity where I output to the log:
private void getProducts(){
final ProgressDialog loading = ProgressDialog.show(this,"Fetching Data","Please wait...",false,false);
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
Log.d(TAG, "getProducts");
httpClient.addInterceptor(new Interceptor() {
#Override
public okhttp3.Response intercept(Chain chain) throws IOException {
Request request = chain.request()
.newBuilder()
.addHeader("X-AppId:", "97377f7b702d7198e47a2bf12eec74")
.build();
return chain.proceed(request);
}
});
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://rasp.rw.by")
.addConverterFactory(SimpleXmlConverterFactory.create())
.build();
myAPIinterface api = retrofit.create(myAPIinterface.class);
Call<Routes> call = api.getProducts();
call.enqueue(new Callback<Routes>() {
#Override
public void onResponse(#NonNull Call<Routes> call, #NonNull Response<Routes> response) {
Log.d(TAG, "onResponse");
Log.d(TAG, String.valueOf(kk));
Log.d(TAG, String.valueOf(response));
loading.dismiss();}
#Override
public void onFailure(Call<Routes> call, Throwable throwable) {
loading.dismiss();
Log.d(TAG, "onFailure" + throwable);
}
});
this is a log:
Response{protocol=http/1.1, code=403, message=Forbidden,
url=https://rasp.rw.by/api/ru/index/route/?from=Minsk&to=Warsaw}
if I take third-party sites where there are no headers, I get a response of 200 without problems. What am I doing wrong in this case? Thank you.
Oh, man, what are you doing. You can use annotations like #Query, #Header, etc.
public interface myAPIinterface {
#GET("/api/ru/index/route")
Call<Routes> getProducts(#Header("X-AppId:") String YOUR_APP_ID,
#Query("from") String from,
#Query("to") String to)
}
Then you can create request like this:
Retrofit retrofit = new Retrofit.Builder().
.baseUrl("https://rasp.rw.by")
.addConverterFactory(SimpleXmlConverterFactory.create())
.build();
retrofit.create(myAPIinterface.class).getProducts(myId, "Minsk", "Warsaw").enqueue ...
How It can help? You forgot to add header at second retrofit and then you have 403 error. So, You must add annotations, and this will be the last mistake when you forgot to put value to header/query/etc.
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.