Sending string and images with retrofit multipart/form-data - android

I was trying to send string and images with a retrofit.
while I could get pass response with x-www-form-urlencoded & hashmap, but I need to send it with the image. so I use form-data, but I couldn't get the same response with the same name and value, tested it on postman and it goes passed the same as my x-www-form.
so here is the postman
Postman request that got pass response
Method that doesn't goes through
with form-data
#Multipart
#POST("report")
fun push(
#HeaderMap headers: Map<String, String>,
#Part("store") string: RequestBody
): Call<ReportingResponse>
RequestBody.create(MediaType.parse("multipart/form-data"), "testing") //#1 fail
RequestBody.create(MediaType.parse("text/plain"), "testing") //#2 fail
I tried both but couldn't get the same response as the postman, and what it looks is just like this Retrofit request interceptor on Android Studio
Method that goes Through with x-www-form
#FormUrlEncoded
#POST("report")
fun push(
#HeaderMap headers: Map<String, String>,
#FieldMap form: MutableMap<String, Any>
): Call<ReportingResponse>
What am I suppose to do?

Step-1 : Create on interface method for call retrofit api
#POST(Const.Task_Ans_FILE_NAME)
Call<TaskInfoBean> verifyTaskAns(#Body RequestBody file);
Step-2: Use below code to send multipart image data along with other field in body.
RetroFitService retroFitService = RetrofitClient.getAppData();
MultipartBody.Builder builder = new MultipartBody.Builder().setType(MultipartBody.FORM);
if (answer_type.equals("1")) {
builder.addFormDataPart(Const.ANSWER, answer);
} else {
try {
builder.addFormDataPart(Const.ANSWER, Const.SelectedFileName, RequestBody.create(MultipartBody.FORM, Const.BOS.toByteArray()));
}catch (Exception e){
Log.e(TAG, "doInBackground: "+e.getMessage() );
}
}
builder.addFormDataPart(Const.LOGIN_ID, login_id)
.addFormDataPart(Const.USER_ID, user_id)
.addFormDataPart(Const.PLAY_ID, play_id)
.addFormDataPart(Const.TASK_ID, task_id)
.addFormDataPart(Const.SCENARIO, scenario)
.addFormDataPart(Const.ANSWER_TYPE,answer_type)
.addFormDataPart(Const.SKIP, skip);
final RequestBody requestBody = builder.build();
Call<TaskInfoBean> call = retroFitService.verifyTaskAns(requestBody);
call.enqueue(new Callback<TaskInfoBean>() {
#Override
public void onResponse(Call<TaskInfoBean> call, Response<TaskInfoBean> response) {
if(response.code()==200) {
TaskInfoBean taskInfoBean = response.body();
listener.OnVerifyTaskAns(taskInfoBean);
}else{
Log.e(TAG, "onResponse: "+response.toString() );
}
}
#Override
public void onFailure(Call<TaskInfoBean> call, Throwable t) {
Log.e(TAG, "onFailure: " + t.toString());
}
});
return null;
}
Step-3: Call this method in your activity/fragment.

Related

How to send "form-data" parameter using Retrofit in Android

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>

Java - Passing variable to an interface

I am building an android app and i am using Retrofit to retrieve data from API. In this app i have to make 3 calls. The first one is working fine. The code for the first one is below. I have one class
public class APIClient {
private static Retrofit retrofit = null;
static Retrofit getClient(){
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(interceptor).build();
retrofit = new Retrofit.Builder()
.baseUrl("https://api_app.com")
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
return retrofit;
}
}
Also i have this interface
#Headers({
"AppId: 3a97b932a9d449c981b595",
"Content-Type: application/json",
"appVersion: 5.10.0",
"apiVersion: 3.0.0"
})
#POST("/users/login")
Call<MainUserLogin> logInUser(#Body LoginBody loginBody);
The code of the Actvity is this
call.enqueue(object : Callback<MainUserLogin> {
override fun onResponse(call: Call<MainUserLogin>, response: Response<MainUserLogin>) {
if (response.code().toString().equals("200")){
val resource = response.body()
bearerToken = resource.session.bearerToken
if (bearerToken.isNotEmpty() && bearerToken.isNotBlank()){
val sharedPreferences = getSharedPreferences("Settings", Context.MODE_PRIVATE)
val editor = sharedPreferences.edit()
editor.putString("bearerToken", bearerToken)
editor.commit()
BearerToken.bearerToken = bearerToken
val i = Intent(this#LoginActivity, UserAccountsActivity::class.java)
i.putExtra("bearerToken", bearerToken)
startActivity(i)
}else{
Toast.makeText(applicationContext, "Please try again.", Toast.LENGTH_LONG).show()
}
}else{
println("edwedw "+response.errorBody().string())
Toast.makeText(applicationContext, "Incorrect email address or password. Please check and try again.", Toast.LENGTH_LONG).show()
}
}
override fun onFailure(call: Call<MainUserLogin>, t: Throwable) {
call.cancel()
}
})
This call is working fine.
With this call i am getting one token. The problem is that i have to pass this token as header to make the second call. So, the second call will be like this.
#Headers({
"AppId: 3a97b932a9d449c981b595",
"Content-Type: application/json",
"appVersion: 5.10.0",
"apiVersion: 3.0.0",
"Authorization: "+***Token***
})
#GET("/products")
Call<MainUserLogin> getUseraccounts ();
Is there any way to pass the variable from the Activity to the interface to make the Api request?
Thank you very much.
Using Retrofit you can call API's with multiple headers as follows
#GET("/products")
Call<MainUserLogin> getUseraccounts(#Header("AppId") String appId, #Header("Content-Type") String contentType, #Header("appVersion") String appVersion, #Header("apiVersion") String apiVersion, #Header("Authorization") String token);
Instead of
#Headers({
"AppId: 3a97b932a9d449c981b595",
"Content-Type: application/json",
"appVersion: 5.10.0",
"apiVersion: 3.0.0",
"Authorization: "+***Token***
})
#GET("/products")
Call<MainUserLogin> getUseraccounts ();
this. When you call getUseraccounts method you can parse the token that you created from the previous endpoint.
Try this and let me know your feedback. Thanks!
Once you receive the token, you should save this token in a global repository since the auth token is something that your app will need in order to make further authenticated api calls.
After that, define a AuthorizationHeaderInterceptor which will extend okhttp3.Interceptor. Override the intercept method of this interceptor to add auth token to your request.
#Override
public Response intercept(#NonNull Chain chain) {
return completeRequest(chain);
}
private Response completeRequest(#NonNull Interceptor.Chain chain) {
AuthToken authToken = authTokenRepository.get();
Request.Builder requestBuilder = chain.request().newBuilder();
if (authToken != null && chain.request().header(Authorization.NAME) == null) {
requestBuilder.addHeader(Authorization.NAME, Authorization.getValue(authToken.getIdToken()));
}
Request request = requestBuilder.build();
try {
return chain.proceed(request);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
The interceptor can be added when you build your okhttpClient.
okHttpClientBuilder.addInterceptor(new AuthorizationHeaderInterceptor(authTokenRepository))
Note that the Authorization class is simple convenience class which encapsulates the authorization header name and value format.
public class Authorization {
public static final String NAME = "Authorization";
#NonNull
public static String getValue(#NonNull String accessToken) {
return String.format("Bearer %s", accessToken);
}
}

Retrofit POST method with url + json return Method Not allowed

My POST API is like this: https://out-test.com/test/out/{ "key":"value" }
When I copy paste URL in browser works without any problem it accepts it.
But in Android, I get a response Method Not Allowed.
my API call:
#POST
Call<OUT> updateOUT( #Url String urlWithJson );
Retrofit declaration:
// Retrofit2 implementation
String BASE_API_URL = "https://out-test.com/";
Retrofit retrofit = new Retrofit.Builder()
.baseUrl( BASE_API_URL )
.addConverterFactory( GsonConverterFactory.create() )
.build();
final OutAPI api = retrofit.create( OutAPI.class );
Parsing object into String ( I got right JSON I validate it already ):
Gson gson = new Gson();
String jsonString = gson.toJson(out);
String url = "test/out/" + jsonString;
And then I call POST :
api.updateOut( url ).enqueue(new Callback<Out>() {
#Override
public void onResponse(Call<Out> call, Response<Out> response) {
Log.d(TAG, "onResponse:" + response );
}
#Override
public void onFailure(Call<Out> call, Throwable t) {
Log.d(TAG, "onFailure: " + t);
}
});
Does anyone have an idea what I am doing wrong here to get back this response? Did I miss somewhere something? Searched on the internet, but didn't find any working solution.
#EDIT:
When I debug and look at response and URL if I copy the value of URL and paste into Browser, it works fine.
Greetings
Try to replace
#POST
Call<OUT> updateOUT( #Url String urlWithJson );
this with following.
#POST("test/out/{key_value}")
Call<OUT> updateOUT(#Path("key_value") String keyAndValue);
Then, update your rest of the code as below
// Retrofit2 implementation
String BASE_API_URL = "https://out-test.com/";
Retrofit retrofit = new Retrofit.Builder()
.baseUrl( BASE_API_URL )
.addConverterFactory( GsonConverterFactory.create() )
.build();
final OutAPI api = retrofit.create( OutAPI.class );
Gson gson = new Gson();
String jsonString = gson.toJson(out);
api.updateOut(jsonString).enqueue(new Callback<Out>() {
#Override
public void onResponse(Call<Out> call, Response<Out> response) {
Log.d(TAG, "onResponse:" + response );
}
#Override
public void onFailure(Call<Out> call, Throwable t) {
Log.d(TAG, "onFailure: " + t);
}
});
Use Body provided by retrofit and Do like this
#POST("test/out/")
Call<OUT> updateOUT(#Body JsonObject urlWithJson );
then follow
Retrofit declaration:
// Retrofit2 implementation
String BASE_API_URL = "https://out-test.com/";
Retrofit retrofit = new Retrofit.Builder()
.baseUrl( BASE_API_URL )
.addConverterFactory( GsonConverterFactory.create() )
.build();
Then Pass your Json using GSON like:
JsonObject jsonObj= new JsonObject
jsonObj.addProperty("order",yourvalue) ;
And then call POST :
api.updateOut( jsonObj).enqueue(new Callback<Out>() {
#Override
public void onResponse(Call<Out> call, Response<Out> response) {
Log.d(TAG, "onResponse:" + response );
}
#Override
public void onFailure(Call<Out> call, Throwable t) {
Log.d(TAG, "onFailure: " + t);
}
});
Feel kinda stupid, but the problem was a guy who created API provided me the wrong documentation. A request is not POST but it's GET. However still had a problem with an error, but this time it was the wrong Content-Type which was text/plain instead of application/json. Thank you, everyone, for helping me out with this one. As #Yupi suggested Postman was a tool to debug this problem. Thank you.

How can I pass xml in payload via retrofit [duplicate]

This question may have been asked before but no it was not definitively answered. How exactly does one post raw whole JSON inside the body of a Retrofit request?
See similar question here. Or is this answer correct that it must be form url encoded and passed as a field? I really hope not, as the services I am connecting to are just expecting raw JSON in the body of the post. They are not set up to look for a particular field for the JSON data.
I just want to clarify this with the restperts once and for all. One person answered not to use Retrofit. The other was not certain of the syntax. Another thinks yes it can be done but only if its form url-encoded and placed in a field (that's not acceptable in my case). No, I can't re-code all the services for my Android client. And yes, it's very common in major projects to post raw JSON instead of passing over JSON content as field property values. Let's get it right and move on. Can someone point to the documentation or example that shows how this is done? Or provide a valid reason why it can/should not be done.
UPDATE: One thing I can say with 100% certainty. You CAN do this in Google's Volley. It's built right in. Can we do this in Retrofit?
The #Body annotation defines a single request body.
interface Foo {
#POST("/jayson")
FooResponse postJson(#Body FooRequest body);
}
Since Retrofit uses Gson by default, the FooRequest instances will be serialized as JSON as the sole body of the request.
public class FooRequest {
final String foo;
final String bar;
FooRequest(String foo, String bar) {
this.foo = foo;
this.bar = bar;
}
}
Calling with:
FooResponse = foo.postJson(new FooRequest("kit", "kat"));
Will yield the following body:
{"foo":"kit","bar":"kat"}
The Gson docs have much more on how object serialization works.
Now, if you really really want to send "raw" JSON as the body yourself (but please use Gson for this!) you still can using TypedInput:
interface Foo {
#POST("/jayson")
FooResponse postRawJson(#Body TypedInput body);
}
TypedInput is a defined as "Binary data with an associated mime type.". There's two ways to easily send raw data with the above declaration:
Use TypedByteArray to send raw bytes and the JSON mime type:
String json = "{\"foo\":\"kit\",\"bar\":\"kat\"}";
TypedInput in = new TypedByteArray("application/json", json.getBytes("UTF-8"));
FooResponse response = foo.postRawJson(in);
Subclass TypedString to create a TypedJsonString class:
public class TypedJsonString extends TypedString {
public TypedJsonString(String body) {
super(body);
}
#Override public String mimeType() {
return "application/json";
}
}
And then use an instance of that class similar to #1.
Yes I know it's late, but somebody would probably benefit from this.
Using Retrofit2:
I came across this problem last night migrating from Volley to Retrofit2 (and as OP states, this was built right into Volley with JsonObjectRequest), and although Jake's answer is the correct one for Retrofit1.9, Retrofit2 doesn't have TypedString.
My case required sending a Map<String,Object> that could contain some null values, converted to a JSONObject (that won't fly with #FieldMap, neither does special chars, some get converted), so following #bnorms hint, and as stated by Square:
An object can be specified for use as an HTTP request body with the #Body annotation.
The object will also be converted using a converter specified on the Retrofit instance. If no converter is added, only RequestBody can be used.
So this is an option using RequestBody and ResponseBody:
In your interface use #Body with RequestBody
public interface ServiceApi
{
#POST("prefix/user/{login}")
Call<ResponseBody> login(#Path("login") String postfix, #Body RequestBody params);
}
In your calling point create a RequestBody, stating it's MediaType, and using JSONObject to convert your Map to the proper format:
Map<String, Object> jsonParams = new ArrayMap<>();
//put something inside the map, could be null
jsonParams.put("code", some_code);
RequestBody body = RequestBody.create(okhttp3.MediaType.parse("application/json; charset=utf-8"),(new JSONObject(jsonParams)).toString());
//serviceCaller is the interface initialized with retrofit.create...
Call<ResponseBody> response = serviceCaller.login("loginpostfix", body);
response.enqueue(new Callback<ResponseBody>()
{
#Override
public void onResponse(Call<ResponseBody> call, retrofit2.Response<ResponseBody> rawResponse)
{
try
{
//get your response....
Log.d(TAG, "RetroFit2.0 :RetroGetLogin: " + rawResponse.body().string());
}
catch (Exception e)
{
e.printStackTrace();
}
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable throwable)
{
// other stuff...
}
});
An elegant Kotlin version of the above, to allow abstracting the parameters from the JSON convertion in the rest of your application code:
interface ServiceApi {
#POST("/api/login")
fun jsonLogin(#Body params: RequestBody): Deferred<LoginResult>
}
class ServiceApiUsingClass {
//ServiceApi init
fun login(username: String, password: String) =
serviceApi.jsonLogin(createJsonRequestBody(
"username" to username, "password" to password))
private fun createJsonRequestBody(vararg params: Pair<String, String>) =
RequestBody.create(
okhttp3.MediaType.parse("application/json; charset=utf-8"),
JSONObject(mapOf(*params)).toString())
}
Instead of classes we can also directly use the HashMap<String, Object> to send body parameters
for example
interface Foo {
#POST("/jayson")
FooResponse postJson(#Body HashMap<String, Object> body);
}
In Retrofit2, When you want to send your parameters in raw you must use Scalars.
first add this in your gradle:
compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'com.squareup.retrofit2:converter-gson:2.3.0'
compile 'com.squareup.retrofit2:converter-scalars:2.3.0'
Your Interface
public interface ApiInterface {
String URL_BASE = "http://10.157.102.22/rest/";
#Headers("Content-Type: application/json")
#POST("login")
Call<User> getUser(#Body String body);
}
Activity
public class SampleActivity extends AppCompatActivity implements Callback<User> {
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sample);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(ApiInterface.URL_BASE)
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
ApiInterface apiInterface = retrofit.create(ApiInterface.class);
// prepare call in Retrofit 2.0
try {
JSONObject paramObject = new JSONObject();
paramObject.put("email", "sample#gmail.com");
paramObject.put("pass", "4384984938943");
Call<User> userCall = apiInterface.getUser(paramObject.toString());
userCall.enqueue(this);
} catch (JSONException e) {
e.printStackTrace();
}
}
#Override
public void onResponse(Call<User> call, Response<User> response) {
}
#Override
public void onFailure(Call<User> call, Throwable t) {
}
}
Using JsonObject is the way it is:
Create your interface like this:
public interface laInterfaz{
#POST("/bleh/blah/org")
void registerPayer(#Body JsonObject bean, Callback<JsonObject> callback);
}
Make the JsonObject acording to the jsons structure.
JsonObject obj = new JsonObject();
JsonObject payerReg = new JsonObject();
payerReg.addProperty("crc","aas22");
payerReg.addProperty("payerDevManufacturer","Samsung");
obj.add("payerReg",payerReg);
/*json/*
{"payerReg":{"crc":"aas22","payerDevManufacturer":"Samsung"}}
/*json*/
Call the service:
service.registerPayer(obj, callBackRegistraPagador);
Callback<JsonObject> callBackRegistraPagador = new Callback<JsonObject>(){
public void success(JsonObject object, Response response){
System.out.println(object.toString());
}
public void failure(RetrofitError retrofitError){
System.out.println(retrofitError.toString());
}
};
And that its! In my personal opinion, its a lot better than making pojos and working with the class mess. This is a lot more cleaner.
Add ScalarsConverterFactory to retrofit:
in gradle:
implementation'com.squareup.retrofit2:converter-scalars:2.5.0'
your retrofit:
retrofit = new Retrofit.Builder()
.baseUrl(WEB_DOMAIN_MAIN)
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
change your call interface #Body parameter to String, don't forget to add #Headers("Content-Type: application/json"):
#Headers("Content-Type: application/json")
#POST("/api/getUsers")
Call<List<Users>> getUsers(#Body String rawJsonString);
now you can post raw json.
I particularly like Jake's suggestion of the TypedString subclass above. You could indeed create a variety of subclasses based on the sorts of POST data you plan to push up, each with its own custom set of consistent tweaks.
You also have the option of adding a header annotation to your JSON POST methods in your Retrofit API…
#Headers( "Content-Type: application/json" )
#POST("/json/foo/bar/")
Response fubar( #Body TypedString sJsonBody ) ;
…but using a subclass is more obviously self-documenting.
#POST("/json/foo/bar")
Response fubar( #Body TypedJsonString jsonBody ) ;
1)Add dependencies-
compile 'com.google.code.gson:gson:2.6.2'
compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'com.squareup.retrofit2:converter-gson:2.3.0'
2) make Api Handler class
public class ApiHandler {
public static final String BASE_URL = "URL";
private static Webservices apiService;
public static Webservices getApiService() {
if (apiService == null) {
Gson gson = new GsonBuilder()
.setLenient()
.create();
Retrofit retrofit = new Retrofit.Builder().addConverterFactory(GsonConverterFactory.create(gson)).baseUrl(BASE_URL).build();
apiService = retrofit.create(Webservices.class);
return apiService;
} else {
return apiService;
}
}
}
3)make bean classes from Json schema 2 pojo
Remember
-Target language : Java
-Source type : JSON
-Annotation style : Gson
-select Include getters and setters
-also you may select Allow additional properties
http://www.jsonschema2pojo.org/
4)make interface fro api calling
public interface Webservices {
#POST("ApiUrlpath")
Call<ResponseBean> ApiName(#Body JsonObject jsonBody);
}
if you have a form-data parameters then add below line
#Headers("Content-Type: application/x-www-form-urlencoded")
Other way for form-data parameter check this link
5)make JsonObject for passing in to body as parameter
private JsonObject ApiJsonMap() {
JsonObject gsonObject = new JsonObject();
try {
JSONObject jsonObj_ = new JSONObject();
jsonObj_.put("key", "value");
jsonObj_.put("key", "value");
jsonObj_.put("key", "value");
JsonParser jsonParser = new JsonParser();
gsonObject = (JsonObject) jsonParser.parse(jsonObj_.toString());
//print parameter
Log.e("MY gson.JSON: ", "AS PARAMETER " + gsonObject);
} catch (JSONException e) {
e.printStackTrace();
}
return gsonObject;
}
6) Call Api Like this
private void ApiCallMethod() {
try {
if (CommonUtils.isConnectingToInternet(MyActivity.this)) {
final ProgressDialog dialog;
dialog = new ProgressDialog(MyActivity.this);
dialog.setMessage("Loading...");
dialog.setCanceledOnTouchOutside(false);
dialog.show();
Call<ResponseBean> registerCall = ApiHandler.getApiService().ApiName(ApiJsonMap());
registerCall.enqueue(new retrofit2.Callback<ResponseBean>() {
#Override
public void onResponse(Call<ResponseBean> registerCall, retrofit2.Response<ResponseBean> response) {
try {
//print respone
Log.e(" Full json gson => ", new Gson().toJson(response));
JSONObject jsonObj = new JSONObject(new Gson().toJson(response).toString());
Log.e(" responce => ", jsonObj.getJSONObject("body").toString());
if (response.isSuccessful()) {
dialog.dismiss();
int success = response.body().getSuccess();
if (success == 1) {
} else if (success == 0) {
}
} else {
dialog.dismiss();
}
} catch (Exception e) {
e.printStackTrace();
try {
Log.e("Tag", "error=" + e.toString());
dialog.dismiss();
} catch (Resources.NotFoundException e1) {
e1.printStackTrace();
}
}
}
#Override
public void onFailure(Call<ResponseBean> call, Throwable t) {
try {
Log.e("Tag", "error" + t.toString());
dialog.dismiss();
} catch (Resources.NotFoundException e) {
e.printStackTrace();
}
}
});
} else {
Log.e("Tag", "error= Alert no internet");
}
} catch (Resources.NotFoundException e) {
e.printStackTrace();
}
}
I found that when you use a compound object as #Body params, it could not work well with the Retrofit's GSONConverter (under the assumption you are using that).
You have to use JsonObject and not JSONObject when working with that, it adds NameValueParams without being verbose about it - you can only see that if you add another dependency of logging interceptor, and other shenanigans.
So what I found the best approach to tackle this is using RequestBody.
You turn your object to RequestBody with a simple api call and launch it.
In my case I'm converting a map:
val map = HashMap<String, Any>()
map["orderType"] = orderType
map["optionType"] = optionType
map["baseAmount"] = baseAmount.toString()
map["openSpotRate"] = openSpotRate.toString()
map["premiumAmount"] = premiumAmount.toString()
map["premiumAmountAbc"] = premiumAmountAbc.toString()
map["conversionSpotRate"] = (premiumAmountAbc / premiumAmount).toString()
return RequestBody.create(MediaType.parse("application/json; charset=utf-8"), JSONObject(map).toString())
and this is the call:
#POST("openUsvDeal")
fun openUsvDeal(
#Body params: RequestBody,
#Query("timestamp") timeStamp: Long,
#Query("appid") appid: String = Constants.APP_ID,
): Call<JsonObject>
This is what works me for the current version of retrofit 2.6.2,
First of all, we need to add a Scalars Converter to the list of our Gradle dependencies, which would take care of converting java.lang.String objects to text/plain request bodies,
implementation'com.squareup.retrofit2:converter-scalars:2.6.2'
Then, we need to pass a converter factory to our Retrofit builder. It will later tell Retrofit how to convert the #Body parameter passed to the service.
private val retrofitBuilder: Retrofit.Builder by lazy {
Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
}
Note: In my retrofit builder i have two converters Gson and
Scalars you can use both of them but to send Json body we need to
focus Scalars so if you don't need Gson remove it
Then Retrofit service with a String body parameter.
#Headers("Content-Type: application/json")
#POST("users")
fun saveUser(#Body user: String): Response<MyResponse>
Then create the JSON body
val user = JsonObject()
user.addProperty("id", 001)
user.addProperty("name", "Name")
Call your service
RetrofitService.myApi.saveUser(user.toString())
You can use hashmap if you don't want to create pojo class for every API call.
HashMap<String,String> hashMap=new HashMap<>();
hashMap.put("email","this#gmail.com");
hashMap.put("password","1234");
And then send like this
Call<JsonElement> register(#Body HashMap registerApiPayload);
After so much effort, found that the basic difference is you need to send the JsonObject instead of JSONObject as parameter.
use following to send json
final JSONObject jsonBody = new JSONObject();
try {
jsonBody.put("key", "value");
} catch (JSONException e){
e.printStackTrace();
}
RequestBody body = RequestBody.create(okhttp3.MediaType.parse("application/json; charset=utf-8"),(jsonBody).toString());
and pass it to url
#Body RequestBody key
If you don't want to create extra classes or use JSONObject you can use a HashMap.
Retrofit interface:
#POST("/rest/registration/register")
fun signUp(#Body params: HashMap<String, String>): Call<ResponseBody>
Call:
val map = hashMapOf(
"username" to username,
"password" to password,
"firstName" to firstName,
"surname" to lastName
)
retrofit.create(TheApi::class.java)
.signUp(map)
.enqueue(callback)
Things required to send raw json in Retrofit.
1) Make sure to add the following header and remove any other duplicate header. Since, on Retrofit's official documentation they specifically mention-
Note that headers do not overwrite each other. All headers with the
same name will be included in the request.
#Headers({"Content-Type: application/json"})
2) a. If you are using a converter factory you can pass your json as a String, JSONObject, JsonObject and even a POJO. Also have checked, having ScalarConverterFactory is not necessary only GsonConverterFactory does the job.
#POST("/urlPath")
#FormUrlEncoded
Call<Response> myApi(#Header("Authorization") String auth, #Header("KEY") String key,
#Body JsonObject/POJO/String requestBody);
2) b. If you are NOT using any converter factory then you MUST use okhttp3's RequestBody as Retrofit's documentation says-
The object will also be converted using a converter specified on the
Retrofit instance. If no converter is added, only RequestBody can be
used.
RequestBody requestBody=RequestBody.create(MediaType.parse("application/json; charset=utf-8"),jsonString);
#POST("/urlPath")
#FormUrlEncoded
Call<Response> myApi(#Header("Authorization") String auth, #Header("KEY") String key,
#Body RequestBody requestBody);
3) Success!!
Based on the top answer, I have a solution to not have to make POJOs for every request.
Example, I want to post this JSON.
{
"data" : {
"mobile" : "qwer",
"password" : "qwer"
},
"commom" : {}
}
then, I create a common class like this:
import java.util.Map;
import java.util.HashMap;
public class WRequest {
Map<String, Object> data;
Map<String, Object> common;
public WRequest() {
data = new HashMap<>();
common = new HashMap<>();
}
}
Finally, when I need a json
WRequest request = new WRequest();
request.data.put("type", type);
request.data.put("page", page);
The request marked annotation #Body then can pass to Retrofit.
For more clarity on the answers given here, this is how you can use the extension functions. This is only if you are using Kotlin
If you are using com.squareup.okhttp3:okhttp:4.0.1 the older methods of creating objects of MediaType and RequestBody have been deprecated and cannot be used in Kotlin.
If you want to use the extension functions to get a MediaType object and a ResponseBody object from your strings, firstly add the following lines to the class in which you expect to use them.
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.toRequestBody
You can now directly get an object of MediaType this way
val mediaType = "application/json; charset=utf-8".toMediaType()
To get an object of RequestBody first convert the JSONObject you want to send to a string this way. You have to pass the mediaType object to it.
val requestBody = myJSONObject.toString().toRequestBody(mediaType)
you need to set #Body in interface
#Headers({ "Content-Type: application/json;charset=UTF-8"})
#POST("Auth/Login")
Call<ApiResponse> loginWithPhone(#Body HashMap<String, String> fields);
To pass the raw body to retrofit just use:
HashMap<String,String> SendData =new HashMap<>();
SendData.put("countryCode",ccode);
SendData.put("phoneNumber",phone);
Call<ApiResponse>call = serviceInterface.loginWithPhone(SendData);
this works for me:
Solved my problem based on TommySM answer (see previous).
But I didn't need to make login, I used Retrofit2 for testing https GraphQL API like this:
Defined my BaseResponse class with the help of json annotations (import jackson.annotation.JsonProperty).
public class MyRequest {
#JsonProperty("query")
private String query;
#JsonProperty("operationName")
private String operationName;
#JsonProperty("variables")
private String variables;
public void setQuery(String query) {
this.query = query;
}
public void setOperationName(String operationName) {
this.operationName = operationName;
}
public void setVariables(String variables) {
this.variables = variables;
}
}
Defined the call procedure in the interface:
#POST("/api/apiname")
Call<BaseResponse> apicall(#Body RequestBody params);
Called apicall in the body of test:
Create a variable of MyRequest type (for example "myLittleRequest").
Map<String, Object> jsonParams = convertObjectToMap(myLittleRequest);
RequestBody body =
RequestBody.create(okhttp3.MediaType.parse("application/json; charset=utf-8"),
(new JSONObject(jsonParams)).toString());
response = hereIsYourInterfaceName().apicall(body).execute();
I wanted to compare speed of volley and retrofit for sending and receiving data I wrote below code (for retrofit part)
first dependency:
dependencies {
implementation 'com.squareup.retrofit2:retrofit:2.4.0'
implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
}
Then interface:
public interface IHttpRequest {
String BaseUrl="https://example.com/api/";
#POST("NewContract")
Call<JsonElement> register(#Body HashMap registerApiPayload);
}
and a function to set parameters to post data to server(In MainActivity):
private void Retrofit(){
Retrofit retrofitRequest = new Retrofit.Builder()
.baseUrl(IHttpRequest.BaseUrl)
.addConverterFactory(GsonConverterFactory.create())
.build();
// set data to send
HashMap<String,String> SendData =new HashMap<>();
SendData.put("token","XYXIUNJHJHJHGJHGJHGRTYTRY");
SendData.put("contract_type","0");
SendData.put("StopLess","37000");
SendData.put("StopProfit","48000");
final IHttpRequest request=retrofitRequest.create(IHttpRequest.class);
request.register(SendData).enqueue(new Callback<JsonElement>() {
#Override
public void onResponse(Call<JsonElement> call, Response<JsonElement> response) {
if (response.isSuccessful()){
Toast.makeText(getApplicationContext(),response.body().toString(),Toast.LENGTH_LONG).show();
}
}
#Override
public void onFailure(Call<JsonElement> call, Throwable t) {
}
});
}
And I found Retrofit faster than volley in my case.
API Call
#Headers("Content-Type: application/json")
#POST("/set_data")
Call<CommonResponse> setPreferences(#Body RequestData request);
Note: Use GSON library of Retrofit
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class RequestData {
#SerializedName("access_token")
#Expose
private String accessToken;
#SerializedName("data")
#Expose
private Data data;
// The above 'Data' is another similar class to add inner JSON objects. JSONObject within a JSONObject.
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public void setData(Data data) {
this.data = data;
}
}
I guess that will help, rest all integration you might already have had and we don't need anything fancy to use above code snippet. It's working perfectly for me.
I tried this:
When you are creating your Retrofit instance, add this converter factory to the retrofit builder:
gsonBuilder = new GsonBuilder().serializeNulls()
your_retrofit_instance = Retrofit.Builder().addConverterFactory( GsonConverterFactory.create( gsonBuilder.create() ) )
While creating OkHttpClient that will be used for Retrofit.
add an Interceptor like this.
private val httpClient = OkHttpClient.Builder()
.addInterceptor (other interceptors)
........................................
//This Interceptor is the main logging Interceptor
.addInterceptor { chain ->
val request = chain.request()
val jsonObj = JSONObject(Gson().toJson(request))
val requestBody = (jsonObj
?.getJSONObject("tags")
?.getJSONObject("class retrofit2.Invocation")
?.getJSONArray("arguments")?.get(0) ?: "").toString()
val url = jsonObj?.getJSONObject("url")?.getString("url") ?: ""
Timber.d("gsonrequest request url: $url")
Timber.d("gsonrequest body :$requestBody")
chain.proceed(request)
}
..............
// Add other configurations
.build()
Now your every Retrofit call's URL and request body will be logged in Logcat. Filter it by "gsonrequest"
Updated solution for 2022:
One of the first things to check is that your post request is working via a third party API such as postman. I had done this before coming across the solutions on this page.
The next step is to add logging facilities to your retrofit instance. Click here on how to add logging to retrofit.
Upon adding logging I saw a 500 server error, based on the fact that the end-point was working via Postman we know that the error must be something to do with the format of the data that is passed to the Post method.
Your retrofit builder should look like this:
val retrofitInstance = Retrofit.Builder()
.baseUrl("https://pacific-tundra-61285.herokuapp.com/")
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.client(httpClient)
.build()
This post helped a lot in helping solve this problem and provided the correct way to convert the object into the correct "application/json" format when making the post request. There were a few deprecated methods used in the kotlin version, the new code is very similar:
private fun createRequestBody(vararg params : Pair<String, Any>) =
JSONObject(mapOf(*params)).toString()
.toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull())
The generic value parameter in the pair is set to Any so that you can handle the different types related to your object.
The final piece just for clarity is the actual post method and the code that is used to invoke the post request.
#POST("create/")
fun create(#Body params : RequestBody) : Call<YourObject>
val call = apiService.create(createRequestBody(
"string" to object // You should pass in any key and value pairs here.
Finally call enqueue on the call as usual.
JSONObject showing error please use
JsonObject paramObject = new JsonObject();
paramObject.addProperty("loginId", vMobile_Email);
Add ScalarsConverterFactory.create() method and pass hard code
#Headers(value = "Content-Type: application/json")
#POST("api/Persona/Add")
Call<Persona> AddPersona(#Header("authorization") String token, #Body JsonObject object);
JsonObject postParam = new JsonObject();
postParam.addProperty("PersonaCedula", item.getPersonaCedula());

How can I handle empty response body with Retrofit 2?

Recently I started using Retrofit 2 and I faced an issue with parsing empty response body. I have a server which responds only with http code without any content inside the response body.
How can I handle only meta information about server response (headers, status code etc)?
Edit:
As Jake Wharton points out,
#GET("/path/to/get")
Call<Void> getMyData(/* your args here */);
is the best way to go versus my original response --
You can just return a ResponseBody, which will bypass parsing the response.
#GET("/path/to/get")
Call<ResponseBody> getMyData(/* your args here */);
Then in your call,
Call<ResponseBody> dataCall = myApi.getMyData();
dataCall.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Response<ResponseBody> response) {
// use response.code, response.headers, etc.
}
#Override
public void onFailure(Throwable t) {
// handle failure
}
});
If you use RxJava, then it's better to use Completable in this case
Represents a deferred computation without any value but only indication for completion or exception. The class follows a similar event pattern as Reactive-Streams: onSubscribe (onError|onComplete)?
http://reactivex.io/RxJava/2.x/javadoc/io/reactivex/Completable.html
in the accepted answer:
#GET("/path/to/get")
Observable<Response<Void>> getMyData(/* your args here */);
If the endpoint returns failure response code, it will still be in the onNext and you will have to check the response code yourself.
However, if you use Completable.
#GET("/path/to/get")
Completable getMyData(/* your args here */);
you will have only onComplete and onError.
if the response code is success it will fire the onComplete else it will fire onError.
If you are using rxjava, use something like :
#GET("/path/to/get")
Observable<Response<Void>> getMyData(/* your args here */);
With kotlin, using the return type Call<Void> still throws IllegalArgumentException: Unable to create converter for retrofit2.Call<java.lang.Void>
Using Response instead of Call resolved the issue
#DELETE("user/data")
suspend fun deleteUserData(): Response<Void>
Here is an example Kotlin in MVVM with service, Repository and ViewModel:
Service:
#POST("/logout")
suspend fun logout(#Header("Authorization") token: String):Response<Unit>
Repository:
//logout
private val mLogoutResponse = MutableLiveData<String>()
val logoutResponse: LiveData<String>
get() {
return mLogoutResponse
}
suspend fun logout(token: String) {
try {
val result=quizzerProfileApi.logout(token)
if(result.code()!=0)
{
mLogoutResponse.postValue(result.code().toString())
}
} catch (e: Exception) {
Log.d("ProfileRepository", "logout: Error: $e")
}
}
ViewModel:
fun logout(token: String) {
viewModelScope.launch {
repository.logout(token)
}
}
val logoutResponseCd: LiveData<String>
get() = repository.logoutResponse
in Activity:
private fun logout() {
myViewModel.logout(token)
myViewModel.logoutResponseCd.observe(this, Observer {
if(it!="0"){
Log.d(TAG, "logout: code= $it")
finish()
}
else
Toast.makeText(this, "Error logging out: $it", Toast.LENGTH_SHORT).show()
})
}
Here is how I used it with Rx2 and Retrofit2, with PUT REST request:
My request had a json body but just http response code with empty body.
The Api client:
public class ApiClient {
public static final String TAG = ApiClient.class.getSimpleName();
private DevicesEndpoint apiEndpointInterface;
public DevicesEndpoint getApiService() {
Gson gson = new GsonBuilder()
.setLenient()
.create();
OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder();
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
okHttpClientBuilder.addInterceptor(logging);
OkHttpClient okHttpClient = okHttpClientBuilder.build();
apiEndpointInterface = new Retrofit.Builder()
.baseUrl(ApiContract.DEVICES_REST_URL)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build()
.create(DevicesEndpoint.class);
return apiEndpointInterface;
}
The interface:
public interface DevicesEndpoint {
#Headers("Content-Type: application/json")
#PUT(ApiContract.DEVICES_ENDPOINT)
Observable<ResponseBody> sendDeviceDetails(#Body Device device);
}
Then to use it:
private void sendDeviceId(Device device){
ApiClient client = new ApiClient();
DevicesEndpoint apiService = client.getApiService();
Observable<ResponseBody> call = apiService.sendDeviceDetails(device);
Log.i(TAG, "sendDeviceId: about to send device ID");
call.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer<ResponseBody>() {
#Override
public void onSubscribe(Disposable disposable) {
}
#Override
public void onNext(ResponseBody body) {
Log.i(TAG, "onNext");
}
#Override
public void onError(Throwable t) {
Log.e(TAG, "onError: ", t);
}
#Override
public void onComplete() {
Log.i(TAG, "onCompleted: sent device ID done");
}
});
}
You can try this one
Retrofit retrofit = new Retrofit.Builder().baseUrl(baseUrl)
.addConverterFactory(new NullOnEmptyConverterFactory())
.client(okHttpClient).build();
class NullOnEmptyConverterFactory extends Converter.Factory {
#Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
final Converter<ResponseBody, ?> delegate = retrofit.nextResponseBodyConverter(this, type, annotations);
return (Converter<ResponseBody, Object>) body -> {
if (body.source().exhausted()) return null;
return delegate.convert(body);
};
}
}
Kotlin, Retrofit
#POST
suspend fun accountVerification(
#Body requestBody: RequestBody
): Response<Unit>
and success can be check using
if (response.isSuccessful) { }

Categories

Resources