Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Retrofit 2.0-beta-2 is adding literal quotes to MultiPart values

Went to upgrade to Retrofit 2.0 and running into this weird problem.

I have a method to log a user in

public interface ApiInterface {      @Multipart     @POST("user/login/")     Call<SessionToken> userLogin(@Part("username") String username, @Part("password") String password); } 

When I look at the key value POST params on the server side they print like this

username : "brian" password : "password" 

The same method using retrofit 1.9 the K:V pairs look like

username : brian password : password 

It's adding literal quotes to the POST variables

If I use any other rest client the variables print like the second way without the quotes.

Here is how I build the Retrofit instance with an interceptor

 OkHttpClient client = new OkHttpClient();     client.interceptors().add(new Interceptor() {         @Override         public Response intercept(Chain chain) throws IOException {             Request original = chain.request();              // Customize the request             Request request = original.newBuilder()                     .header("Accept", "application/json")                     .header("Authorization", myPrefs.accessToken().getOr(""))                     .method(original.method(), original.body())                     .build();              Response response = chain.proceed(request);              // Customize or return the response             return response;         }     });      Ok2Curl.set(client);      Retrofit retrofit = new Retrofit.Builder()             .baseUrl(apiEndpoint)             .addConverterFactory(GsonConverterFactory.create())             .client(client)             .build(); 

I imagine i'm doing something wrong with the converter but not sure what.

Has anyone else ran into this problem yet? I know its in beta but it's pretty widly used.

like image 461
Brian Avatar asked Oct 19 '15 02:10

Brian


People also ask

What is@ multipart in Retrofit?

A multipart message is made up of several parts. One part consists of a header and a body. The body can be any type of media and can contain text or binary data. A multipart media type can be included in a part. Retrofit supports requests that are in parts.

How do you write a double quote in a string?

A double-quoted string can have single quotes without escaping them, conversely, a single-quoted string can have double quotes within it without having to escape them. Double quotes ( \" ) must escape a double quote and vice versa single quotes ( \' ) must escape a single quote.


2 Answers

I have the same problem, and how it solved:

1) Add to build.gradle:

compile 'com.squareup.retrofit2:converter-scalars:2.1.0' // Remember to add the same version 

2) Add one line here:

Retrofit retrofit = new Retrofit.Builder()                 .baseUrl(URL_BASE)                 .addConverterFactory(ScalarsConverterFactory.create()) // this line                 .addConverterFactory(GsonConverterFactory.create(gson))                 .client(getUnsafeOkHttpClient())                 .build(); 
like image 45
nicolas asinovich Avatar answered Oct 06 '22 10:10

nicolas asinovich


This is because it's running through the JSON converter.

Solution1: use RequestBody instead of String

public interface ApiInterface {     @Multipart     @POST("user/login/")     Call<SessionToken> userLogin(@Part("username") RequestBody username, @Part("password") RequestBody password); } 

Build RequestBody:

RequestBody usernameBody = RequestBody.create(MediaType.parse("text/plain"), usernameStr); RequestBody passwordBody = RequestBody.create(MediaType.parse("text/plain"), passwordStr); 

Launch network operation:

 retrofit.create(ApiInterface.class).userLogin(usernameBody , passwordBody).enqueue().... 

Solution2: Create a custom ConverterFactory to dispose String part value.

For: Retrofit2 final release not beta. (com.squareup.retrofit2:retrofit:2.0.0)

Create your StringConverterFactory:

public class StringConverterFactory extends Converter.Factory { private static final MediaType MEDIA_TYPE = MediaType.parse("text/plain");  public static StringConverterFactory create() {     return new StringConverterFactory(); }  @Override public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {     if (String.class.equals(type)) {         return new Converter<ResponseBody, String>() {              @Override             public String convert(ResponseBody value) throws IOException {                 return value.string();             }         };     }     return null; }  @Override public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {     if(String.class.equals(type)) {         return new Converter<String, RequestBody>() {             @Override             public RequestBody convert(String value) throws IOException {                 return RequestBody.create(MEDIA_TYPE, value);             }         };     }      return null; }  } 

Add to your retrofit instance:

retrofit = new Retrofit.Builder()             .baseUrl(SERVER_URL)             .client(client)             .addConverterFactory(StringConverterFactory.create())             .addConverterFactory(GsonConverterFactory.create())             .addCallAdapterFactory(RxJavaCallAdapterFactory.create())             .build(); 

Attention: StringConverterFactory should add before GsonConverterFactory!

then you can use String as part value directly.

You can find more information about this issue in https://github.com/square/retrofit/issues/1210

like image 114
Loyea Avatar answered Oct 06 '22 10:10

Loyea