Retrofit 2.0 วิธีรับการตอบสนองข้อผิดพลาด deserialised ร่างกาย


132

ฉันใช้Retrofit 2.0.0-beta1

ในการทดสอบฉันมีสถานการณ์อื่นและคาดว่าจะเกิดข้อผิดพลาด HTTP 400

ฉันอยากจะมีretrofit.Response<MyError> response แต่response.body() == null

MyError ไม่ได้ถูก deserialised - ฉันเห็นที่นี่เท่านั้น

response.errorBody().string()

แต่มันไม่ได้ให้ MyError เป็นวัตถุ



เป็นแนวทางปฏิบัติที่ดีในการยกเลิกการตอบสนองข้อผิดพลาดหรือไม่ เนื่องจากการตอบสนองอาจเป็นข้อผิดพลาดของเว็บเซิร์ฟเวอร์ซึ่งเป็น html
Hossein Shahdoost

1
ขอบคุณ @ahmadalibaloch ลิงค์นั้นช่วยได้มากจริงๆ
Ravi Vaniya

คำตอบ:


139

ปัจจุบันฉันใช้การใช้งานที่ง่ายมากซึ่งไม่จำเป็นต้องใช้ตัวแปลงหรือคลาสพิเศษ รหัสที่ฉันใช้มีดังต่อไปนี้:

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

6
โซลูชันของคุณไม่แสดงเนื้อหาตอบสนองข้อผิดพลาด
CoolMind

1
ตรวจสอบการแก้ไขของฉันไม่รู้ว่าทำไมฉันถึงทำมันไม่ชัดเจนตั้งแต่แรก
Saif Bechan

4
ในที่สุดคำตอบง่ายๆสำหรับคำถาม Android ที่ใช้งานได้ (คำตอบของ Android ส่วนใหญ่มีความซับซ้อนเชิงตลก)
Doug Voss

นี่ไม่ตอบคำถามแน่นอน เพียงส่งคืนข้อความแสดงข้อผิดพลาดไม่ใช่ Error Enum Object โปรดปฏิบัติตามคำตอบนี้: stackoverflow.com/a/21103420/2914140
Tobliug

ในคำตอบมันกำลังมองหาการจับคู่กับ "ข้อความ" แต่การตอบสนองข้อผิดพลาดของฉันไม่มี มีการแมปกับ "ข้อผิดพลาด" ดังนั้นทุกคนที่อ่านมันขึ้นอยู่กับการตอบสนองที่คุณได้รับ!
mco

46

ErrorResponseคือออบเจ็กต์ตอบกลับที่คุณกำหนดเอง

Kotlin

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

3
คุณไม่ควรบังคับให้gson.fromJson(response.errorBody()?.charStream(), type)
คลาย

37

ฉันแก้ไขโดย:

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

2
java.lang.IllegalStateException: คาดว่าจะเป็น BEGIN_OBJECT แต่เป็น STRING ที่บรรทัด 1 คอลัมน์ 2 เส้นทาง $
Ronel Gonzales

ใช้.addConverterFactory(ScalarsConverterFactory.create())@RonelGonzales
Pratik Butani

30

ใน Retrofit 2.0 beta2 นี่คือวิธีที่ฉันได้รับการตอบสนองข้อผิดพลาด:

  1. พร้อมกัน

    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
    }
  2. ไม่ตรงกัน

    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:

  1. ซิงโครนัส - ไม่เปลี่ยนแปลง
  2. Asynchronous - พารามิเตอร์ Retrofit ถูกลบออกจาก 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
        }
    });

4
คุณมีอะไรใน BasicResponse?
Jemshit Iskenderov

2
เป็นเพียงคลาสคำอธิบายประกอบพื้นฐานของ Jackson ที่มีข้อความและรหัสข้อผิดพลาด ไม่ว่าในกรณีใดคุณสามารถมีคลาสที่มีคำอธิบายประกอบที่ตรงกับประเภทการตอบกลับเซิร์ฟเวอร์ของคุณ ลองใช้jsonschema2pojoเพื่อสร้างสิ่งที่ตรงกับความต้องการของคุณ
JFreeman

สำหรับคนอื่นคุณสามารถใช้สิ่งนี้แทน: Converter <ResponseBody, <Message> errorConverter = retrofit.responseBodyConverter (Message.class, annotation ใหม่ [0]);
Kim Montano

@JFreeman ถ้าฉันต้องการ deserialize List<BasicResponse>ล่ะ?
azizbekian

ให้ฉันดูคลาส MyApplication
dungtv

10

ในhttps://stackoverflow.com/a/21103420/2914140และhttps://futurestud.io/tutorials/retrofit-2-simple-error- การจัดการตัวแปรนี้แสดงไว้สำหรับชุดติดตั้งเพิ่ม 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();
            }
        }
    }

10

สร้างแบบจำลองของการตอบสนองข้อผิดพลาดและ 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();
}

9
 @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();
                }

            }

7

ฉันทำวิธีนี้สำหรับการโทรแบบอะซิงโครนัสโดยใช้ 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();
        }
    }
}

คลาส MyError จะเป็นอย่างไร?
Dhrupal

ฉันคิดว่า onResponse ควรมีพารามิเตอร์การโทรและพารามิเตอร์การตอบกลับ ของคุณมีพารามิเตอร์ Retrofit ได้อย่างไร?
Marty Miller

@MartyMiller สิ่งนี้ทำขึ้นสำหรับรุ่นต่อไปของ Retrofit Retrofit 2.0-beta2
shantanu

7

หากคุณใช้ 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
   // ...
}

ในที่สุดก็มีคนใช้พลัง Kotlin เพื่อทำให้โค้ดอ่านง่าย!
Vince

6

ตรงไปตรงมามากจริงๆ

Kotlin:

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

Adios


ไม่มีreadText()ส่วนขยายบน java ให้ใช้TextStreamsKt.readText(response.errorBody().charStream())ถ้าคุณยังใช้ java
mochadwi

4

ฉันประสบปัญหาเดียวกัน ฉันแก้ไขด้วยการติดตั้งเพิ่มเติม ให้ฉันแสดงสิ่งนี้ ...

หากโครงสร้าง 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) {

        }
    });
}

เท่านี้ก็แสดงขนมปังปิ้งได้แล้ว


4

นี่คือโซลูชันที่สวยงามโดยใช้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)


3

ด้วยวิธีนี้คุณไม่จำเป็นต้องมีอินสแตนซ์ Retrofit หากคุณเพียงฉีดบริการที่สร้างจากชุดติดตั้งเพิ่มเติม

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

3

อ่าน 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();
    }
 }

2

ดูเหมือนว่าจะเป็นปัญหาเมื่อคุณใช้ 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(); 
}
}

0

แก้ไขได้โดย:

Converter<MyError> converter = 
    (Converter<MyError>)JacksonConverterFactory.create().get(MyError.class);
MyError myError =  converter.fromBody(response.errorBody());

ฉันจะแปลงผ่าน GsonConverterFactory ได้อย่างไร ความคิดใด ๆ ?
Shan Xeeshi

ฉันพบวิธีแล้ว ฉันเพิ่งเปลี่ยนJacksonConverterFactory เป็นGsonConverterFactoryมันแปลง json เป็นวัตถุที่กำหนดเองของฉัน แต่มันให้คำเตือนการติดตั้งเพิ่ม Cast ที่ไม่ได้ตรวจสอบตัวแปลง <capture <? >>
Shan Xeeshi

3
คลาส MyError มีอะไรบ้าง?
Dhrupal

0
try{
                ResponseBody response = ((HttpException) t).response().errorBody();
                JSONObject json = new JSONObject( new String(response.bytes()) );
                errMsg = json.getString("message");
            }catch(JSONException e){
                return t.getMessage();
            }
            catch(IOException e){
                return t.getMessage();
            }

0

ใน Kotlin:

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) {
    }
})

0

ค่า 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) {
    ....
}

0

ทดสอบแล้วใช้งานได้จริง

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

-1
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)`
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.