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();
コンバーターで何か問題が発生していると思いますが、何が原因かわかりません。
他の誰かがこの問題に遭遇しましたか?私はそれがベータ版であることを知っていますが、それはかなり広く使われています。
これは、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();
注意:StringConverterFactory
はGsonConverterFactory
の前に追加する必要があります!
次に、String
をパーツ値として直接使用できます。
この問題の詳細については、 https://github.com/square/retrofit/issues/121 をご覧ください。
私は同じ問題を抱えており、それがどのように解決したか:
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();
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に似ていますが、もう少しエレガントだと思います。
そのように何をしようとしていますか?
RequestBody caption = RequestBody.create(MediaType.parse("text/plain"), new String("caption"));
これを解決する方法は次のとおりです。
まず:
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;
}
}
UIに引用符で応答が表示されている場合は、getAsString
の代わりにtoString
を使用できます。
手遅れかどうかはわかりませんが、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);
}