Trang bị thêm 2.0 cách nhận phản hồi lỗi đã khử.


128

Tôi đang sử dụng Retrofit 2.0.0-beta1 .

Trong các thử nghiệm, tôi có một kịch bản thay thế và mong đợi lỗi HTTP 400

Tôi muốn có retrofit.Response<MyError> response nhưngresponse.body() == null

MyError không được giải trừ - tôi chỉ thấy nó ở đây

response.errorBody().string()

nhưng nó không cho tôi MyError làm đối tượng



nó có phải là một thực hành tốt để giảm thiểu phản ứng lỗi? vì phản hồi có thể là lỗi máy chủ web là html.
Hossein Shahdoost

1
thx @ahmadalibaloch, liên kết đó thực sự hữu ích.
Ravi Vaniya

Câu trả lời:


138

Tôi hiện đang sử dụng một triển khai rất dễ dàng, không yêu cầu sử dụng các trình chuyển đổi hoặc các lớp đặc biệt. Mã tôi sử dụng là như sau:

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
Giải pháp của bạn không hiển thị nội dung phản hồi lỗi.
CoolMind

1
Kiểm tra chỉnh sửa của tôi, dunno tại sao tôi làm cho nó không rõ ràng ở nơi đầu tiên.
Saif Bechan

4
Cuối cùng, một câu trả lời đơn giản cho một câu hỏi Android hoạt động (hầu hết các câu trả lời của Android đều phức tạp về mặt hài hước).
Doug Voss

Điều này chắc chắn không trả lời câu hỏi. Nó chỉ đơn giản trả về thông báo lỗi chứ không phải đối tượng Error Enum. Vui lòng làm theo phản hồi này: stackoverflow.com/a/21103420/2914140
Tobliug

Trong câu trả lời, nó đang tìm kiếm ánh xạ tới "tin nhắn", nhưng phản hồi lỗi của tôi không có điều đó. Nó có một ánh xạ tới "lỗi". Vì vậy, tất cả mọi người đọc, nó phụ thuộc vào phản ứng bạn nhận được!
mco

43

ErrorResponse là đối tượng phản hồi tùy chỉnh của bạn

Kotlin

val gson = Gson()
val type = object : TypeToken<ErrorResponse>() {}.type
var errorResponse: ErrorResponse? = gson.fromJson(response.errorBody()!!.charStream(), type)

Java

Gson gson = new Gson();
Type type = new TypeToken<ErrorResponse>() {}.getType();
ErrorResponse errorResponse = gson.fromJson(response.errorBody.charStream(),type);

3
bạn không nên ép buộc các tùy chọn gson.fromJson(response.errorBody()?.charStream(), type)
hủy kết nối

37

Tôi đã giải quyết nó bằng cách:

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

Lớp 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: Dự kiến ​​BEGIN_OB DỰ ÁN nhưng đã BẮT ĐẦU ở dòng 1 cột 2 đường dẫn $
Ronel Gonzales

Sử dụng .addConverterFactory(ScalarsConverterFactory.create())@RonelGonzales
Pratik Butani

30

Trong Retrofit 2.0 beta2, đây là cách tôi nhận được phản hồi lỗi:

  1. Đồng bộ

    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. Không đồng bộ

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

Cập nhật cho Retrofit 2 beta3:

  1. Đồng bộ - không thay đổi
  2. Không đồng bộ - Tham số trang bị thêm đã bị xóa khỏi 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
Bạn có gì trong BasicResponse?
Jemshit Iskenderov

2
Chỉ là một lớp chú thích Jackson cơ bản có chứa thông báo và mã lỗi. Trong mọi trường hợp, bạn có thể có bất kỳ lớp chú thích nào phù hợp với loại phản hồi máy chủ của bạn. Hãy thử sử dụng jsonschema2pojo để tạo một phù hợp với nhu cầu của bạn.
JFreeman

Đối với những người khác, bạn có thể sử dụng điều này thay vào đó: Trình chuyển đổi <FeedbackBody, <Message> errorConverter = retrofit.responseBodyConverter (Message. Class, chú thích mới [0]);
Kim Montano

@JFreeman, nếu tôi muốn giải trừ List<BasicResponse>thì sao?
azizbekian

bạn có thể cho tôi xem lớp MyApplication
dungtv

10

Trong https://stackoverflow.com/a/21103420/2914140https://futurestud.io/tutorials/retrofit-2-simple-error-handling biến thể này được hiển thị cho Retrofit 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();
            }
        }
    }

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

            }

9

Tạo một mô hình của phản hồi Lỗi & người dùng Gson để chuyển đổi phản hồi cho nó. Điều này sẽ làm việc tốt.

APIError.java

public class APIError {
    private String message;

    public String getMessage() {
        return message;
    }
}

MainActivity.java (yêu cầu bên trong 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();
}

7

Tôi đã làm theo cách này cho các cuộc gọi không đồng bộ bằng 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();
        }
    }
}

Lớp MyError sẽ là gì?
Dhrupal

Tôi nghĩ onResponse được cho là chứa tham số Cuộc gọi và tham số Phản hồi. Làm thế nào mà bạn có một tham số Retrofit?
Marty Miller

@MartyMiller điều này đã được thực hiện cho phiên bản sau của trang bị thêm Retrofit 2.0-beta2
shantanu

7

Nếu bạn sử dụng Kotlin, một giải pháp khác có thể chỉ là tạo chức năng mở rộng cho lớp Phản hồi:

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
}

Sử dụng

val myError = response.parseErrJsonResponse<MyErrorResponse>()
if(myError != null) {
   // handle your error logic here
   // ...
}

Cuối cùng, ai đó đã sử dụng sức mạnh của Kotlin để làm cho mã dễ đọc!
Vince

5

Nó thực sự rất thẳng về phía trước.

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

Đã thử nghiệm trên trang bị thêm: 2.5.0. Đọc văn bản từ charStream sẽ cung cấp cho bạn một Chuỗi, sau đó phân tích cú pháp thành JSONObject.

Adios.


không có readText()tiện ích mở rộng trên java, hãy sử dụng TextStreamsKt.readText(response.errorBody().charStream())nếu bạn vẫn sử dụng java
mochadwi

4

Tôi đã phải đối mặt với vấn đề tương tự. Tôi đã giải quyết nó với trang bị thêm. Hãy để tôi chỉ cho ...

Nếu lỗi cấu trúc JSON của bạn giống như

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

Và đây là lớp trạng thái Lỗi của tôi

public class ErrorStatus {

  @SerializedName("status")
  @Expose
  private String status;

  public String getStatus() {
      return status;
  }

  public void setStatus(String status) {
      this.status = status;
  }
}

Bây giờ chúng tôi cần một lớp có thể xử lý json của chúng tôi.

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

Bây giờ chúng tôi có thể kiểm tra phản hồi của chúng tôi trong cuộc gọi api trang bị thêm

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

        }
    });
}

Đó là bây giờ bạn có thể hiển thị bánh mì nướng của bạn


4

Đây là giải pháp tao nhã bằng cách sử dụng Kotlintiện ích mở rộng:

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
}

và cách sử dụng:

showError(retrofitThrowable.getApiError()?.message)


3

Bằng cách này, bạn không cần một ví dụ Retrofit nếu bạn chỉ tiêm một dịch vụ được tạo từ 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;
  }
}

Sử dụng nó như thế này:

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

2

Đây có vẻ là vấn đề khi bạn sử dụng OkHttp cùng với Retrofit, vì vậy bạn có thể xóa OkHttp hoặc sử dụng mã bên dưới để nhận phần lỗi:

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

2

Đọc errorBody thành Chuỗi và phân tích thủ công 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();
    }
 }

0

giải quyết nó bằng cách:

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

Làm cách nào tôi có thể chuyển đổi qua GsonConverterFactory? Bất kỳ ý tưởng ?
Shan Xeeshi

Tôi tìm thấy một cách. Tôi chỉ thay đổi JacksonConverterFactory thành GsonConverterFactoryNó chuyển đổi json thành đối tượng tùy chỉnh của mình nhưng nó đưa ra cảnh báo Không được kiểm tra cast retrofit.Converter <Capture <? >>
Shan Xeeshi

3
Lớp MyError có gì?
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

Trong 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

Các giá trị errorBody nên đặt đối tượng APIError trong Retrofit. Vì vậy, bạn có thể sử dụng cấu trúc mã dưới đây.

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

Đã thử nghiệm và hoạt động

 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)`
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.