retrofitのcontent-type浅析
10831 ワード
背景:問題のフィードバックをする時、バックエンドはPythonで書いて、postの複雑なlistの時、1つの穴に出会った.バックエンドpost構造体配列の場合、androidチーム版に伝わるcontent-typeは「text/html;charset=UTF-8」で、バックエンドの同級生は解析できないと言います.ios側から伝わってきたcontent-typeに従って、バックエンドjsonにこのstringを解析させると、渡すstringのフォーマットが面倒になり、各パラメータがkey-valueのフォーマットになります.
後で協議を経て、バックエンドはインタフェースcontent-typeを統一して“application/json;charset=utf-8”に変えて、つまり私達が伝えたのは1つのjsonで、よし、これでiosは問題があって、jsonのcontent-typeを使う時、チーム版のバックエンドは解析できません.すなわち、騎手プラットフォームappフィードバックこのインタフェースは、「アプリケーション/json;charset=utf-8」形式のcontent-typeを単独で使用する必要があり、他のすべてのインタフェースは、「text/html;charset=UTF-8」を使用する必要がある.
私たちandroidとiosを悩ませているcontent-typeはいったい何なのか.
私の理解によると、content-typeはパラメータです.私たちが伝えたパラメータは何ですか.そして、このパラメータに基づいてデータを異なる処理をします.私たちがretrofitを使うときと、responseに戻ってもcontent-typeがあります.content-typeタイプによって、データをどのように読み取るかを決めます.
チーム版でPOSTリクエストをしたとき、なぜcontent-typeはjsonだったりtextだったりしますか?
実際には、インタフェースファイルに@FormUrlEncodedを追加すると、content-typeはtext形式であり、この注釈がない場合、retrofitにGsonRequestBodyConverterを追加するため、このconvertはデフォルトのcontent-typeをjsonに変更することがわかります.つまり、インタフェースに注釈が付いていない場合は、カスタムcontent-typeを使用し、注釈があれば注釈のcontent-typeを使用します.
その事実はそうではないでしょうか.ソースコードを探してみましょう.
エントリ関数Retrofitのcreate関数.
この中でダイナミックエージェントを使う方法で、エージェントが必要なクラスを得るのは、私たちが注釈をつけたクラスです.他は詳しく見ません.サービスMethodは何ですか.
戻ってくるのはresult、resultは何ですか?サービスを見てみましょうBuilderは何をしましたか?
一目瞭然で、注釈のあるクラスの注釈、方法、typeなどを手に入れ、build()でbuilderのパラメータを手に入れ、
ここには私たちが探しているcontentTypeがあります.具体的なbuild()は具体的に見てもいいですが、ここでは詳しく説明しません.
ここまで来たらcontentTypeを手に入れてどうやってネットリクエストしますか?OkHttpCallのenqueue関数を見てみましょう.コードが1行入っています.
ここではretrofitが使用するokhttpのcallです.createRawCall関数を見てみましょう.
戻るのも確かにokhttpのcallですが、ここのrequestはどこから来たのでしょうか?では、前のserviceMethodのtoRequest関数に戻ります.
ははは、RequestBuilderが最終的に私たちが配置したファイルだったのか、入ってみると一目瞭然で、contentTypeが目に入りました.
はい、build()関数はokhttp requestを構成するために必要なステップです.contentTypeがどこにあるかを知る必要があります(ここではパラメータをbodyに構成してから、具体的な方法に戻ります).
ぐるぐる回って、やっと君を見つけた!私たちの注釈の中にいくつかの注釈があるとき、ここではヘッダーのcontenttypeを直接私たちの注釈の中に置き換えます.注釈にcontentTypeがなかったら?
注記がなければaddHeaderなどでcontentTypeを設定します.
ContentTypeのretrofitでの流れはこのようなもので、迂回していますが、最終的には私たちの観点を確定しました.
インタフェースに注釈が付いていない場合は、カスタムcontent-typeを使用し、注釈があれば注釈のcontent-typeを使用します.
過程は複雑で、結論は簡単で、後でバックエンドと調整する時、何かcontent-typeが間違っている問題に出会ったら、私たちはすぐに問題を見つけることができます.
もちろんretrofitには他の注釈もありますが、興味のある人は理解してください.
後で協議を経て、バックエンドはインタフェースcontent-typeを統一して“application/json;charset=utf-8”に変えて、つまり私達が伝えたのは1つのjsonで、よし、これでiosは問題があって、jsonのcontent-typeを使う時、チーム版のバックエンドは解析できません.すなわち、騎手プラットフォームappフィードバックこのインタフェースは、「アプリケーション/json;charset=utf-8」形式のcontent-typeを単独で使用する必要があり、他のすべてのインタフェースは、「text/html;charset=UTF-8」を使用する必要がある.
私たちandroidとiosを悩ませているcontent-typeはいったい何なのか.
私の理解によると、content-typeはパラメータです.私たちが伝えたパラメータは何ですか.そして、このパラメータに基づいてデータを異なる処理をします.私たちがretrofitを使うときと、responseに戻ってもcontent-typeがあります.content-typeタイプによって、データをどのように読み取るかを決めます.
チーム版でPOSTリクエストをしたとき、なぜcontent-typeはjsonだったりtextだったりしますか?
実際には、インタフェースファイルに@FormUrlEncodedを追加すると、content-typeはtext形式であり、この注釈がない場合、retrofitにGsonRequestBodyConverterを追加するため、このconvertはデフォルトのcontent-typeをjsonに変更することがわかります.つまり、インタフェースに注釈が付いていない場合は、カスタムcontent-typeを使用し、注釈があれば注釈のcontent-typeを使用します.
その事実はそうではないでしょうか.ソースコードを探してみましょう.
エントリ関数Retrofitのcreate関数.
public T create(final Class service) {
Utils.validateServiceInterface(service);
if (validateEagerly) {
eagerlyValidateMethods(service);
}
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();
@Override public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// If the method is a method from Object then defer to normal invocation.
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
ServiceMethod
この中でダイナミックエージェントを使う方法で、エージェントが必要なクラスを得るのは、私たちが注釈をつけたクラスです.他は詳しく見ません.サービスMethodは何ですか.
ServiceMethod, ?> loadServiceMethod(Method method) {
ServiceMethod, ?> result = serviceMethodCache.get(method);
if (result != null) return result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
result = new ServiceMethod.Builder<>(this, method).build();
serviceMethodCache.put(method, result);
}
}
return result;
}
戻ってくるのはresult、resultは何ですか?サービスを見てみましょうBuilderは何をしましたか?
Builder(Retrofit retrofit, Method method) {
this.retrofit = retrofit;
this.method = method;
this.methodAnnotations = method.getAnnotations();
this.parameterTypes = method.getGenericParameterTypes();
this.parameterAnnotationsArray = method.getParameterAnnotations();
}
一目瞭然で、注釈のあるクラスの注釈、方法、typeなどを手に入れ、build()でbuilderのパラメータを手に入れ、
Type responseType;
boolean gotField;
boolean gotPart;
boolean gotBody;
boolean gotPath;
boolean gotQuery;
boolean gotUrl;
String httpMethod;
boolean hasBody;
boolean isFormEncoded;
boolean isMultipart;
String relativeUrl;
Headers headers;
MediaType contentType;
ここには私たちが探しているcontentTypeがあります.具体的なbuild()は具体的に見てもいいですが、ここでは詳しく説明しません.
ここまで来たらcontentTypeを手に入れてどうやってネットリクエストしますか?OkHttpCallのenqueue関数を見てみましょう.コードが1行入っています.
call = rawCall = createRawCall();
ここではretrofitが使用するokhttpのcallです.createRawCall関数を見てみましょう.
private okhttp3.Call createRawCall() throws IOException {
Request request = serviceMethod.toRequest(args);
okhttp3.Call call = serviceMethod.callFactory.newCall(request);
if (call == null) {
throw new NullPointerException("Call.Factory returned null.");
}
return call;
}
戻るのも確かにokhttpのcallですが、ここのrequestはどこから来たのでしょうか?では、前のserviceMethodのtoRequest関数に戻ります.
Request toRequest(Object... args) throws IOException {
RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
contentType, hasBody, isFormEncoded, isMultipart);
@SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
ParameterHandler[] handlers = (ParameterHandler[]) parameterHandlers;
int argumentCount = args != null ? args.length : 0;
if (argumentCount != handlers.length) {
throw new IllegalArgumentException("Argument count (" + argumentCount
+ ") doesn't match expected count (" + handlers.length + ")");
}
for (int p = 0; p < argumentCount; p++) {
handlers[p].apply(requestBuilder, args[p]);
}
return requestBuilder.build();
}
ははは、RequestBuilderが最終的に私たちが配置したファイルだったのか、入ってみると一目瞭然で、contentTypeが目に入りました.
RequestBuilder(String method, HttpUrl baseUrl, String relativeUrl, Headers headers,
MediaType contentType, boolean hasBody, boolean isFormEncoded, boolean isMultipart) {
this.method = method;
this.baseUrl = baseUrl;
this.relativeUrl = relativeUrl;
this.requestBuilder = new Request.Builder();
this.contentType = contentType;
this.hasBody = hasBody;
はい、build()関数はokhttp requestを構成するために必要なステップです.contentTypeがどこにあるかを知る必要があります(ここではパラメータをbodyに構成してから、具体的な方法に戻ります).
if (contentType != null) {
if (body != null) {
body = new ContentTypeOverridingRequestBody(body, contentType);
} else {
requestBuilder.addHeader("Content-Type", contentType.toString());
}
}
ぐるぐる回って、やっと君を見つけた!私たちの注釈の中にいくつかの注釈があるとき、ここではヘッダーのcontenttypeを直接私たちの注釈の中に置き換えます.注釈にcontentTypeがなかったら?
void addHeader(String name, String value) {
if ("Content-Type".equalsIgnoreCase(name)) {
MediaType type = MediaType.parse(value);
if (type == null) {
throw new IllegalArgumentException("Malformed content type: " + value);
}
contentType = type;
} else {
requestBuilder.addHeader(name, value);
}
}
注記がなければaddHeaderなどでcontentTypeを設定します.
ContentTypeのretrofitでの流れはこのようなもので、迂回していますが、最終的には私たちの観点を確定しました.
インタフェースに注釈が付いていない場合は、カスタムcontent-typeを使用し、注釈があれば注釈のcontent-typeを使用します.
過程は複雑で、結論は簡単で、後でバックエンドと調整する時、何かcontent-typeが間違っている問題に出会ったら、私たちはすぐに問題を見つけることができます.
もちろんretrofitには他の注釈もありますが、興味のある人は理解してください.