I'm trying to send a .wav file using Retrofit2 on Android in order to use the Create Enrollment request of the Microsoft Speaker Recognition API (https://dev.projectoxford.ai/docs/services/563309b6778daf02acc0a508/operations/56406930e597ed20c8d8549c)
But I always get the following Error 400:
com.mobile.cir.voicerecognition D/OkHttp: <-- 400 Bad Request https://api.projectoxford.ai/spid/v1.0/verificationProfiles/94BC205B-FACD-42A7-9D80-485403106627/enroll (3907ms)
com.mobile.cir.voicerecognition D/OkHttp: Cache-Control: no-cache
com.mobile.cir.voicerecognition D/OkHttp: Pragma: no-cache
com.mobile.cir.voicerecognition D/OkHttp: Content-Length: 123
com.mobile.cir.voicerecognition D/OkHttp: Content-Type: application/json; charset=utf-8
com.mobile.cir.voicerecognition D/OkHttp: Expires: -1
com.mobile.cir.voicerecognition D/OkHttp: X-AspNet-Version: 4.0.30319
com.mobile.cir.voicerecognition D/OkHttp: X-Powered-By: ASP.NET
com.mobile.cir.voicerecognition D/OkHttp: apim-request-id: e5472946-ec90-4662-a3c9-dda62c2c6b27
com.mobile.cir.voicerecognition D/OkHttp: Date: Fri, 12 Aug 2016 11:43:04 GMT
com.mobile.cir.voicerecognition D/OkHttp: }
com.mobile.cir.voicerecognition D/OkHttp: <-- END HTTP (123-byte body)
com.mobile.cir.voicerecognition D/EnableVoiceRecognition: Upload success
com.mobile.cir.voicerecognition D/errorĀ message: RequestError{code='null', message='null'}
Here is my Apiclient class:
public static Retrofit getClient() {
if (retrofit==null) {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient httpClient = new OkHttpClient.Builder().addInterceptor(logging).build();
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(httpClient)
.build();
}
return retrofit;
}
The POST request:
#Multipart
#POST("verificationProfiles/{VerificationProfileId}/enroll")
Call<EnrollmentResponse> createEnrollment(#Path("VerificationProfileId") String profileId,
#Header("Content-Type") String contentType,
#Header("Ocp-Apim-Subscription-Key") String subscriptionKey,
#Part("file") RequestBody audioFile);
}
And the action itself:
File audioFile = new File(fileDirectory + "my_voice.wav");
RequestBody requestAudioFile = RequestBody.create(MediaType.parse("application/octet-stream"), audioFile);
Call<EnrollmentResponse> call = apiService.createEnrollment(PROFILE_ID_TEST,"audio/wav; samplerate=1600",API_KEY,requestAudioFile);
call.enqueue(new Callback<EnrollmentResponse>() {
#Override
public void onResponse(Call<EnrollmentResponse> call, Response<EnrollmentResponse> response) {
Log.d(TAG,"Upload success");
RequestError error = ErrorUtils.parseError(response);
Log.d("error message", error.toString());
Log.d(TAG,"Response: " + response.body().toString());
}
#Override
public void onFailure(Call<EnrollmentResponse> call, Throwable t) {
Log.d(TAG,"Upload error: " + t.getMessage());
}
});
Where am I doing wrong?
* EDIT *
Microsoft developers released the Android library for this API
(https://github.com/Microsoft/Cognitive-SpeakerRecognition-Android)
Related
I have a problem with retrofit 2. I'm sending some data to Microsoft EAS server but a response is received after 20 sec. Why I know that this is a retrofit problem? Because without retrofit it is working correctly.
my logs:
D/OkHttp: --> POST https://{my_host}/Microsoft-Server-ActiveSync?Cmd=Provision&User={myuser}&DeviceId=837bc6c5690b40b98ab55f7a2231e50c&DeviceType={mydevicetype} http/1.1
D/OkHttp: Content-Type: application/vnd.ms-sync.wbxml
D/OkHttp: Content-Length: 456
D/OkHttp: Authorization: Basic XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
D/OkHttp: MS-ASProtocolVersion: 14.1
D/OkHttp: Connection: keep-alive
D/OkHttp: User-Agent: MyUserAgent
D/OkHttp: X-MS-PolicyKey: 0
D/OkHttp: [3,1,106,0,0,14,[...more here...],88,77,76,0,1,1,1,1] // <--- body
D/OkHttp: --> END POST (456-byte body)
after 20 sec (it seems like I receive response after 20 + normal receive time - in this case: 20596ms):
D/OkHttp: <-- 200 OK https://{my_host}/Microsoft-Server-ActiveSync?Cmd=Provision&User={myuser}&DeviceId=837bc6c5690b40b98ab55f7a2231e50c&DeviceType={mydevicetype} (20596ms)
D/OkHttp: Content-Type: application/vnd.ms-sync.wbxml
D/OkHttp: request-id: {request_id}
D/OkHttp: Set-Cookie: {cookie_data}
D/OkHttp: X-CalculatedBETarget: {target}.local
D/OkHttp: X-MS-BackOffDuration: L/-470
D/OkHttp: X-DiagInfo: EXCHANGEHA
D/OkHttp: X-BEServer: EXCHANGEHA
D/OkHttp: Set-Cookie: {cookie_data}
D/OkHttp: X-FEServer: EX01
D/OkHttp: Date: Fri, 07 Oct 2016 10:30:08 GMT
D/OkHttp: <-- END HTTP (binary 15-byte body omitted)
If I'm requesting it without retrofit, it sends me response immiedetaly.
My code:
Init:
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient.addInterceptor(chain -> {
Request original = chain.request();
Request.Builder requestBuilder = original.newBuilder()
.addHeader("Authorization", getAuth(account))
.addHeader("MS-ASProtocolVersion", getProtocolVersion())
.addHeader("Connection", getConnectionType())
.addHeader("User-Agent", getUserAgent())
.addHeader("X-MS-PolicyKey", getPolicyKey());
Request request = requestBuilder.build();
return chain.proceed(request);
});
if (EasConfig.isDebug()) {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
httpClient.addInterceptor(logging);
}
httpClient.readTimeout(30, TimeUnit.SECONDS);
httpClient.writeTimeout(30, TimeUnit.SECONDS);
OkHttpClient client = httpClient.build();
retrofit = new Retrofit.Builder()
.baseUrl(getBaseUrl(account))
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
Call:
ActiveSyncService service = retrofit.create(ActiveSyncService.class);
service.getData(getContentType(), command, getUser(account), getDeviceId(), getUserAgent(), serializer.toByteArray());
Endpoint:
#POST("Microsoft-Server-ActiveSync")
Call<ResponseBody> getData(#Header("Content-Type") String contentType, // <--- it has to be here -- it's not woking if I set it in incerteptor (don't know why)
#Query("Cmd") String command,
#Query("User") String user,
#Query("DeviceId") String deviceId,
#Query("DeviceType") String deviceType,
#Body byte[] data);
My POST request keeps sending as GET & got rejected by API endpoint
MyService class
#FormUrlEncoded
#POST("api/users/")
Call<List<User>> getUsers(#FieldMap HashMap<String, String> parameters);
Request code
Gson builder = new GsonBuilder().setLenient().create();
Retrofit client = new Retrofit.Builder()
.baseUrl(Constants.API_ENDPOINT_TEST_URL)
.addConverterFactory(GsonConverterFactory.create(builder))
.build();
mApiService = client.create(MyService.class);
Call<List<User>> call = mApiService.getUsers(mParameters);
call.enqueue(new Callback<List<User>>() {
#Override
public void onResponse(Call<List<User>> call, Response<List<User>> response) {
mResponse = response.body();
mResponseObserver.onFinish(mResponse);
}
#Override
public void onFailure(Call<List<User>> call, Throwable t) {
mResponseObserver.onFailure();
}
});
But server rejects it as it reached the server in GET request form !? Having checked with debugger, I saw that:
rawResponse.request.method = GET
Here is screenshot of watch window showing the retrofit's request object:
As you can see, the request method was GET. But the weird part is in the tag, it shows a request object with POST method?
Am i miss something here?
UPDATE
I added logging interceptor & here is the log:
D/OkHttp: --> POST http://***/api/users/ http/1.1
D/OkHttp: Content-Type: application/x-www-form-urlencoded
D/OkHttp: Content-Length: 56
D/OkHttp: --> END POST
D/OkHttp: <-- 200 OK https://***/api/users/ (388ms)
D/OkHttp: Date: Thu, 01 Sep 2016 11:50:23 GMT
D/OkHttp: Server: Apache
D/OkHttp: X-Powered-By: PHP/5.4.34
D/OkHttp: Cache-Control: max-age=2592000
D/OkHttp: Expires: Sat, 01 Oct 2016 11:50:23 GMT
D/OkHttp: Vary: Accept-Encoding
D/OkHttp: Content-Type: application/json; charset=UTF-8
D/OkHttp: Set-Cookie: SESSION_DEFAULT=***; expires=Sun, 04-Sep-2016 11:50:24 GMT; path=/; HttpOnly
D/OkHttp: Set-Cookie: COOKIE[***]=***; path=/; httponly
D/OkHttp: Connection: close
D/OkHttp: <-- END HTTP
looks like the request is a POST. But, the server still responds with error message, saying that the request method is a GET
Hmm, I'll dig a little bit more into it.
Actually, the issue is the combination of 2 factors:
Wrong request protocol has been made (http instead of https)
Server responded with a weird message on wrong protocol: "GET is not supported".
ANyway, thanks #nshmura for your assistant.
I checked your program with logging, and confirms the POST request is sent.
I recommend you to check like this:
class TestClass {
private void testRequest() {
HashMap<String, String> mParameters = new HashMap<>();
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.HEADERS);
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient.addInterceptor(logging); // <-- this is the important line!
Gson builder = new GsonBuilder().setLenient().create();
Retrofit client = new Retrofit.Builder()
.baseUrl(Constants.API_ENDPOINT_TEST_URL)
.addConverterFactory(GsonConverterFactory.create(builder))
.client(httpClient.build())
.build();
MyService mApiService = client.create(MyService.class);
Call<List<User>> call = mApiService.getUsers(mParameters);
call.enqueue(new Callback<List<User>>() {
#Override
public void onResponse(Call<List<User>> call, Response<List<User>> response) {
}
#Override
public void onFailure(Call<List<User>> call, Throwable t) {
}
});
}
interface MyService {
#FormUrlEncoded
#POST("api/users/")
Call<List<User>> getUsers(#FieldMap HashMap<String, String> parameters);
}
class User {
}
class Constants {
public static final String API_ENDPOINT_TEST_URL = "http://.........";
}
}
here is request log:
D/OkHttp: --> POST http://......... http/1.1
D/OkHttp: Content-Type: application/x-www-form-urlencoded
D/OkHttp: Content-Length: 0
D/OkHttp: --> END POST
Here is response:
D/OkHttp: <-- 200 OK http://.............../ (167ms)
D/OkHttp: Server: nginx
D/OkHttp: Date: Thu, 01 Sep 2016 11:30:32 GMT
D/OkHttp: Content-Type: text/html; charset=UTF-8
D/OkHttp: Connection: close
....
D/OkHttp: <-- END HTTP
I met a similar problem. In my case, I received in response to 301, redirect and change of method POST -> GET.
First, check your BASE_URL, it must be of the form "h ttps://www.YourSite.com/" www - very important. If everything is in place and the problem still exists, then you can change as described below:
From okhttp wiki and based on this image, you need to change something like this:
...
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
final RequestBody requestBody = new FormBody.Builder().build();
httpClient.addNetworkInterceptor(new Interceptor() {
#Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
HttpUrl.Builder builder = request.url().newBuilder();
HttpUrl url = builder.build();
request = request.newBuilder()
.url(url)
.post(requestBody)
.build();
return chain.proceed(request);
}
})
httpClient.addInterceptor(logging);
...
Can u try
#POST("/api/users/")
With slash before "api"?
Also it would be helpful to double check request with Fiddler or smth.
That is partly correct a http client behavior. According to the HTTP spec
If the 307 status code is received in response to a request other than GET or HEAD, the user agent MUST NOT automatically redirect the request unless it can be confirmed by the user, since this might change the conditions under which the request was issued.
Sometime it is not possible to change backend API. So here a workaround you can add to you interceptor to manually redirect a request with correct method.
fun forceRedirectMethod(chain: Interceptor.Chain, originResponse: Response): Response {
/* TODO
one more request is made; disable followRedirect and make it manually?
*/
if (originResponse.priorResponse()?.code() == 302) {
val priorResponse: Response = originResponse.priorResponse() as Response
val redirectRequest = priorResponse.request()
val builder = originResponse.request().newBuilder()
.method(redirectRequest.method(), redirectRequest.body())
val newRequest = builder.build()
return chain.proceed(newRequest)
} else {
return originResponse
}
}
Update There seems to be an issue with the way dates are interpreted by retrofit and spring. Request works when date is set to null.
I am trying to send a POST request from android using retrofit2 to my backend server running on Spring. I get a 400 Bad Request code and can't find the reason for it. I tried the spring controller with swagger UI and it worked ok, so I'm guessing the android side is the issue.
Android side:
//this is inside Async task, there's 1 more retrofit call above
UserTemplate template;
// fill template
UserService userService = (UserService) RetrofitBuilder.getService(UserService.class,
RetrofitBuilder.BASE_URL);
Call<User> userCall = userService.register(template);
Response<User> registeredUser;
try {
registeredUser = userCall.execute();
} catch (IOException e) {
e.printStackTrace();
return null;
}
Log.d("call", "call status " + registeredUser.code());
return registeredUser.body();
}
//---------------------------------------------------------
public class RetrofitBuilder {
public static final String BASE_URL = "http://my-backend-url.com/";
public static final String IMGUR_URL = "https://api.imgur.com/";
/**
* Added logging
*/
public static Retrofit build(String url) {
GsonBuilder builder = new GsonBuilder();
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(interceptor).build();
builder.registerTypeAdapter(Date.class, new JsonDeserializer<Date>() {
public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
return new Date(json.getAsJsonPrimitive().getAsLong());
}
});
Gson gson = builder.create();
return new Retrofit.Builder()
.client(client)
.baseUrl(useLocalhost ? LOCALHOST : url)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
}
public static Object getService(Class<?> clas, String url) {
return build(url).create(clas);
}
}
//----------------------------------------------------
public interface UserService {
#POST("/register/")
Call<User> register(#Body UserTemplate userTemplate);
}
And spring side:
#RestController
public class RegisterController {
#Autowired
UserService userService;
#RequestMapping(value = "register", method = RequestMethod.POST)
public ResponseEntity<?> register(#RequestBody UserTemplate userTemplate) {
User user = UserMapper.map(userTemplate);
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
user.setPassword(passwordEncoder.encode(user.getPassword()));
User u = userService.register(user);
return ResponseEntity.ok().body(u);
}
UserTemplate
public class UserTemplate {
String firstName;
String lastName;
String email;
String username;
String password;
float targetDistance;
String notifications;
Date dateOfBirth;
String description;
String profilePicture;
//getters, setters, toString
}
OkHttp log
D/OkHttp: --> POST http://mafia-test-env-java.eu-central-1.elasticbeanstalk.com/register http/1.1
D/OkHttp: Content-Type: application/json
D/OkHttp: Content-Length: 168
D/OkHttp: {"dateOfBirth":"Aug 21, 2016 12:00:00 AM","email":"a#a.aa","firstName":"A","lastName":"A","password":"a","profilePicture":"IyYD8uY","username":"a","targetDistance":0.0}
D/OkHttp: --> END POST (168-byte body)
D/OkHttp: <-- 400 Bad Request http://mafia-test-env-java.eu-central-1.elasticbeanstalk.com/register (192ms)
D/OkHttp: Server: nginx/1.8.1
D/OkHttp: Date: Sun, 21 Aug 2016 20:28:02 GMT
D/OkHttp: Content-Type: application/json;charset=UTF-8
D/OkHttp: Transfer-Encoding: chunked
D/OkHttp: Connection: keep-alive
D/OkHttp: Access-Control-Allow-Origin: *
D/OkHttp: Access-Control-Allow-Methods: POST, GET, PUT, OPTIONS, DELETE
D/OkHttp: Access-Control-Max-Age: 3600
D/OkHttp: Access-Control-Allow-Headers: Origin, X-Requested-With, Content- Type, Accept, ${cerberus.token.header}
D/OkHttp: X-Content-Type-Options: nosniff
D/OkHttp: X-XSS-Protection: 1; mode=block
D/OkHttp: Cache-Control: no-cache, no-store, max-age=0, must-revalidate
D/OkHttp: Pragma: no-cache
D/OkHttp: Expires: 0
D/OkHttp: X-Frame-Options: DENY
D/OkHttp: <-- END HTTP (binary 1408-byte body omitted)
D/call: call status =400, message =Bad Request
Please enlighten me.
The Web server (running the Web site) thinks that the data stream sent by the client (e.g. your Web browser or our CheckUpDown robot) was 'malformed' i.e. did not respect the HTTP protocol completely. So the Web server was unable to understand the request and process it.
I guess you pass not completely data to server.
I'm sure this is a non issue for someone who knows what they're doing, but this is my first time using MailChimp's api and retrofit. Basically, my api key is always missing. I have no idea what MailChimp's proper url looks like when you are including an api key.
The url I am attempting is:
/3.0/lists/613cd953b2/members?user=email#gmail.com:apikey
And others of that variety
http://developer.mailchimp.com/documentation/mailchimp/reference/lists/members/#create-post_lists_list_id_members
curl --request GET \
--url 'https://.api.mailchimp.com/3.0/' \
--user 'anystring:your_apikey'
This says GET but I am attempting a post.
Here is my android / java code...
final OkHttpClient client = new OkHttpClient();
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
client.interceptors().add(interceptor);
client.interceptors().add(new Interceptor() {
#Override
public com.squareup.okhttp.Response intercept(Chain chain) throws IOException {
Request original = chain.request();
Request.Builder requestBuilder = original.newBuilder()
// .header("Authorization", basic)
.header("user", "email#gmail.com:apikey");
Request request = requestBuilder.build();
return chain.proceed(request);
}
});
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build();
//Create interface instance
MailChimpAPI apiService = retrofit.create(MailChimpAPI.class);
Email chimpEmail = new Email("hi#hi.hi");
Call<Email> call = apiService.addUser(chimpEmail);
call.enqueue(new Callback<Email>() {
#Override
public void onResponse(Response<Email> response, Retrofit retrofit) {
int statusCode = response.code();
//Log.d("tag", "enter the void");
Log.d("tag", statusCode + "");
Log.d("tag", client.toString());
}
#Override
public void onFailure(Throwable t) {
Log.d("tag", t.toString());
}
});
And how I define my interface method
#POST("/3.0/lists/613cd953b2/members")
Call<Email> addUser(#Body Email email);
And my retrofit log...
D/OkHttp: --> POST /3.0/lists/613cd953b2/members HTTP/1.1
D/OkHttp: {"email_address":"hi#hi.hi","merge_fields":{"FNAME":"","LNAME":""},"status":""}
D/OkHttp: --> END POST (79-byte body)
D/OkHttp: <-- HTTP/1.1 401 (299ms)
D/OkHttp: OkHttp-Selected-Protocol: h2
D/OkHttp: server: nginx
D/OkHttp: content-type: application/problem+json; charset=utf-8
D/OkHttp: x-request-id: 6b6ca6a9-c141-45fc-8de7-ec0922892bbf
D/OkHttp: link: <https://us12.api.mailchimp.com/schema/3.0/ProblemDetailDocument.json>; rel="describedBy"
D/OkHttp: vary: Accept-Encoding
D/OkHttp: date: Tue, 05 Jan 2016 21:21:45 GMT
D/OkHttp: set-cookie: _AVESTA_ENVIRONMENT=prod; path=/
D/OkHttp: OkHttp-Sent-Millis: 1452028904738
D/OkHttp: OkHttp-Received-Millis: 1452028904830
D/OkHttp: {"type":"http://developer.mailchimp.com/documentation/mailchimp/guides/error-glossary/","title":"API Key Missing","status":401,"detail":"Your request did not include an API key.","instance":""}
D/OkHttp: <-- END HTTP (193-byte body)
I was not using basic authentication properly. I followed this tutorial
https://futurestud.io/blog/android-basic-authentication-with-retrofit
and it fixed my error! Yay for new errors!
Also for future people, maybe this might help you.
BASIC AUTHORIZATION isn't just a way to describe something! It's an actual concept and implementable! haha
I am using Square Retrofit version 2.0 beta2. I have tried to follow this tutorial .I am trying to upload a bitmap image to the server but somehow code is not working. I have tried testing my server using postman and I am able to post photo and even able to retrieve it. Here is my flask controller.
#app.route('/api/photo/user/<int:user_id>', methods=["POST"])
def post_user_photo(user_id):
app.logger.info("post_user_photo=> user_id:{}, photo: {}".format(
user_id,
request.files['photo'].filename,
))
user = User.query.get_or_404(user_id)
try:
user.photo = request.files['photo'].read()
except Exception as e:
app.logger.exception(e)
db.session.rollback()
raise
db.session.commit()
return "", codes.no_content
I have used the postman to test my controller and here is the request generated by postman.
POST /api/photo/user/5 HTTP/1.1
Host: blooming-cliffs-9672.herokuapp.com
Cache-Control: no-cache
Postman-Token: 8117fb79-4781-449d-7d22-237c49b53389
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
----WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="photo"; filename="sfsu.jpg"
Content-Type: image/jpeg
----WebKitFormBoundary7MA4YWxkTrZu0gW
I have defined the retrofit service and the to upload the image and here is my Android code. Interface part
#Multipart
#POST("/api/photo/user/{userId}")
Call<Void> uploadUserProfilePhoto(#Path("userId") Integer userId, #Part("photo") RequestBody photo);
Here client builder part
public static BeamItService getService(){
if (service == null) {
OkHttpClient client = new OkHttpClient();
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
HttpLoggingInterceptor interceptor2 = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS);
client.interceptors().add(interceptor);
client.interceptors().add(interceptor2);
service = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build().create(BeamItService.class);
}
return service;
}
And here is the Android activity code which tries to upload the bitmap.
private void uploadProfilePhoto(){
BeamItService service = BeamItServiceTransport.getService();
MediaType MEDIA_TYPE_PNG = MediaType.parse("image/jpeg");
byte [] data = BitmapUtility.getBitmapToBytes(((BitmapDrawable) ivProfilePhoto.getDrawable()).getBitmap());
Log.d(TAG, String.format("Profile detals => user_id: %d, size of data: %d", 5, data.length));
RequestBody requestBody1 = RequestBody.create(MEDIA_TYPE_PNG,
data);
Log.d(TAG, "requestBody: " + requestBody1.toString());
RequestBody requestBody2 = new MultipartBuilder()
.type(MultipartBuilder.FORM)
.addPart(Headers.of("Content-Disposition", "form-data; name=\"photo\"; filename=\"t.jpg\""),
requestBody1)
.build();
Log.d(TAG, "requestBody: " + requestBody2.toString());
// ProfileDetails profileDetails = new DBHelper(this).fetchProfileDetails();
Call<Void> call = service.uploadUserProfilePhoto(5, requestBody2);
call.enqueue(new ProfilePhotoUploadCallback());
}
private class ProfilePhotoUploadCallback implements Callback<Void> {
#Override
public void onResponse(Response<Void> response, Retrofit retrofit) {
Log.d(TAG, String.format("ProfilePhotoUploadCallback=> code: %d", response.code()));
}
#Override
public void onFailure(Throwable t) {
}
}
But it fails to upload it, flask app returns status code 400 every time. I tried to put the breakpoint in the flask app, but request doesn't even reach there.
here is the server log
2015-11-02T06:05:42.288574+00:00 heroku[router]: at=info method=POST path="/api/photo/user/5" host=blooming-cliffs-9672.herokuapp.com request_id=2cc8b6c8-f12a-4e4b-8279-cedfc39712f2 fwd="204.28.113.240" dyno=web.1 connect=1ms service=88ms status=400 bytes=347
2015-11-02T06:05:42.209347+00:00 app[web.1]: [2015-11-02 06:05:42 +0000] [11] [DEBUG] POST /api/photo/user/5
I also tried to enable the retrofit intercepter and log the request and response, but I don't get the entire POST request body. Here is the Android log.
11-02 00:24:22.119 3904-4382/com.contactsharing.beamit D/OkHttp: --> POST /api/photo/user/5 HTTP/1.1
11-02 00:24:22.119 3904-4382/com.contactsharing.beamit D/OkHttp: Content-Type: multipart/form-data; boundary=4031e177-0e4b-4f16-abe8-20c54e506846
11-02 00:24:22.120 3904-4382/com.contactsharing.beamit D/OkHttp: Content-Length: 17171
11-02 00:24:22.120 3904-4382/com.contactsharing.beamit D/OkHttp: Host: blooming-cliffs-9672.herokuapp.com
11-02 00:24:22.120 3904-4382/com.contactsharing.beamit D/OkHttp: Connection: Keep-Alive
11-02 00:24:22.120 3904-4382/com.contactsharing.beamit D/OkHttp: Accept-Encoding: gzip
11-02 00:24:22.120 3904-4382/com.contactsharing.beamit D/OkHttp: User-Agent: okhttp/2.6.0-SNAPSHOT
11-02 00:24:22.120 3904-4382/com.contactsharing.beamit D/OkHttp: --> END POST
11-02 00:24:22.179 3904-4537/com.contactsharing.beamit I/DBHelper: Updated row: 1
11-02 00:24:22.316 3904-4382/com.contactsharing.beamit D/OkHttp: <-- HTTP/1.1 400 BAD REQUEST (195ms)
11-02 00:24:22.316 3904-4382/com.contactsharing.beamit D/OkHttp: Connection: keep-alive
11-02 00:24:22.316 3904-4382/com.contactsharing.beamit D/OkHttp: Server: gunicorn/19.3.0
11-02 00:24:22.316 3904-4382/com.contactsharing.beamit D/OkHttp: Date: Mon, 02 Nov 2015 08:24:22 GMT
11-02 00:24:22.316 3904-4382/com.contactsharing.beamit D/OkHttp: Content-Type: text/html
11-02 00:24:22.316 3904-4382/com.contactsharing.beamit D/OkHttp: Content-Length: 192
11-02 00:24:22.316 3904-4382/com.contactsharing.beamit D/OkHttp: Via: 1.1 vegur
11-02 00:24:22.316 3904-4382/com.contactsharing.beamit D/OkHttp: OkHttp-Selected-Protocol: http/1.1
11-02 00:24:22.316 3904-4382/com.contactsharing.beamit D/OkHttp: OkHttp-Sent-Millis: 1446452662120
11-02 00:24:22.316 3904-4382/com.contactsharing.beamit D/OkHttp: OkHttp-Received-Millis: 1446452662316
11-02 00:24:22.316 3904-4382/com.contactsharing.beamit D/OkHttp: <-- END HTTP
Please help, I am stuck and not able to make any progress.
You are nesting a multipart request body here (A multipart within a multipart).
Implemented something similar recently, instead of using #Multipart and #Part you can use #Body with MultipartBuilder.
#POST("/api/photo/user/{userId}")
Call<Void> uploadUserProfilePhoto(#Path("userId") Integer userId, #Body RequestBody photo);
Then instead of using MultipartBuilder.addPart(...) use MultipartBuilder.addFormDataPart(name, filename, requestBody)
private void uploadProfilePhoto() {
BeamItService service = BeamItServiceTransport.getService();
MediaType MEDIA_TYPE_PNG = MediaType.parse("image/jpeg");
byte [] data = BitmapUtility.getBitmapToBytes(((BitmapDrawable) ivProfilePhoto.getDrawable()).getBitmap());
Log.d(TAG, String.format("Profile detals => user_id: %d, size of data: %d", 5, data.length));
RequestBody requestBody1 = RequestBody.create(MEDIA_TYPE_PNG, data);
Log.d(TAG, "requestBody: " + requestBody1.toString());
RequestBody requestBody2 = new MultipartBuilder()
.type(MultipartBuilder.FORM)
.addFormDataPart("photo", "t.jpg", requestBody1)
.build();
Log.d(TAG, "requestBody: " + requestBody2.toString());
// ProfileDetails profileDetails = new DBHelper(this).fetchProfileDetails();
Call<Void> call = service.uploadUserProfilePhoto(5, requestBody2);
call.enqueue(new ProfilePhotoUploadCallback());
}