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();

Я представляю, что делаю что-то не так с конвертером, но не знаю, что именно.

Кто-нибудь еще сталкивался с этой проблемой? Я знаю, что это бета-версия, но она довольно широко используется.


person Brian    schedule 19.10.2015    source источник
comment
Точно так же и здесь. Только что закончили 2-дневное масштабное обновление Retrofit 2.0, большинство вещей работает отлично, но я схожу с ума от этих дополнительных кавычек, добавленных к строкам. При использовании метода API @Multipart Retrofit строка является элементом @Part. Сервер получает строку test как test. ФУ!!!   -  person Matthew Housser    schedule 19.10.2015
comment
@MatthewHousser Я еще не нашел настоящего решения, вместо этого, поскольку я контролирую и бэкэнд, я установил специальный заголовок в клиентском приложении, а затем, если этот заголовок существует, я запускаю метод, который удаляет цитаты из параметров GET и POST, это супер хакерский, но, по крайней мере, запросы пока работают. Я думаю, что открою проблему с репозиторием на github   -  person Brian    schedule 19.10.2015
comment
пожалуйста, опубликуйте здесь свою проблему после ее создания, спасибо!   -  person Matthew Housser    schedule 19.10.2015
comment
Для справки, у меня на самом деле очень похожая проблема, которая, как я считаю, основана на той же ошибке Retrofit 2.0, хотя я пришел к проблеме с помощью Enums: stackoverflow.com/questions/33207400 /   -  person Matthew Housser    schedule 19.10.2015
comment
@MatthewHousser вы пробовали перейти на версию 2.0-beta1? Я тоже пробовал, но мне пришлось реорганизовать слишком много кода, чтобы заставить его работать.   -  person Brian    schedule 19.10.2015
comment
нет, я не пробовал переходить на более раннюю версию. У меня есть сотня конечных точек и такое же количество обработчиков обратных вызовов во всех 50 фрагментах, диалогах и т. Д. Было сложно просто перейти на Retrofit 2.0-beta2 .. Я даже не хочу думать о понижении версии = P   -  person Matthew Housser    schedule 19.10.2015


Ответы (7)


Это потому, что он работает через конвертер JSON.

Решение1: используйте 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: дооснащение: 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

person Loyea    schedule 25.03.2016
comment
Почему это решение 2 не подлежит модернизации? Я использую Retrofit: 2.0.2, и только этот ответ устранил проблему, хотя есть несколько решений, которые не сработали. - person Janaka R Rajapaksha; 11.05.2016
comment
@JanakaRRajapaksha Как сказал Джейк Уортон: Я не уверен, что хочу добавить это. Доказательство: github.com/square/retrofit/issues/1210. - person Yazon2006; 19.12.2016
comment
Большое спасибо! А как насчет того, чтобы поместить файл в этот запрос? - person AlexS; 05.06.2019

У меня такая же проблема, и как она решена:

1) Добавьте в build.gradle:

compile 'com.squareup.retrofit2:converter-scalars:2.1.0' // Remember to 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();
person nicolas asinovich    schedule 21.09.2017

Что насчет того, чтобы делать?

RequestBody caption = RequestBody.create(MediaType.parse("text/plain"), new String("caption"));
person Pedro Fraca    schedule 02.12.2015

Вот как это решить,

В первую очередь:

 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;
    }
}
person andrey2ag    schedule 03.01.2016

Я нашел еще одно решение, кроме тех. Работал с Retrofit 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, но я думаю, что он немного элегантнее.

person Yazon2006    schedule 19.12.2016

Если ваш пользовательский интерфейс показывает ваши ответы в кавычках, вы можете использовать getAsString вместо toString

person cyberjaime45    schedule 09.04.2016

Я не знаю, слишком ли поздно, но мы также можем отправлять запросы с 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);
}
person Ashwani jaggi    schedule 19.07.2017