Retrofit 2.0-beta-2 добавляет литеральные кавычки к значениям MultiPart
Пришел к обновлению до Retrofit 2.0 и столкнулся с этой странной проблемой.
У меня есть способ зарегистрировать пользователя в
public interface ApiInterface {
@Multipart
@POST("user/login/")
Call<SessionToken> userLogin(@Part("username") String username, @Part("password") String password);
}
Когда я смотрю на ключевые значения POST-параметров на стороне сервера, они печатаются следующим образом
username : "brian"
password : "password"
Тот же метод с использованием модификации 1.9 пары K: V выглядят как
username : brian
password : password
Он добавляет литеральные цитаты к переменным POST
Если я использую любой другой клиент для отдыха, переменные печатаются как второй путь без кавычек.
Вот как я могу создать экземпляр Retrofit с помощью перехватчика
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();
Я предполагаю, что я делаю что-то неправильно с конвертером, но не уверен, что.
Кто-нибудь еще сталкивался с этой проблемой? Я знаю его в бета-версии, но он довольно широко используется.
Ответы
Ответ 1
Это потому, что он работает через конвертер JSON.
Solution1:
используйте RequestBody
вместо String
public interface ApiInterface {
@Multipart
@POST("user/login/")
Call<SessionToken> userLogin(@Part("username") RequestBody username, @Part("password") RequestBody password);
}
Создайте RequestBody:
RequestBody usernameBody = RequestBody.create(MediaType.parse("text/plain"), usernameStr);
RequestBody passwordBody = RequestBody.create(MediaType.parse("text/plain"), passwordStr);
Запустите работу сети:
retrofit.create(ApiInterface.class).userLogin(usernameBody , passwordBody).enqueue()....
Решение2: Создайте пользовательский ConverterFactory для размещения значения String.
Для: окончательный выпуск Retrofit2 не бета-версии. (com.squareup.retrofit2: retrofit: 2.0.0)
Создайте свой 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;
}
}
Добавьте в свой модифицированный экземпляр:
retrofit = new Retrofit.Builder()
.baseUrl(SERVER_URL)
.client(client)
.addConverterFactory(StringConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
Внимание: StringConverterFactory
следует добавить до GsonConverterFactory
!
то вы можете использовать String
как значение детали напрямую.
Дополнительную информацию об этой проблеме вы можете найти в https://github.com/square/retrofit/issues/1210
Ответ 2
Я нашел другое решение, кроме тех. Работает с дооснащением 2.1.0. (Здесь используется адаптер Rx)
Мой модифицированный интерфейс выглядит следующим образом:
@POST("/children/add")
Observable<Child> addChild(@Body RequestBody requestBody);
И в ApiManager я использую его следующим образом:
@Override
public Observable<Child> addChild(String firstName, String lastName, Long birthDate, @Nullable File passportPicture) {
MultipartBody.Builder builder = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("first_name", firstName)
.addFormDataPart("last_name", lastName)
.addFormDataPart("birth_date", birthDate + "");
//some nullable optional parameter
if (passportPicture != null) {
builder.addFormDataPart("certificate", passportPicture.getName(), RequestBody.create(MediaType.parse("image/*"), passportPicture));
}
return api.addChild(builder.build());
}
Он похож на Solution1 от Loyea, но я думаю, что он немного более изящный.
Ответ 3
Как это сделать?
RequestBody caption = RequestBody.create(MediaType.parse("text/plain"), new String("caption"));
Ответ 4
Вот как его решить,
Во-первых:
return new Retrofit.Builder()
.baseUrl(Env.GetApiBaseUrl())
.addConverterFactory(new GsonStringConverterFactory())
.addConverterFactory(GsonConverterFactory.create(gson))
.client(getHttpClient())
.build();
Создайте CustomConverter, подобный этому, это необходимо для Retrofit 2, если только некоторые не зафиксируют "функцию", добавленную в v2.
public class GsonStringConverterFactory extends Converter.Factory {
private static final MediaType MEDIA_TYPE = MediaType.parse("text/plain");
@Override
public Converter<?, RequestBody> toRequestBody(Type type, Annotation[] annotations) {
if (String.class.equals(type))// || (type instanceof Class && ((Class<?>) type).isEnum()))
{
return new Converter<String, RequestBody>() {
@Override
public RequestBody convert(String value) throws IOException {
return RequestBody.create(MEDIA_TYPE, value);
}
};
}
return null;
}
}
Ответ 5
Если ваш пользовательский интерфейс показывает ваши ответы с кавычками, вы можете использовать getAsString
вместо toString
Ответ 6
Я не знаю, слишком ли поздно, но мы также можем отправлять запросы с помощью RequestBody.
Пример:
public interface ApiInterface {
@Multipart
@POST("user/login/")
Call<SessionToken> userLogin(@Part("username") String username, @Part("password") String password);
}
Мы можем преобразовать, как показано ниже:
public interface ApiInterface {
@Multipart
@POST("user/login/")
Call<SessionToken> userLogin(@Part("username") RequestBody username, @Part("password") String password);
}
Ответ 7
У меня та же проблема, и как она решена:
1) Добавить в build.gradle:
compile 'com.squareup.retrofit2:converter-scalars:2.1.0' // remeber add the same version
2) Добавьте одну строку здесь:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(URL_BASE)
.addConverterFactory(ScalarsConverterFactory.create()) // this line
.addConverterFactory(GsonConverterFactory.create(gson))
.client(getUnsafeOkHttpClient())
.build();