In my application I want send some data to server and for this I used Retrofit library.
I should send data such as below:
{
"hours": ["22:05","19:57"]
}
I write below codes for Api services:
#POST("profile")
suspend fun postSubmitDrug(#Header("Authorization") token: String, #Body body: RequestBody):Response<ProfileData>
And write below codes info fragment for send data to server:
private lateinit var requestBody: RequestBody
var hourMinuteList = mutableListOf<String>()
val multiPart = MultipartBody.Builder().setType(MultipartBody.FORM).addFormDataPart("_method", "post")
multiPart.addFormDataPart("hours[]", parentActivity.hourMinuteList.toString())
requestBody = multiPart.build()
But send this list such as below:
{
"hours": [22:05,19:57]
}
How can I fix it and send list of string to server?
Use com.google.gson, and when you create your retrofitCLient call .addConverterFactory(create(GsonBuilder()))
create data model, ex:
data class RequestBody(
val hours: ArrayList<String>? = null
)
and just call your endpoint
#POST("profile")
suspend fun postSubmitDrug(#Header("Authorization") token: String, #Body body: RequestBody):Response<ProfileData>
and this is all, gson will automatically serialize your data to json
I am using retrofit2 for network calls in my project. For a post method I need to pass request body data. So decided to use #Body annotation. This converts the given Gson.JsonObject to Array like below mentioned.
Retrofit dependencies used
//retrofit
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.okhttp3:logging-interceptor:4.7.2'
Expected
{
"key1": "value1",
"key2": "value2",
"key3": value3
}
But got converted like
[{
"key1": "value1",
"key2": "value2",
"key3": value3
}]
This is my API service call
#Headers("Content-Type: application/json")
#POST("Account/LoginAsync")
fun doLogin(#Body payLoad: JsonObject): Call<JsonObject>
And data class is
data class LoginRequest(
val email: String,
val password: String,
val rememberMe: Boolean
)
Internally what I am doing is
From the specified values in LoginRequest generating org.json.JSONObject like below
private fun generateLoginPayload(
email: String,
password: String,
rememberMe: Boolean
): JSONObject {
return JSONObject().apply {
put(LOGIN_EMAIL, email)
put(LOGIN_PASSWORD, password)
put(LOGIN_REMEMBER_ME, rememberMe)
}
}
And then
val payload = generateLoginPayload(email, password, rememberMe)
val requestCall = ApiService.create()
.doLogin(JsonParser().parse(payload.toString()) as JsonObject)
After enqueuing the request call got error response as 400 (bad request).
I have checked from the debug values.
What is the possible reason for it ?
Solution 1
If you're trying to send a json payload containing the email, password, and rememberMe option to the Account/LoginAsync endpoint you don't need the generateLoginPayload function.
First, refactor your API service function to something like this:
#Headers("Content-Type: application/json")
#POST("Account/LoginAsync")
fun doLogin(#Body payLoad: LoginRequest): Call<JSONObject>
Then update the payload
val payload = LoginRequest(email, password, rememberMe)
val requestCall = ApiService.create().doLogin(payload)
Solution 2
If you really want to use the JSONObject object in the request body there's no need to convert the payload object to a string, only to later use your JsonParser to convert it back to JSONObject. Just update the last part of the code to:
val payload = generateLoginPayload(email, password, rememberMe)
val requestCall = ApiService.create().doLogin(payload)
I'm not familiar with the JsonParser object. If it's a custom class, please also include the body of the parse method so we can inspect it (if the above solutions don't work).
I am using Retrofit for API calls.. My API is like this https://test/update/123456789..
In this API need to pass "123456789" as value.. Can anyone help me to do this using Retrofit.. How can I pass this value w/o Query..
Thanks in Advance..:)
fun updatePriceTesting(#Query("device_id") deviceId: String, #Query("appId") appId: String, #Body RequestBody: RequestBody) : Deferred
Response>
To pass a value in retrofit you have to annotate it as #Query,or #Path,in case you dont want to use #Query,you can use #Path in the method parameter ,for example : if you want to send a value to your api , let us say
www.fb.com/post,to pass id of post to retrieve,we have to do it like that:
#Get("www.fb.com/post/{post_id}")
if your id is an int then you can use :
Call<Post> getPost( #Path("post_id") int postId);
but if it was an string :
Call<Post> getPost( #Path("post_id") String postId);
and then you can call this method :
yourApiServiceObj.getPost(thePostId);
As it's appears that this is not clear I will write it as answer
if your url will be like this update/123456789?appId=5452266 which is update/device_id?appId=5452266 then your function call will be
#FormUrlEncoded
#POST("update/{device_id}")
fun updatePriceTesting(#Path("device_id") deviceId: String, #Field("appId") appId: String, #Body RequestBody: RequestBody) : Deferred
if your url will be like this update/5452266?device_id=123456789 which is update/appId?device_id=123456789 then your function call will be
#FormUrlEncoded
#POST("update/{appId}")
fun updatePriceTesting(#Field("device_id") deviceId: String, #Path("appId") appId: String, #Body RequestBody: RequestBody) : Deferred
if your url will be like this update/123456789/5452266 which is update/device_id/appId then your function call will be
#FormUrlEncoded
#POST("update/{device_id}/{appId}")
fun updatePriceTesting(#Path("device_id") deviceId: String, #Path("appId") appId: String, #Body RequestBody: RequestBody) : Deferred
#Headers("Accept: application/json")
#POST("/api/v1/search/filter")
#FormUrlEncoded fun searchFilter(#Field("leaf_id") leaf_id: Int,
#Field("lang") lang: String,
#Field("token") token: String,
#Field("ud_id") ud_id: String,
#Field("min_price") min_price: Float,
#Field("max") max: Float,
#Field("page") page: Int,
#Field("per_page") per_page: Int,
#Field("properties") properties:JSONObject): Observable<RespProductsInCatg>
here in the #Field("properties") I'm trying to send json object but getting failed response.
In Postman its returning proper response
Try to use a data class for parameters to be passed in properties field.
Then pass an object of that data class, and use #Body instead of #Field for properties.
Try creating whole request as an object and add the same as #Body param without using #Field values.
Something like
data class Request(var leaf_id:Int,
var lang: String,
.
.
.
var properties: List<Property>);
and
data class Property(var param1:Int, .... );
construct the Request Object and try adding just that as #Body param.
I am trying to make POST request using the Retrofit 2. The request type is form-data NOT application/x-www-form-urlencoded.
I am only posting data not the files in the request and the response is in the form of JSON.
I have tried #FormUrlEncoded, #Multipart but it is not working.
I have tried following request
1. First Attempt
#FormUrlEncoded
#POST("XXXX")
Call<PlanResponse> getPlanName(#Field(Constants.ACTION_ID) String actionId, #Field(Constants.OFFER_CODE) String offerCode);
2. Second Attempt
#Headers({"Content-Type: multipart/form-data","Content-Type: text/plain"})
#FormUrlEncoded
#POST("XXXX")
Call<PlanResponse> getPlans(#Body #FieldMap(encoded = false) Map<String, String> data);
3. Third Attempt
#Headers("Content-Type: multipart/form-data")
#Multipart
#POST("XXXX")
Call<PlanResponse> myPlans(#Part(Constants.ACTION_ID) String actionId, #Part(Constants.OFFER_CODE) String offerCode);
I am only getting the body as null. It is working with the POSTMAN.
I have also search about form-data and application/x-www-form-urlencoded and found that if the data is binary then use form-data and if data is ASCII then use application/x-www-form-urlencoded
I am trying find Is form-data is not supported by the Retrofit?
POSTMAN request
Cache-Control: no-cache
Postman-Token: XXXXXXXXXXXX-XXXXXXXXXXXX-XXXXXXXXXXXX-XXXXXXXXXXXX-XXXXXXXXXXXX
Content-Type: multipart/form-data; boundary=---- WebKitFormBoundaryXXXXXXXXXXXX
----WebKitFormBoundaryXXXXXXXXXXXX
Content-Disposition: form-data; name="actionId"
1000
----WebKitFormBoundaryXXXXXXXXXXXX
Content-Disposition: form-data; name="offerCode"
MYCODE
----WebKitFormBoundaryXXXXXXXXXXXX
I can only add HTTP Generated code snipped from POSTMAN
Here's another Solution using request body:
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("param1", param1)
.addFormDataPart("param2", param2)
.build();
apiInterface.somePostMethod(requestBody).enqueue(
//onResponse onFailure methods
);
here's my api inteface POST method
#POST("somePostMethod")
Call<ResponseBody> somePostMethod(#Body RequestBody body);
Hope it helps.
In retrofit 2.0 to perform POST request like above, you should use RequestBody type for your parameter like this.
#Multipart
#POST("XXXX")
Call<PlanResponse> myPlans(#Part(Constants.ACTION_ID) RequestBody actionId, #Part(Constants.OFFER_CODE) RequestBody offerCode);
And here how to get requestBody from String.
String somevalue = "somevalue";
RequestBody body = RequestBody.create(MediaType.parse("text/plain"), somevalue);
I wanted to pass an array of ids to an existing request.
I tried several variants from here, Retrofit - Send request body as array or number, How to send PUT request with retrofit string and array list of model I need to use URL encoded, but they didn't work. Then I tried android retrofit send array as x-www-form-urlencoded.
I added [] to a list parameter and List to it's type:
#FormUrlEncoded
#POST("your_request/")
fun sendIds(
#Field("token") token: String,
#Field("city_id") cityId: Int?,
#Field("description") description: String,
#Field("ids[]") ids: List<Int>? // Add '[]' here.
): Deferred<YourResponse>
Then called it as usual (with Kotlin coroutines):
api.sendIds("f0123abc", null, "description", listOf(1, 2, 3)).await()
See also Is it possible to send an array with the Postman Chrome extension? to understand how it looks like in Postman.
form-data is supported for sure.
I will make you clear using an example of typical signup process.
First of all add a header
#FormUrlEncoded
in your user client.
Use
#FieldMap
instead of direct objects. So your user-client code will something like this
#POST("signup/")
#FormUrlEncoded
Call<ResponseModel> signup(#FieldMap Map<String,String> params);
Now in your main activity, make a Hashmap all of your data like this,
Map<String,String> params = new HashMap<String, String>();
params.put("fullname", fullname);
params.put("city", city);
params.put("state",state);
params.put("address",address);
params.put("email",email);
params.put("password1", password1);
params.put("password2", password2);
Now simple pass this hashmap into the method like this
Call<ResponseModel> call = service.signup(params);
call.enqueue(new Callback<ResponseModel>() {
#Override
public void onResponse(Call<ResponseModel> call, Response<ResponseModel> response) {
if (response.isSuccessful()) {
Toast.makeText(SignUp.this,response.body.getData,Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(SignUp.this, "Error : ", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onFailure(Call<ResponseModel> call, Throwable t) {
t.printStackTrace();
Toast.makeText(SignUp.this, "Server Unavailable : "+t.toString(), Toast.LENGTH_SHORT).show();
}
});
Here's another Solution using the request body form-data in Kotlin. This solution work for me in Kotlin.
val request = ServiceBuilder.buildService(TmdbEndpoints::class.java)
val requestBody: RequestBody = MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("email", "abc#gmail.com")
.addFormDataPart("password", "admin")
.addFormDataPart("push_token", "token")
.addFormDataPart("device_id", "1112222")
.addFormDataPart("platform", "android")
.addFormDataPart("device_name", "my device")
.addFormDataPart("version", "1.2")
.build()
val call = request.userFigLogin(requestBody)
call.enqueue(object : Callback<LoginResult> {
override fun onFailure(call: Call<LoginResult>, t: Throwable) { }
override fun onResponse(call: Call<LoginResult>,
response: retrofit2.Response<LoginResult>) { }
})
You should use RequestBody type for your parameter like this.
#POST("api/login")
fun userFigLogin(#Body body: RequestBody): Call<LoginResult>
For Kotlin, This is another way of doing it. For api that do not accept FormUrEncoded data.
fun login(email: String, password: String, grantType: String):
Single<TokenModel> {
var userNameB:RequestBody=
email.toRequestBody(email.toMediaTypeOrNull())
var passwordB: RequestBody =
password.toRequestBody(password.toMediaTypeOrNull())
var grantTypeB: RequestBody =
grantType.toRequestBody(grantType.toMediaTypeOrNull())
return userApi.loginUSer(userNameB,passwordB,grantTypeB)
.map { TokenModel(it.accessToken, it.refreshToken) }
}
Then.
#Multipart
#POST("auth/token/")
fun loginUSer(
#Part("username") request: RequestBody,
#Part("password") passwordB: RequestBody,
#Part("grant_type") grantTypeB: RequestBody
): Single<Token>
just remove this from header
defaultProperties["Content-Type"] = "application/json"
I think this can help you
#Multipart
#Headers( "Content-Type: application/x-www-form-urlencoded")
#POST("api/register")
fun postRegister(
#Part("authtype") authtype: String,
#Part("channel")channel : String,
#Part("children")children : List<String>,
#Part("names") names: List<String>,
#Part("email") email: String,
#Part("password")password : String,
#Part("name") name: String,
#Part("timezone") timezone: Int,
#Part("timezone_name")timezone_name : String,
#Part("token_device")token_device : String,
#Part("imageData") imageData: String,
#Part("mimeType") mimeType: String,
#Part("extension") extension: String,
): Call<ResponseBase>