Retrofit 2 Multipart requests - android

I'm migrating my existing codebase to Retrofit 2, but having some trouble understanding the new syntax for Multipart requests. I'm also using Kotlin, although apart from a few syntax changes I think it shouldn't matter for this particular question.
Here's what I have right now:
val audioDuration = RequestBody.create(null, audioDuration.toString())
val file = RequestBody.create(MediaType.parse("audio/mp4"),
File(context.filesDir, filename).absoluteFile)
sendAudioChunk(audioDuration, file).enqueue(callback)
And here's the definition of the API:
#Multipart
#POST("path_to_request")
fun sendAudioChunk(#Part("duration") audioDuration: RequestBody,
#Part("audio") audioBlob: RequestBody) : Call<ResponseObject>
On Retrofit 1.9 I used TypedString and TypedFile for the request parameters, and now it seems one need to use RequestBody from OkHttp but I must be missing something since the request does not execute correctly.

I eventually figured it out. My web-service expects a filename for file uploads. This seems to be a work in progress support in the new Retrofit 2, but it is possible to circumvent the problem by adding it to the named parameter definition.
More details here : https://github.com/square/retrofit/issues/1140

One thing that is different is that TypedString would have a Content-Type of "text/plain; charset=UTF-8", where you are not setting a Context-Type at all for your audioDuration parameter. Try setting it to text/plain to get the same behavior as TypedString (charset will be set to utf-8 by default).
val audioDuration = RequestBody.create(MediaType.parse("text/plain"), audioDuration.toString())
If that doesn't work, you'll need to provide more information about how the "request does not execute correctly". A working request that you are trying to replicate would also be helpful.

Related

Can't PUT from Android to DJANGO REST

I have a DJANGO REST server on my personal network that is set up to get and receive JSON strings to update "statuses" for a project I'm working on. I can GET the information down into my Android application just fine but when I try to PUT information up to the server, I get the error:
Bad Request: /api/users/1/
[28/Oct/2019 21:23:23] "PUT /api/users/1/ HTTP/1.1" 400 107
I think it's the app because I can successfully do a PUT request via Windows Powershell, but it doesn't work on the Android app. Here is the code using the OKHTTP class:
MediaType JSON = MediaType.parse("application/json; charset=utf-8");
String content = String.format("{'status':%s}", selectedStatus);
RequestBody body = RequestBody.create(JSON, content);
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(IPAddress + "api/users/" + currentUserID + "/")
.addHeader("Content-Type", "application/json")
.put(body) //PUT
.build();
client.newCall(request).execute();
I have tried using the standard HttpURLConnection class, which is what I did for the GET method, but I don't even get a response from the server when I do that. I've tried about a dozen ways to PUT something to the server, but nothing has worked. Any help achieving this goal would be appreciated. Thanks.
For anyone looking at this later, I found the issue. It turns out that in the Windows Powershell, you can use single quotes in the JSON string, but you NEED to use double quotes with the OKHTTP methods. Thus, I changed:
String content = String.format("{'status':%s}", selectedStatus);
to:
String content = String.format("{\"status\":%s}", selectedStatus);
and everything went through perfectly. Hope this helps someone.

Retrofit URL Problem using PUT-Request with colon in Path

I am confused using retrofit with a PUT-Request containing path elements with a colon (":") in the var. It should change the colon to "%3A" but it doesn't and i get a 400 Error Response from the backend.
#PUT("/api/2/elements/{elementId}/features/{featureId}/options")
Call<String> updateThingRFIDTag(
#Header("api-token") String token,
#Header("Authorization") String base_auth,
#Path("elementId") String elemnentId,
#Path("featureId") String featureId,
#Body String optionTag
);
The Request URL looks like: https://pageurl.com/api/2/elements/com.element.d3:f4345-43234-5654d-33/features/com.featurelistings.powersign:1.0.0/options
When I use Postman the it works perfectly and the Request URL after hinting send looks the same except the colons(':') changed to '%3A'...
I already tried to use encode boolean = true in the path argument - doesn't help.
I already tried to change the base url an the path attr. before the request to '%3A'. But then retrofit encodes the '%3A' to soemthing else and I still get an error response. Can soemone help? I already work on this the last 3 days... Besides an http Interceptor doesn't help at all.
Thanks in advance!

Send large data OkHttp3 outofmemory

I want to send a PDF image to the server in Android.
The server spec needs I should use Base64 encoding.
So I should convert the PDF Image file to Base64 String.
Below is the HTTP POST request:
<?xml version="1.0" encoding="UTF-8"?>
<request>
<auth>
<!-- auth info -->
</auth>
<imagefile>
<filename>Test Attachment</filename>
<type>pdf</type>
<data>"HERE IS BASE64 String"</data>
</imagefile>
</request>
And I used below code, it is just Http post request code.
fun post(url: String, xml: String) = Observable.defer {
val request = Request.Builder()
.url(url)
.post(RequestBody.create(contentType, xml))
.build()
okHttpClient.newCall(request).execute()
}
In case of the small pdf file, it's ok. (Works fine)
But when I used the large pdf file(over 20MB), out of memory occurs.
So I tried another way like below.
fun post(url: String, xmlFile: File) = Observable.defer {
val request = Request.Builder()
.url(url)
.post(RequestBody.create(contentType, xmlFile))
.build()
okHttpClient.newCall(request).execute()
}
The difference is that I don't use String, but File.
I created a file using the Base64 encoded text with XML syntax.
But it is failed too.
How can I send the large data in Android without OOM?
You're going to need to find a streaming base64 encoder and use that to write a RequestBody that streams to the server. There is no such encoder built-in to OkHttp or Okio but you can use the one in Android itself. Use wrap on the OutputStream that you'll get in the writeTo method of your custom RequestBody.
https://developer.android.com/reference/java/util/Base64.Encoder

Retrofit 1, sending multipart form data with custom key for value

I am using an API that is out of my control, as well having joined a development team recently that is using Retrofit1.
I am unable to send the request to the server in the format required, as the server requires multipart form data Body in the following format:
Uniqueidentifier:FileName.jpg:ReroutServerIP:Base64EncodedDocString.
I have tried many different techniques in order to accomplish this task but I cannot find any working method to do this. The server tells me that the message format not supported. Here is my current code (with the url stripped out). Please could someone assist?
#POST("URL")
public Response post_SendData(#Header("Content-Type") String ContentType, #Body String body);
In order to achieve the desired result, I can use postman with no headers and post a file from my system using the form-data post method. In the working postman post, the Key is the formatted string mentioned above and the value is a file selected from my desktop. Please see below for postman (edited to remove urls).
Postman
Thanks a lot guys.
If you want to send a file to the server :
public void uploadPictureRetrofit(File file, Callback<YourObject> response) {
// this will build full path of API url where we want to send data.
RestAdapter restAdapter = new RestAdapter
.Builder()
.setEndpoint(YOUR_BASE_URL)
.setConverter(new SimpleXMLConverter()) // if the response is an xml
.setLogLevel(RestAdapter.LogLevel.FULL)
.build();
// SubmitAPI is name of our interface which will send data to server.
SendMediaApiInterface api = restAdapter.
create(SendMediaApiInterface.class);
TypedFile typedFile = new TypedFile(MULTIPART_FORM_DATA, file);
api.sendImage(typedFile, response);
}
And this is the interface SendMediaApiInterface :
public interface SendMediaApiInterface {
#Multipart
#POST("url")
void sendImage(#Part("here_is_the_attribute_name_in_webservice") TypedFile attachments, Callback<YourObject> response);}

Is OkHttp equivalent code really OK in Postman?

I'm trying to send a json post request to some API which in response sends a binary file back.
I'm doing well in Postman:
Header:
Body and result:
And I get the following code from Code section in Postman for Java/OKHTTP
OkHttpClient client = new OkHttpClient();
MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "{\r\n \"Text\":\"Hello\",\r\n \"APIKey\":\"MY_API_KEY\",\r\n \"Speaker\":\"Female1\",\r\n \"Format\":\"mp3/32/m\",\r\n \"Quality\":\"quality/normal\"\r\n}");
Request request = new Request.Builder()
.url("http://url/CloudService/ReadText")
.post(body)
.addHeader("content-type", "application/json")
.addHeader("cache-control", "no-cache")
.addHeader("postman-token", "0a1ce7c9-7a95-a2b9-7cde-8a7e6ce58386")
.build();
Response response = client.newCall(request).execute();
But when I use the above code in android it fails, I'm sure that I got Internet permission and the code is executed within an AsyncTask.
I'm not asking about the API or how to send json Post request to some API and get a binary file in response. I've used client.newCall(request).enqueue(new Callback(){//stuff here}); but none works. In response I got a 307 status code (instead of 200 in Postman) and no binary data at all. The API is very unclear and said nothing about the failure and I'm still working on that.
All I'm asking is that does Postman generates equivalent code for OkHttp correctly? and if not what is your suggestion for equivalent of this request in Java/OkHttp?
Just to provide another example, the following is also a working Python requests script to do the same job:
url = 'http://url/CloudService/ReadText'
api_key = 'MY_API_KEY'
body = {
'Text': 'Hello',
'Speaker': 'Female1',
'Format': 'mp3/32/m',
'Quality': 'quality/normal',
'APIKey': api_key
}
header = {
'Content-type': 'application/json'
}
r = requests.post(url, data=json.dumps(body), headers=header)
So after 3 days I found the problem, the server API did not mention that the URL endpoint must be ended with an / and even in their sample code they didn't use one.
It seems that both Postman and Python requests use an / at the end of URL in case of need, but Postman at least does not mention that in the generated equivalent code. Also OkHttp does not operate in the same manner.
However using a trailing / solved the problem.

Categories

Resources