retrofit 2 : pass dynamic header along with body - android

I want to pass Header and Body dynamically to a Web Api. So, I have implemented as below:
public interface NotificationService {
#POST("user/update/notification")
Call<JsonObject> notification(#Header("Authorization") String authorization, #Body NotificationRequest notificationRequest);
}
And using this as,
showProgressDialog();
NotificationRequest notificationRequest = new NotificationRequest(checked ? ApiConstants.IS_ON : ApiConstants.IS_OFF, getUserId());
NotificationService notificationService = ApiFactory.provideNotificationService();
Call<JsonObject> call = notificationService.notification(getAuthorizationHeader(), notificationRequest);
call.enqueue(new Callback<JsonObject>() {
#Override
public void onResponse(Call<JsonObject> call, Response<JsonObject> response) {
logDebug(SettingsFragment.class, response.body().toString());
hideProgressDialog();
}
#Override
public void onFailure(Call<JsonObject> call, Throwable t) {
hideProgressDialog();
}
});
But this way, I am not getting null response (response.body() is null).
Can anyone suggest how to pass Dynamic Header and Body together ?
Note: I went through this tutorial but didn't found the way to pass both.

As far as I can see, there is no way to pass both Header and Body at the same time.
But You can add Interceptor into OkHttpClient as below:
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.cache(cache);
builder.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request.Builder ongoing = chain.request().newBuilder();
ongoing.addHeader("Authorization", getToken(app));
return chain.proceed(ongoing.build());
}
});
This will add authorization header in every request. You can control adding header to some condition like if User is logged in, only then request header should be added.
Just wrap below line in if condition to something like:
if(isUserLoggedIn())
ongoing.addHeader("Authorization", getToken(app));

You are using Retrofit2. It is completely possible using dynamic headers for example:
#POST("hello-world")
fun getKaboom(
#Body body: TheBody,
#Header("hello-world-header") helloWorldHeader: String? = "kaboom"
): Single<KaboomResponse>

Related

how to handle empty response body in android fast networking library?

how to handle an empty response body in android fast networking library?
I know in retrofit we have void, but how to handle in fast networking.
POST request, request content type JSON, empty response body with Http code.
retrofit equivalent:
#POST("/path/to/get")
Call<Void> getMyData(/* your args here */);
my code is below
#Override
public Single<Response> checkPhoneNumberAvailabilityApiCall(CheckPhoneNumberAvailabilityRequest checkPhoneNumberAvailabilityRequest) {
return Rx2AndroidNetworking.post(ApiEndPoint.getEndPoint(ApiEndPoint.ENDPOINTS.CHECK_PHONE_AVAILABLE_API))
.addHeaders(getHeaders(GENERAL))
.addJSONObjectBody(convertToJSON(checkPhoneNumberAvailabilityRequest))
.build()
.getObjectSingle(Response.class);
}
I have no experience in Retrofit yet, but hope it helps
AndroidNetworking.post(YOUR_ENDPOINT)
.addHeaders(YOUR_HEADER)
.addJSONObjectBody(YOUR_JSONOBJECTBODY)
.build()
.getAsJSONObject(new JSONObjectRequestListener() {
#Override
public void onResponse(JSONObject response) {
if (response.toString().isEmpty()) {
//handle empty body
} else {
//do logic with json value
}
}
#Override
public void onError(ANError anError) {
//print log error code
Log.e("FAN_ERROR", "ERROR CODE : "+ anError.getErrorCode());
//print log error body
Log.e("FAN_ERROR", "ERROR BODY : "+ anError.getErrorBody());
}
});

Can't figure out what to check for in OkHttp's onResponse method

I know there are lots of tutorials for OkHttp, but basically all of them do something different in the onResponse method and most don't bother to explain why. Some check for if (response.isSuccessful), some surround it with try/catch, some don't do any of this at all.
This is my example project. What is the proper way to handle the onResponse method?
public class MainActivity extends AppCompatActivity {
private TextView textViewResult;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textViewResult = findViewById(R.id.text_view_result);
OkHttpClient client = new OkHttpClient();
String url = "https://reqres.in/api/users?page=2";
Request request = new Request.Builder()
.url(url)
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
#Override
public void onResponse(Call call, Response response) throws IOException {
final String myResponse = response.body().string();
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
textViewResult.setText(myResponse);
}
});
}
});
}
}
Update
onResponse of okhttp runs on background thread. So, yes, it's necessary to do MainActivity.this.runOnUiThread(...).
Original answer
onResponse callback already runs on ui thread AFAIK. So, you don't actually need to do MainActivity.this.runOnUiThread(...).
And everyone's onResponse is different because everyone has different needs. Use try/catch if your operations in onResponse might give error and you don't want it to crash.
For some network requests you may need to check if response is successful for other you may not. It all depends on use cases. Do what works for you best.
I'd suggest you surround your code in onResponse in a try/catch block because the user might close the app before the network request is finished. And when you set the textview text in onResponse it will crash because the activity and that textview doesn't exist anymore.
Adding to the answer from rafid. There are basically three cases you want to check.
response.isSuccessful() => status code between 200 and 300
response.code() => to manually check after response is not successful
onFailure() => Network error or parsing error etc.
Ideally your callback would handle those cases something like
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
// network error or parsing error
}
#Override
public void onResponse(Call call, Response response) {
if (response.isSuccessful()) {
// do stuff all good
} else {
// handle different cases for different status codes or dump them all here
}
}
});
The reason you need a try-catch is because OkHttp is trying to parse the response. This is the case for example for response.errorBody().string();. Another case would be if your Callback<T> has actually a type parameter. Again OkHttp will try to parse the response to that type. If it fails it will result in a callback onto the onFailure method.
I think you need to make sure you know the legal response from the request, like an json or File. if it's just a json, use like below:
#Override
public void onResponse(Call call, Response response) throws IOException {
final String myResponse = response.body().string();
if (response.isSuccessful() && !TextUtils.isEmpty(myResponse)) {
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
textViewResult.setText(myResponse);
}
});
}
}
Edit: To be more clear.
Callback is running in mainThread so there is no need to call runOnUiThread.
If response is not successful you can try to parse error body as below. If response is successful you can parse with Gson as i show.
String message = "";
if (response.errorBody() != null) {
try {
message = response.errorBody().string();
} catch (IOException ignored) {
Log.e("OkHttp IOException", "error while parsing response");
}
Log.d("Error Message", message);
}
I recommend you to use Gson Library. First you should create your pojo class. You can use http://www.jsonschema2pojo.org/ to create your pojo class. Then you can parse body like below
Gson gson = new Gson();
MyPojo myPojo = gson.fromJson(response.body().charStream(), MyPojo.class);

Retrofit 2 is not making calls

I've setup retrofit 2.1 and it is not making calls to my api at all. I just setup a lamp stack and made my ip publicly accessible. I'm trying to send information via a POST to my php script which would add data to my db. For some reason, retrofit will not make the call to my api... I'm not sure what I'm doing wrong.
#POST("/sendInformation.php")
Call<JSONObject> sendUserInfo(#Body JSONObject userViewModel);
OkHttpClient.Builder client = new OkHttpClient.Builder();
HttpLoggingInterceptor logging = new HttpLoggingInterceptor(message -> Log.d(TAG, message));
logging.setLevel(HttpLoggingInterceptor.Level.BASIC);
client.addInterceptor(logging);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client.build())
.addConverterFactory(JacksonConverterFactory.create())
.build();
UserInformationService userService = retrofit.create(UserInformationService.class);
Call<JSONObject> call = userService.sendUserInfo(jsonObject);
call.enqueue(new Callback<JSONObject>() {
#Override
public void onResponse(Call<JSONObject> call, Response<JSONObject> response) {
Toast.makeText(HomeActivity.this, response.body().toString(), Toast.LENGTH_SHORT).show();
}
#Override
public void onFailure(Call<JSONObject> call, Throwable t) {
}
});
I tried to add logging but it won't make the call so I can't even see the logging. Anyone have any ideas?
EDIT: The BASE_URL I'm using is my public IP. I just forwarded my ports so it's accessible. I tried doing a POST on hurl.it and it works fine. It's only retrofit not working. I've also tried this with an asyncTask and httpURLConnection and it also works. I must be missing something really minor...
Call<JSONObject> call = userService.sendUserInfo(jsonObject);
This line is not enough. The Call<T> object represents an 'intent' of a call rather than the operation itself, and you need to execute it by calling one of two methods on the object: execute and enqueue.
Execute works in a blocking manner and will return your JSONObject through the response.getBody() method:
Response<JSONObject> response = call.execute();
Enqueue works asynchronously and will provide your JSONObject through the callback object - if call is successful, onResponse method will be called with your response as a call parameter.
call.enqueue(new Callback<JSONObject>() {
#Override
public void onResponse(Call<JSONObject> call, Response<JSONObject> response) {
}
#Override
public void onFailure(Call<JSONObject> call, Throwable t) {
}
});

Retrofit and Spotify API: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $

There are several questions on stackoverflow that are just like mine, but I haven't been able to solve my issue. The error is as stated in the title. I'm working with the Spotify API to get the current user's profile. I feel like I have a fundamental misunderstanding with what's going on.
Here's what I have in my main activity:
Gson gson1 = new GsonBuilder()
.setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
.setLenient()
.create();
Retrofit retrofit1 = new Retrofit.Builder()
.baseUrl(ENDPOINT_ACCOUNTS)
.addConverterFactory(GsonConverterFactory.create(gson1))
.build();
SpotifyAPI spotifyAPI = retrofit1.create(SpotifyAPI.class);
Call<User> call1 = spotifyAPI.getUserData("Authorization: Bearer " + sp.getString("accessCode", "DEFAULT"));
call1.enqueue(new Callback<User> () {
#Override
public void onResponse(Call<User> call, Response<User> response) {
...
}
#Override
public void onFailure(Call call, Throwable t) {
...
}
});
Here is my interface:
String ENDPOINT_V1 = "https://api.spotify.com/";
#GET("/v1/me")
Call<User> getUserData(#Header("Authorization") String authorization);
And here is my User class:
public class User {
String id;
public String toString() {
return(id);
}
}
OnFailure() is being triggered, spitting out the error message in the title. If I substitute all instances of User with ResponseBody, the call succeeds with a status code of 200. But the contents of response is not at all what I want. Please let me know if there is more information I can provide. Thank you.
Turns out my base URL was wrong. Works like a charm now. Thanks for looking this through.

Changing the type of the Response in a Call object

I'm consuming an API endpoint with Retrofit 2, and I would like to mutate the response object type in a Call object. I need to use a Call object so that I can cancel the request.
This behavior is easy with rxJava. For instance, let's say I have an endpoint that returns an Account object, but I'd like to return the an Email object instead. With rxJava I can do something like:
public Observable<Email> getEmail(Account account) {
return service.getUser().map(new Func1<Account, Email>() {
#Override public Email call(Account account) {
return new Email(account.getEmail());
}
});
}
where getUser() returns and Observable<Account>.
However, call.enqueue does not seem to allow this kind of chaining. Is there a way to accomplish the above with a Call<Email> instead of rxJava to allow request cancellation?
There are no ways to do it in retrofit. But you can use Response<JsonElement> and get as Json object your response and after that use any json deserializer to convert to your class.
call.enqueue(new Callback<JsonElement>() {
#Override
public void onResponse(Call<JsonElement> call, Response<JsonElement> response) {
if(response.isSuccessful()){
JsonElement jsonElement = response.body();
JsonObject objectWhichYouNeed = jsonElement.getAsJsonObject();
//use any json deserializer to convert to your class.
}
else{
System.out.println(response.message());
}
}
#Override
public void onFailure(Call<JsonElement> call, Throwable t) {
System.out.println("Failed");
}
});

Categories

Resources