Retrofit 2.0 역 직렬화 된 오류 응답을 얻는 방법.
내가 사용하고 개조 2.0.0-β1을 .
테스트에서 대체 시나리오가 있으며 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();
}
}
}
Retrofit 2.0 beta2에서 이것이 오류 응답을받는 방법입니다.
동기식
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 } });
나는 그것을 다음과 같이 해결했다.
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;
}
}
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);
에서는 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();
}
}
}
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();
}
}
}
@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();
}
}
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
// ...
}
이렇게하면 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();
}
}
나는 같은 문제에 직면했다. 개조로 해결했습니다. 보여 드릴게요 ...
오류 JSON 구조가 다음과 같은 경우
{
"error": {
"status": "The email field is required."
}
}
My ErrorRespnce.java
public class ErrorResponce {
@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 ErrorResponce parseError (Response<?> response){
Converter<ResponseBody , ErrorResponce> converter = ApiClient.getClient().responseBodyConverter(ErrorResponce.class , new Annotation[0]);
ErrorResponce errorResponce;
try{
errorResponce = converter.convert(response.errorBody());
}catch (IOException e){
return new ErrorResponce();
}
return errorResponce;
}
}
이제 retrofit 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){
ErrorResponce errorResponce = ErrorUtils.parseError(response);
Toast.makeText(MainActivity.this, ""+errorResponce.getError().getStatus(), Toast.LENGTH_SHORT).show();
}
}
@Override
public void onFailure(Call<RegistrationResponce> call, Throwable t) {
}
});
}
이제 토스트를 보여줄 수 있습니다.
이것은 Retrofit과 함께 OkHttp를 사용할 때 문제인 것 같으므로 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<RESPONSE, ERROR extends Throwable> implements Callback<RESPONSE> {
Class<ERROR> errorClass;
Retrofit retrofit;
TestCallback(Retrofit retrofit, Class<ERROR> errorClass) {
this.retrofit = retrofit;
this.errorClass = errorClass;
}
abstract void onSuccess(Call<RESPONSE> call, RESPONSE response);
@Override
public void onResponse(Call<RESPONSE> call, Response<RESPONSE> response) {
if (response.isSuccessful()) {
onSuccess(call, response.body());
return;
}
if (response.errorBody() != null) {
Converter<ResponseBody, ERROR> 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<User> call = retrofit.create(UsersApi.class).signUp(email, password);
call.enqueue(new TestCallback<User, TestError>(retrofit, TestError.class) {
@Override
public void onFailure(Call<User> call, Throwable t) {
if (t instanceof TestError) {
} else {
}
}
@Override
void onSuccess(Call<User> call, User response) {
// No need to check for isSuccessful() + no need to
// duplicate the same code for all of your handlers.
}
});
errorBody를 String으로 읽고 수동으로 json을 구문 분석하십시오.
if(!response.isSuccessful()) {
response.errorBody();
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());
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();
}
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) {
}
})
errorBody 값은 Retrofit에서 APIError 개체를 설정해야합니다. 따라서 아래 코드 구조를 사용할 수 있습니다.
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;
}
'programing tip' 카테고리의 다른 글
EC2 인스턴스에 액세스하려고 할 때 시간 초과의 가능한 이유 (0) | 2020.08.28 |
---|---|
운영 체제 정보 얻기 (0) | 2020.08.28 |
객체 초기화 구문 (0) | 2020.08.28 |
object == null 또는 null == object? (0) | 2020.08.28 |
도메인에서 JavaScript 쿠키를 만들고 하위 도메인에서 읽기 (0) | 2020.08.28 |