web-dev-qa-db-ja.com

Retrofit2.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 paramsを見ると、次のように出力されます

username : "brian"
password : "password"

レトロフィット1.9を使用した同じ方法では、K:Vペアは次のようになります。

username : brian
password : password

POST変数にリテラル引用符を追加しています

他のRESTクライアントを使用する場合、変数は引用符なしで2番目の方法のように出力されます。

インターセプターを使用して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();

コンバーターで何か問題が発生していると思いますが、何が原因かわかりません。

他の誰かがこの問題に遭遇しましたか?私はそれがベータ版であることを知っていますが、それはかなり広く使われています。

25
Brian

これは、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()....

Solution2:文字列部分の値を破棄するカスタムConverterFactoryを作成します。

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

注意:StringConverterFactoryGsonConverterFactoryの前に追加する必要があります!

次に、Stringをパーツ値として直接使用できます。

この問題の詳細については、 https://github.com/square/retrofit/issues/121 をご覧ください。

32
Loyea

私は同じ問題を抱えており、それがどのように解決したか:

1)build.gradleに追加します:

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

2)ここに1行追加します:

Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(URL_BASE)
                .addConverterFactory(ScalarsConverterFactory.create()) // this line
                .addConverterFactory(GsonConverterFactory.create(gson))
                .client(getUnsafeOkHttpClient())
                .build();
6

those 以外の別の解決策を見つけました。 Retrofit2.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());
    }

LoyeaのSolution1に似ていますが、もう少しエレガントだと思います。

3
Yazon2006

そのように何をしようとしていますか?

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

これを解決する方法は次のとおりです。

まず:

 return new Retrofit.Builder()
    .baseUrl(Env.GetApiBaseUrl())
    .addConverterFactory(new GsonStringConverterFactory())
    .addConverterFactory(GsonConverterFactory.create(gson))
    .client(getHttpClient())
    .build();

このようなCustomConverterを作成します。これは、v2で追加された「機能」を修正しない限り、Retrofit2で必要になります。

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;
    }
}
3
andrey2ag

UIに引用符で応答が表示されている場合は、getAsStringの代わりにtoStringを使用できます。

0
cyberjaime45

手遅れかどうかはわかりませんが、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);
}
0
Ashwani jaggi