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'm trying to post some data with retrofit 2 but I'm gettins some problems... and don't find any example like this...
This is the body that I have to send:
{
"birthday": "12-01-1987",
"name": bob,
"activity": {
"activity_preferences": {
"user_subjects": [4,7,8],
"user_allergies": [1,6,10],
}
}
}
This is my data class:
data class GenericFormDataEntity(
var birthday: String,
var name: String,
#SerializedName("activity")
var food: ActivityEntity?
)
data class ActivityEntity(#SerializedName("activity_preferences")val activityPreferences: ActivityPreferencesEntity)
data class ActivityPreferencesEntity(#SerializedName("user_Subjects")var userSubjects:List<Int>?,#SerializedName("user_allergies")var userAllergies: List<Int>?)
This is the method that I'm trying to build the json:
fun getUserFormEntity(): String{
val paramObject = JSONObject()
paramObject.put("birthday", birthday)
paramObject.put("name", name)
paramObject.put("activity", getActivityEntity())
return paramObject.toString()
}
private fun getActivityEntity(): ActivityEntity{
return ActivityEntity(ActivityPreferencesEntity(selectedSubjectList, selecteAllergiesList))
}
And this is the json that is returning me:
{\"birthday\":\"23-12-2019\",\"name\":Bob,"activity\":\"ActivityEntity(activity_preferences=ActivityPreferencesEntity(user_Subjects=[4,7,8], user_allergies=[1,6,10])"}"
My question is, how can I get the correct json that I have to send as a body:
#Headers("Accept: application/json")
#POST("xxxxxxxx")
suspend fun saveUserData(#Body userFormData: String)
You need to stringify getActivityEntity using Gson.
Gson.toJson(getActivityEntity())
Also, from your API I infer that you are using retrofit why not pass along the entire instance of GenericFormDataEntity as the body for your API.
For enabling this you need to follow by adding GsonConverterFactory.create(gson) to your retrofit.
Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create(gson))
.callFactory(okHttpClient)
.build()
I am creating a retrofit get request where i need to pass a data base query and some spacial character like '$' in URL in kotlin. But I am getting error.
java.lang.IllegalArgumentException: URL query string must not have replace block. For dynamic query parameters use #Query.
This is URL which I am using in postman but cant in retrofil
https://someURL.com?customParam=true&pageSize=100&query=$filter=(drivercell eq'1111111119')$orderby=creationTimedesc&withTotalPages=true
This is the code of calling retrofit method
val restServiceModel = DRestServiceModel.create()
val model = restServiceModel.getTripsData("Basic bWs6SU9UMTIzNCM=", "application/json", "\$filter=(drivercell%20eq'1111111119')")
This is method
#GET("inventory/managedObjects?customParam=true&pageSize=100&{query}\$orderby=creationTimedesc&withTotalPages=true")
fun getTripsData(#Header("Authorization") token: String, #Header("Content-Type") contentType: String, #Query("query", encoded = true) query : String): Single<TripsResponseModel>
Please help me.
Problem is you are trying to put Path param in a middle of a query while supplying it via another Query. You should rework your request. Try something like:
#GET("inventory/managedObjects")
fun getTripsData(#Header("Authorization") token: String,
#Header("Content-Type") contentType: String,
#Query("customParam") customParam: Boolean?,
#Query("pageSize") pageSize: Int?,
#Query("query", encoded = true) query: String,
#Query("withTotalPages") withTotalPages: Boolean?): Single<TripsResponseModel>
And use it like:
val model = restServiceModel.getTripsData("Basic bWs6SU9UMTIzNCM=", "application/json", true, 100, "your query_goes here", true)
This way should it work.
I've created a POST request using rxjava and retrofit that successfully hits my backend server and logs the user in (I get a 201 response in my console, all good). However, I want to then retrieve the users access token that is returned, but when I try to access the rxjava result, it just gives me the object I passed to it. Nowhere can I find out how to get the json success response. I have also verified there is in fact a response in Postman, so it's something with how I make this call.
Here is my Retrofit portion
#Headers("Content-Type: application/json")
#POST("api/v1/login")
fun loginTask(#Body credentials: UserLogin)
: Observable<UserLogin>
And the correspoinding API function
class ApiFunctions(val apiService: LunchVoteApi) {
fun provideHello(): io.reactivex.Observable<Hello> {
return apiService.helloMessage()
}
fun loginTask(email: String, password: String): io.reactivex.Observable<UserLogin> {
val credentials: UserLogin = UserLogin(email, password)
return apiService.loginTask(credentials)
}
}
The UserLogin model that is deserialized by Gson
data class UserLogin(
#SerializedName("email") val email: String,
#SerializedName("password") val password: String
)
And finally the call in my LoginActivity
val loginTask = ApiProvider.provideLoginTask()
override fun doInBackground(vararg params: Void): Boolean? {
// TODO: attempt authentication against a network service.
try {
// Simulate network access.
// Thread.sleep(2000)
compositeDisposable.add(
loginTask.loginTask(mEmail, mPassword)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe ({
result ->
System.out.println(result.toString())
}, { error ->
System.out.println(error)
})
)
} catch (e: InterruptedException) {
return false
}
The confusion comes when I try to access the result -> portion of the compositeDisposable call. It just prints out the UserLogin object. Am I missing something here? Thanks.
Turns out I was returning my UserLogin type instead of a pojo object with an access token property.
Changing my retrofit call to #Headers("Content-Type: application/json")
#POST("api/v1/login")
fun loginTask(#Body credentials: UserLogin)
: Observable<AccessToken>
And creating a new model
data class AccessToken(
#SerializedName("accessToken") val email: String
)
I am now able to print out the token. Thanks to #john-oreilly
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>