I do a post request to Firebase push notifications:
#FormUrlEncoded
#POST("https://fcm.googleapis.com/fcm/send")
suspend fun createPushNotifications(
#Header("Authorization") Authorization: String,
#Field("to") to: String,
#Field("data") data: String
): Response<ResponseBody>
// Create Retrofit
val retrofit = Retrofit.Builder()
.baseUrl(urlApp)
.addConverterFactory(GsonConverterFactory.create())
.build()
// Create Service
val service = retrofit.create(notificationAPI::class.java)
var data:String = "{\"body\":\"value\",\"title\":\"Collapsing A\"}"
// Do the POST request and get response
val response = service.createPushNotifications(FireBaseKey,deviceId, data)
Question : I get back a wrong format data payload
Message
data payload:{data={"body":"value","title":"Collapsing A"}}
But what i need is a payload in this format:
{data: {"body":"value","title":"Collapsing A"}}
Related
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 Retrofit 2 to upload an audio file to an Azure blob storage service via Azure's REST APIs.
The upload appears to work, but the file stored in the Azure blob container is corrupt because as well as the audio data it contains what appears to be HTTP headers. For example, these are the contents of one uploaded file:
--3c88cdb1-5946-432d-a129-cc8e930d014c
Content-Disposition: form-data; name="tape";
filename="/data/user/0/blahblah.mp4"
Content-Type: audio/mp4
Content-Length: 8365
...expected binary data blah blah blah ....
--3c88cdb1-5946-432d-a129-cc8e930d014c--
What am I doing wrong?
My upload function looks like this:
val tapeFile = File(fileName)
val tapePart = tapeFile.asRequestBody("audio/mp4".toMediaType())
val tapeBodyPart = MultipartBody.Part.createFormData("tape",tapeFile.absolutePath, tapePart)
tapeAzureWebService.uploadTape(url, tapeBodyPart).enqueue(object : Callback<ResponseBody> {
override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
if (response.isSuccessful) {
etc etc
My Retrofit interface interface is like this:
#Multipart
#PUT
fun uploadTape(#Url url: String,
#Part tape: MultipartBody.Part): Call<ResponseBody>
(It's using #URL because I'm using Azure SAS, with dynamic URLs with authentication embedded in the URL as a series of query strings, and that works very well and is a neat hint for anyone who stumbles on this, by the way, since it prevents Retrofit from encoding the URL and query.)
And my OKHttp Client looks like this, adding some headers that Azure demands:
class TapeAzureWebServiceAPI {
fun service() : TapeAzureWebService {
val headerInterceptor = object: Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val original = chain.request()
val requestBuilder = original.newBuilder()
.header("x-ms-version", "2015-12-11")
.header("x-ms-blob-type","BlockBlob")
val request = requestBuilder.build()
return chain.proceed(request)
}
}
val loggingInterceptor = HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger {
override fun log(message: String) {
logI("retrofit: $message")
}
}).setLevel(HttpLoggingInterceptor.Level.BODY)
val client : OkHttpClient = OkHttpClient.Builder().apply {
this.addInterceptor(headerInterceptor)
this.addInterceptor(loggingInterceptor)
}.build()
val retrofit = Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(AZURE_URL)
.client(client)
.build()
return retrofit.create(TapeAzureWebService::class.java)
}
}
If I use a simple RequestBody rather than a multipart form, I still get the same corruption to the audio file, though there are fewer headers in the audio file.
I've looked at this a long time, and I can't tell if it's something I'm doing wrong in Retrofit, whether Azure wants different headers, or whether Azure simply doesn't like multipart form data.
thanks
John
remove #Multipart just add,
#Headers( "x-ms-blob-type: BlockBlob", "x-ms-blob-content-type: image/png")
#PUT
suspend fun uploadDocument(#Url url: String, #Body request: RequestBody)
and pass request body as,
val mediaType = "image/png".toMediaTypeOrNull()
val body = yourImageFile.asRequestBody(mediaType)
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 trying to access IBM's Speech to Text service without using the library. I am using Retrofit with GSON.
The issue is in the authentication, which apparently does not occur correctly, returning code 401. From the official documentation, the HTTP request should come in this format
curl -X POST -u "apikey:{apikey}" \
--header "Content-Type: audio/flac" \
--data-binary #{path_to_file}audio-file.flac \
"{url}/v1/recognize"
When I test the curl command with my credentials, the service works fine.
This is the interface I'm using
interface SpeechToTextApi {
#Multipart
#POST("v1/recognize")
fun speechToText(
#Header("Authorization") authKey: String,
#Part("file") filename: RequestBody,
#Part voiceFile: MultipartBody.Part
): Call<List<SpeechToText>>
}
where I have the following data classes
data class SpeechToText(val results: List<SttResult>)
data class SttResult(val alternatives: List<RecognitionResult>, val final: Boolean)
data class RecognitionResult(val confidence: Float, val transcript: String)
and this is how I set up Retrofit
private val retrofit = Retrofit.Builder()
.baseUrl(STT_BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
private val service = retrofit.create(SpeechToTextApi::class.java)
while calling the actual service looks like this
val requestFile = RequestBody.create(MediaType.parse("audio/mp3"), file.name)
val body = MultipartBody.Part.createFormData("file", file.name, requestFile)
service
.speechToText(getString(R.string.stt_iam_api_key), requestFile, body)
.enqueue(object: Callback<List<SpeechToText>> {
override fun onResponse(call: Call<List<SpeechToText>>, response: Response<List<SpeechToText>>) {
val listOfStts = response.body()
Log.d(TAG, "Response code: ${response.code()}")
if (listOfStts != null) {
for (stt in listOfStts) {
for (res in stt.results) {
Log.d(TAG, "Final value: ${res.final}")
for (alt in res.alternatives) {
Log.d(TAG, "Alternative confidence: ${alt.confidence}\nTranscript: ${alt.transcript}")
Toast.makeText(this#MainActivity, alt.transcript, Toast.LENGTH_SHORT).show()
}
}
}
}
}
override fun onFailure(call: Call<List<SpeechToText>>, t: Throwable) {
Log.d(TAG, "Error: ${t.message}")
t.printStackTrace()
}
})
Recordings are MP3 files, for which I am sure they are stored correctly and accessible. I have replaced audio/flac with audio/mp3 as well.
Issue seems to be in the way authentication works. Prior to the code I have shown above, I've used
private val retrofit = Retrofit.Builder()
.baseUrl(STT_BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(OkHttpClient.Builder()
.addInterceptor { chain ->
val request = chain.request()
val headers = request
.headers()
.newBuilder()
.add("Authorization", getString(R.string.stt_iam_api_key))
.build()
val finalRequest = request.newBuilder().headers(headers).build()
chain.proceed(finalRequest)
}
.build())
.build()
but the same response code 401 persisted. Of course, the interface method lacked the #Header parameter.
Any sort of help is much appreciated.
I am kind of saddened by the fact nobody was able to solve this one sooner, but here's the solution I came across by accident when working on a different project altogether.
As you can see from the curl command, authentication comes in the form of username: password pattern, in this case, username being apikey string and password is your API key.
So the way you should tackle this is by building your Retrofit instance this way:
fun init(token: String) {
//Set logging interceptor to BODY and redact Authorization header
interceptor.level = HttpLoggingInterceptor.Level.BODY
interceptor.redactHeader("Authorization")
//Build OkHttp client with logging and token interceptors
val okhttp = OkHttpClient().newBuilder()
.addInterceptor(interceptor)
.addInterceptor(TokenInterceptor(token))
.build()
//Set field naming policy for Gson
val gsonBuilder = GsonBuilder()
gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
//Build Retrofit instance
retrofit = Retrofit.Builder()
.baseUrl(IBM_BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gsonBuilder.create()))
.client(okhttp)
.build()
}
and create this custom interceptor
class TokenInterceptor constructor(private val token: String) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val original = chain.request()
val requestBuilder = original
.newBuilder()
.addHeader("Authorization", Credentials.basic("apikey", token))
.url(original.url)
return chain.proceed(requestBuilder.build())
}
}
You need to use Credentials.basic() in order to encode credentials.
I really hope somebody with a similar issue stumbles across this and saves themselves some time.
I'm trying to get posts from facebook page using Retrofit but I can't pass Access token and every time I get an error io.reactivex.exceptions.OnErrorNotImplementedException: HTTP 400 Bad Request
This is my code:
RetroAPI:
#GET("{page_id}/feed")
fun getPosts(#Path("page_id") pageId : String,
#Header("access_token") authToken : AccessToken)
: Observable<Posts>
Set Access Token:
AccessToken.setCurrentAccessToken(AccessToken("AppToken", "AppID","userID",null,null,null,null,null))
Get data:
var pagePosts : Observable<Posts> = facebook.getPosts("pageID", AccessToken.getCurrentAccessToken())
pagePosts.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe({result ->
var a : Posts = result
var b : List<Data> = result.data
Log.d("Posts A","${a.data[1].id}")
Log.d("Data B", "$b")
})
Set RetrofitAPI:
private val facebook : RetroAPI
init{
val retrofit = Retrofit.Builder()
.baseUrl("https://graph.facebook.com/")
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build()
facebook = retrofit.create(RetroAPI::class.java)
}
Normally access_token is sent as a query param to authenticate your request.
However you can also send a header in the form
Authorization: Bearer XXX
But AFAIK sending access_token as a header is unsupported.