Retrofit 2.0 как получить десериализованную ошибку response.body

Я использую Retrofit 2.0.0-beta1 .

В тестах я имею альтернативный сценарий и ожидаю ошибку HTTP 400

Я хотел бы иметь retrofit.Response response но response.body() == null

MyError не десериализирован – я вижу его только здесь

 response.errorBody().string() 

но это не дает мне MyError как объект

В настоящее время я использую очень легкую реализацию, которая не требует использования преобразователей или специальных classов. Код, который я использую, следующий:

 public void onResponse(Call call, Response response) { DialogHelper.dismiss(); if (response.isSuccessful()) { // Do your success stuff... } else { try { JSONObject jObjError = new JSONObject(response.errorBody().string()); Toast.makeText(getContext(), jObjError.getString("message"), Toast.LENGTH_LONG).show(); } catch (Exception e) { Toast.makeText(getContext(), e.getMessage(), Toast.LENGTH_LONG).show(); } } } 

В Retrofit 2.0 beta2 это способ, которым я получаю ответы об ошибках:

  1. синхронный

     try { Call call = backendServiceApi.register(data.in.account, data.in.password, data.in.email); Response response = call.execute(); if (response != null && !response.isSuccess() && response.errorBody() != null) { Converter 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 call = service.loadRepo(); call.enqueue(new Callback() { @Override public void onResponse(Response response, Retrofit retrofit) { if (response != null && !response.isSuccess() && response.errorBody() != null) { Converter 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 } }); 

Обновление для дооснащения 2 beta3:

  1. Синхронный – не изменен
  2. Асинхронный – параметр Retrofit был удален из onResponse

     Call call = service.loadRepo(); call.enqueue(new Callback() { @Override public void onResponse(Response response) { if (response != null && !response.isSuccess() && response.errorBody() != null) { Converter 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 } }); 

Я решил это:

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

Я сделал это для асинхронных вызовов с помощью Retrofit 2.0-beta2:

 @Override public void onResponse(Response 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(); } } } 

В https://stackoverflow.com/a/21103420/2914140 и https://futurestud.io/tutorials/retrofit-2-simple-error-handling этот вариант показан для Retrofit 2.1.0.

 call.enqueue(new Callback() { @Override public void onResponse(Call call, Response response) { if (response.isSuccessful()) { ... } else { Converter 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 call, retrofit2.Response 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(); } } 

Таким образом, вам не нужен экземпляр Retrofit, если вы только вводите службу, созданную из 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(); } } 

Это, похоже, проблема, когда вы используете 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(); } } 

Как насчет некоторого обобщенного решения, я думаю, что что-то вроде этого может работать:

 abstract class TestCallback implements Callback { Class errorClass; Retrofit retrofit; TestCallback(Retrofit retrofit, Class errorClass) { this.retrofit = retrofit; this.errorClass = errorClass; } abstract void onSuccess(Call call, RESPONSE response); @Override public void onResponse(Call call, Response response) { if (response.isSuccessful()) { onSuccess(call, response.body()); return; } if (response.errorBody() != null) { Converter converter = retrofit.responseBodyConverter(errorClass, new Annotation[0]); ERROR error; try { error = converter.convert(response.errorBody()); onFailure(call, error); } catch (IOException e) { // Conversion error. Add some meaningful message or return a custom error. onFailure(call, new Throwable()); } } else { // Unknown HTTP error (errorBody == null). Add some meaningful message or return a custom error. onFailure(call, new Throwable()); } } } 

И тогда мы можем использовать этот пользовательский Callback<> следующим образом:

 Call call = retrofit.create(UsersApi.class).signUp(email, password); call.enqueue(new TestCallback(retrofit, TestError.class) { @Override public void onFailure(Call call, Throwable t) { if (t instanceof TestError) { } else { } } @Override void onSuccess(Call call, User response) { // No need to check for isSuccessful() + no need to // duplicate the same code for all of your handlers. } }); 

решил:

 Converter converter = (Converter)JacksonConverterFactory.create().get(MyError.class); MyError myError = converter.fromBody(response.errorBody()); 
 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(); } 

В Котлине:

 val call = APIClient.getInstance().signIn(AuthRequestWrapper(AuthRequest("1234567890z", "12341234", "nonce"))) call.enqueue(object : Callback { override fun onResponse(call: Call, response: Response) { if (response.isSuccessful) { } else { val a = object : Annotation{} val errorConverter = RentalGeekClient.getRetrofitInstance().responseBodyConverter(AuthFailureResponse::class.java, arrayOf(a)) val authFailureResponse = errorConverter.convert(response.errorBody()) } } override fun onFailure(call: Call, t: Throwable) { } }) 

Значения errorBody должны установить объект APIError в Retrofit. Таким образом, вы можете использовать приведенную ниже структуру кода.

открытый class APIErrorUtils {

 public static APIError parseError(Response response) { Converter 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 = APIErrorUtils.parseError (response); if (error.getStatus () == 400) {….}

Котлин

 val gson = Gson() val type = object : TypeToken() {}.type var errorResponse: ErrorResponse? = gson.fromJson(errorBody.string(), type) 

Ява

 Gson gson = new Gson(); Type type = new TypeToken() {}.getType(); ErrorResponse errorResponse = gson.fromJson(errorBody.string(),type); 
Давайте будем гением компьютера.