Retrofit 2.0.0-beta1を使用しています。
テストでは代替シナリオがあり、エラーHTTP 400が予想されます
欲しいのですretrofit.Response<MyError> response
がresponse.body() == null
MyErrorはデシリアライズされていません-ここでしか表示されません
response.errorBody().string()
しかし、それはオブジェクトとしてMyErrorを与えません
Retrofit 2.0.0-beta1を使用しています。
テストでは代替シナリオがあり、エラーHTTP 400が予想されます
欲しいのですretrofit.Response<MyError> response
がresponse.body() == null
MyErrorはデシリアライズされていません-ここでしか表示されません
response.errorBody().string()
しかし、それはオブジェクトとしてMyErrorを与えません
回答:
私は現在、非常に簡単な実装を使用しており、コンバーターや特別なクラスを使用する必要はありません。私が使用するコードは次のとおりです。
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
DialogHelper.dismiss();
if (response.isSuccessful()) {
// Do your success stuff...
} else {
try {
JSONObject jObjError = new JSONObject(response.errorBody().string());
Toast.makeText(getContext(), jObjError.getJSONObject("error").getString("message"), Toast.LENGTH_LONG).show();
} catch (Exception e) {
Toast.makeText(getContext(), e.getMessage(), Toast.LENGTH_LONG).show();
}
}
}
ErrorResponseはカスタム応答オブジェクトです
コトリン
val gson = Gson()
val type = object : TypeToken<ErrorResponse>() {}.type
var errorResponse: ErrorResponse? = gson.fromJson(response.errorBody()!!.charStream(), type)
ジャワ
Gson gson = new Gson();
Type type = new TypeToken<ErrorResponse>() {}.getType();
ErrorResponse errorResponse = gson.fromJson(response.errorBody.charStream(),type);
gson.fromJson(response.errorBody()?.charStream(), type)
私はそれを解決しました:
if(!response.isSuccessful()){
Gson gson = new Gson();
MyErrorMessage message=gson.fromJson(response.errorBody().charStream(),MyErrorMessage.class);
if(message.getCode()==ErrorCode.DUPLICATE_EMAIL_ID_CODE){
//DO Error Code specific handling
}else{
//DO GENERAL Error Code Specific handling
}
}
MyErrorMessageクラス:
public class MyErrorMessage {
private int code;
private String message;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
.addConverterFactory(ScalarsConverterFactory.create())
@RonelGonzalesを使用する
Retrofit 2.0ベータ2では、これがエラー応答を受け取る方法です。
同期
try {
Call<RegistrationResponse> call = backendServiceApi.register(data.in.account, data.in.password,
data.in.email);
Response<RegistrationResponse> response = call.execute();
if (response != null && !response.isSuccess() && response.errorBody() != null) {
Converter<ResponseBody, BasicResponse> errorConverter =
MyApplication.getRestClient().getRetrofitInstance().responseConverter(BasicResponse.class, new Annotation[0]);
BasicResponse error = errorConverter.convert(response.errorBody());
//DO ERROR HANDLING HERE
return;
}
RegistrationResponse registrationResponse = response.body();
//DO SUCCESS HANDLING HERE
} catch (IOException e) {
//DO NETWORK ERROR HANDLING HERE
}
非同期
Call<BasicResponse> call = service.loadRepo();
call.enqueue(new Callback<BasicResponse>() {
@Override
public void onResponse(Response<BasicResponse> response, Retrofit retrofit) {
if (response != null && !response.isSuccess() && response.errorBody() != null) {
Converter<ResponseBody, BasicResponse> errorConverter =
retrofit.responseConverter(BasicResponse.class, new Annotation[0]);
BasicResponse error = errorConverter.convert(response.errorBody());
//DO ERROR HANDLING HERE
return;
}
RegistrationResponse registrationResponse = response.body();
//DO SUCCESS HANDLING HERE
}
@Override
public void onFailure(Throwable t) {
//DO NETWORK ERROR HANDLING HERE
}
});
Retrofit 2 beta3の更新:
非同期-レトロフィットパラメーターがonResponseから削除されました
Call<BasicResponse> call = service.loadRepo();
call.enqueue(new Callback<BasicResponse>() {
@Override
public void onResponse(Response<BasicResponse> response) {
if (response != null && !response.isSuccess() && response.errorBody() != null) {
Converter<ResponseBody, BasicResponse> errorConverter =
MyApplication.getRestClient().getRetrofitInstance().responseConverter(BasicResponse.class, new Annotation[0]);
BasicResponse error = errorConverter.convert(response.errorBody());
//DO ERROR HANDLING HERE
return;
}
RegistrationResponse registrationResponse = response.body();
//DO SUCCESS HANDLING HERE
}
@Override
public void onFailure(Throwable t) {
//DO NETWORK ERROR HANDLING HERE
}
});
List<BasicResponse>
ますか?
でhttps://stackoverflow.com/a/21103420/2914140とhttps://futurestud.io/tutorials/retrofit-2-simple-error-handlingこのバリアントは、レトロフィット2.1.0のために示されています。
call.enqueue(new Callback<MyResponse>() {
@Override
public void onResponse(Call<MyResponse> call, Response<MyResponse> response) {
if (response.isSuccessful()) {
...
} else {
Converter<ResponseBody, MyError> converter
= MyApplication.getRetrofit().responseBodyConverter(
MyError.class, new Annotation[0]);
MyError errorResponse = null;
try {
errorResponse = converter.convert(response.errorBody());
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void onResponse(Call<Void> call, retrofit2.Response<Void> response) {
if (response.isSuccessful()) {
//Do something if response is ok
} else {
JsonParser parser = new JsonParser();
JsonElement mJson = null;
try {
mJson = parser.parse(response.errorBody().string());
Gson gson = new Gson();
MyError errorResponse = gson.fromJson(mJson, MyError.class);
} catch (IOException ex) {
ex.printStackTrace();
}
}
エラー応答とユーザーGsonのモデルを作成して、応答に変換します。これは問題なく動作します。
APIError.java
public class APIError {
private String message;
public String getMessage() {
return message;
}
}
MainActivity.java(リクエスト内のonResponse)
if (response.isSuccessful()) {
// Do your success stuff...
} else {
APIError message = new Gson().fromJson(response.errorBody().charStream(), APIError.class);
Toast.makeText(MainActivity.this, "" + message.getMessage(), Toast.LENGTH_SHORT).show();
}
Retrofit 2.0-beta2を使用した非同期呼び出しの場合は、次のようにしました。
@Override
public void onResponse(Response<RegistrationResponse> response,
Retrofit retrofit) {
if (response.isSuccess()) {
// Do success handling here
} else {
try {
MyError myError = (MyError)retrofit.responseConverter(
MyError.class, MyError.class.getAnnotations())
.convert(response.errorBody());
// Do error handling here
} catch (IOException e) {
e.printStackTrace();
}
}
}
Kotlinを使用する場合、別の解決策は単にResponseクラスの拡張関数を作成することです:
inline fun <reified T>Response<*>.parseErrJsonResponse(): T?
{
val moshi = MyCustomMoshiBuilder().build()
val parser = moshi.adapter(T::class.java)
val response = errorBody()?.string()
if(response != null)
try {
return parser.fromJson(response)
} catch(e: JsonDataException) {
e.printStackTrace()
}
return null
}
使用法
val myError = response.parseErrJsonResponse<MyErrorResponse>()
if(myError != null) {
// handle your error logic here
// ...
}
それは実際には非常に簡単です。
コトリン:
val jsonObj = JSONObject(response.errorBody()!!.charStream().readText())
responseInterface.onFailure(jsonObj.getString("msg"))
Java:
JSONObject jsonObj = new JSONObject(response.errorBody().charStream().readText());
responseInterface.onFailure(jsonObj.getString("msg"));
後付けでテスト済み:2.5.0。文字列を提供するcharStreamからテキストを読み取り、JSONObjectに解析します。
アディオス。
readText()
Javaには拡張機能はありませTextStreamsKt.readText(response.errorBody().charStream())
ん。まだJavaを使用している場合は使用してください
私は同じ問題に直面していました。後付けで解決しました。これを見せて...
エラーJSON構造が次のような場合
{
"error": {
"status": "The email field is required."
}
}
My ErrorRespnce.java
public class ErrorResponse {
@SerializedName("error")
@Expose
private ErrorStatus error;
public ErrorStatus getError() {
return error;
}
public void setError(ErrorStatus error) {
this.error = error;
}
}
そして、これは私のエラーステータスクラスです
public class ErrorStatus {
@SerializedName("status")
@Expose
private String status;
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
}
ここで、jsonを処理できるクラスが必要です。
public class ErrorUtils {
public static ErrorResponse parseError (Response<?> response){
Converter<ResponseBody , ErrorResponse> converter = ApiClient.getClient().responseBodyConverter(ErrorResponse.class , new Annotation[0]);
ErrorResponse errorResponse;
try{
errorResponse = converter.convert(response.errorBody());
}catch (IOException e){
return new ErrorResponse();
}
return errorResponse;
}
}
これで、改良API呼び出しで応答を確認できます
private void registrationRequest(String name , String email , String password , String c_password){
final Call<RegistrationResponce> registrationResponceCall = apiInterface.getRegistration(name , email , password , c_password);
registrationResponceCall.enqueue(new Callback<RegistrationResponce>() {
@Override
public void onResponse(Call<RegistrationResponce> call, Response<RegistrationResponce> response) {
if (response.code() == 200){
}else if (response.code() == 401){
ErrorResponse errorResponse = ErrorUtils.parseError(response);
Toast.makeText(MainActivity.this, ""+errorResponse.getError().getStatus(), Toast.LENGTH_SHORT).show();
}
}
@Override
public void onFailure(Call<RegistrationResponce> call, Throwable t) {
}
});
}
これでトーストを表示できます
Kotlin
拡張機能を使用したエレガントなソリューションは次のとおりです。
data class ApiError(val code: Int, val message: String?) {
companion object {
val EMPTY_API_ERROR = ApiError(-1, null)
}
}
fun Throwable.getApiError(): ApiError? {
if (this is HttpException) {
try {
val errorJsonString = this.response()?.errorBody()?.string()
return Gson().fromJson(errorJsonString, ApiError::class.java)
} catch (exception: Exception) {
// Ignore
}
}
return EMPTY_API_ERROR
}
および使用法:
showError(retrofitThrowable.getApiError()?.message)
この方法では、レトロフィットから作成されたサービスのみを注入する場合は、レトロフィットインスタンスは必要ありません。
public class ErrorUtils {
public static APIError parseError(Context context, Response<?> response) {
APIError error = new APIError();
try {
Gson gson = new Gson();
error = gson.fromJson(response.errorBody().charStream(), APIError.class);
} catch (Exception e) {
Toast.makeText(context, e.getMessage(), Toast.LENGTH_LONG).show();
}
if (TextUtils.isEmpty(error.getErrorMessage())) {
error.setError(response.raw().message());
}
return error;
}
}
次のように使用します。
if (response.isSuccessful()) {
...
} else {
String msg = ErrorUtils.parseError(fragment.getActivity(), response).getError(); // would be from your error class
Snackbar.make(someview, msg, Snackbar.LENGTH_LONG).show();
}
}
これは、OkHttpをRetrofitと一緒に使用するときに問題になるようです。そのため、OkHttpを削除するか、以下のコードを使用してエラー本文を取得できます。
if (!response.isSuccessful()) {
InputStream i = response.errorBody().byteStream();
BufferedReader r = new BufferedReader(new InputStreamReader(i));
StringBuilder errorResult = new StringBuilder();
String line;
try {
while ((line = r.readLine()) != null) {
errorResult.append(line).append('\n');
}
} catch (IOException e) {
e.printStackTrace();
}
}
errorBodyをStringに読み取り、jsonを手動で解析します。
if(!response.isSuccessful()) {
String error = "";
try {
BufferedReader ereader = new BufferedReader(new InputStreamReader(
response.errorBody().byteStream()));
String eline = null;
while ((eline = ereader.readLine()) != null) {
error += eline + "";
}
ereader.close();
} catch (Exception e) {
error += e.getMessage();
}
Log.e("Error",error);
try {
JSONObject reader = new JSONObject(error);
String message = reader.getString("message");
Toast.makeText(context,message,Toast.LENGTH_SHORT).show();
} catch (JSONException e) {
e.printStackTrace();
}
}
それを解決:
Converter<MyError> converter =
(Converter<MyError>)JacksonConverterFactory.create().get(MyError.class);
MyError myError = converter.fromBody(response.errorBody());
JacksonConverterFactory
GsonConverterFactory
コトリンでは:
val call = APIClient.getInstance().signIn(AuthRequestWrapper(AuthRequest("1234567890z", "12341234", "nonce")))
call.enqueue(object : Callback<AuthResponse> {
override fun onResponse(call: Call<AuthResponse>, response: Response<AuthResponse>) {
if (response.isSuccessful) {
} else {
val a = object : Annotation{}
val errorConverter = RentalGeekClient.getRetrofitInstance().responseBodyConverter<AuthFailureResponse>(AuthFailureResponse::class.java, arrayOf(a))
val authFailureResponse = errorConverter.convert(response.errorBody())
}
}
override fun onFailure(call: Call<AuthResponse>, t: Throwable) {
}
})
errorBody値は、APIErrorオブジェクトをRetrofitに設定する必要があります。そのため、以下のコード構造を使用できます。
public class APIErrorUtils {
public static APIError parseError(Response<?> response) {
Converter<ResponseBody, APIError> converter = API.getClient().responseBodyConverter(APIError.class, new Annotation[0]);
APIError error;
try {
error = converter.convert(response.errorBody());
Log.d("SERVICELOG", "****************************************************");
Log.d("SERVICELOG", "***** SERVICE LOG");
Log.d("SERVICELOG", "***** TIMESTAMP: " + String.valueOf(error.getTimestamp()));
Log.d("SERVICELOG", "***** STATUS: " + String.valueOf(error.getStatus()));
Log.d("SERVICELOG", "***** ERROR: " + error.getError());
Log.d("SERVICELOG", "***** MESSAGE: " + error.getMessage());
Log.d("SERVICELOG", "***** PATH: " + error.getPath());
Log.d("SERVICELOG", "****************************************************");
} catch (IOException e) {
return new APIError();
}
return error;
}
}
APIError error = APIErrorUtils.parseError(response);
if (error.getStatus() == 400) {
....
}
テスト済みで動作
public BaseModel parse(Response<BaseModel> response , Retrofit retrofit){
BaseModel error = null;
Converter<ResponseBody, BaseModel> errorConverter =
retrofit.responseBodyConverter(BaseModel.class, new Annotation[0]);
try {
if (response.errorBody() != null) {
error = errorConverter.convert(response.errorBody());
}
} catch (IOException e) {
e.printStackTrace();
}
return error;
}
val error = JSONObject(callApi.errorBody()?.string() as String)
CustomResult.OnError(CustomNotFoundError(userMessage = error["userMessage"] as String))
open class CustomError (
val traceId: String? = null,
val errorCode: String? = null,
val systemMessage: String? = null,
val userMessage: String? = null,
val cause: Throwable? = null
)
open class ErrorThrowable(
private val traceId: String? = null,
private val errorCode: String? = null,
private val systemMessage: String? = null,
private val userMessage: String? = null,
override val cause: Throwable? = null
) : Throwable(userMessage, cause) {
fun toError(): CustomError = CustomError(traceId, errorCode, systemMessage, userMessage, cause)
}
class NetworkError(traceId: String? = null, errorCode: String? = null, systemMessage: String? = null, userMessage: String? = null, cause: Throwable? = null):
CustomError(traceId, errorCode, systemMessage, userMessage?: "Usted no tiene conexión a internet, active los datos", cause)
class HttpError(traceId: String? = null, errorCode: String? = null, systemMessage: String? = null, userMessage: String? = null, cause: Throwable? = null):
CustomError(traceId, errorCode, systemMessage, userMessage, cause)
class UnknownError(traceId: String? = null, errorCode: String? = null, systemMessage: String? = null, userMessage: String? = null, cause: Throwable? = null):
CustomError(traceId, errorCode, systemMessage, userMessage?: "Unknown error", cause)
class CustomNotFoundError(traceId: String? = null, errorCode: String? = null, systemMessage: String? = null, userMessage: String? = null, cause: Throwable? = null):
CustomError(traceId, errorCode, systemMessage, userMessage?: "Data not found", cause)`