我正在使用Retrofit 2,我在这一行得到一个空指针异常:
RetrofitClient.APIError error = RetrofitClient.ErrorUtils.parseError(response, retrofit);
错误为空。更多细节:
这是API返回错误的格式:
{
"error": {
"message": "Incorrect credentials",
"statusCode": 401
}
}
这是我的登录回拨代码:
new Callback<LoginResponse>() {
@Override
public void onResponse(Response<LoginResponse> response, Retrofit retrofit) {
if (listener != null) {
if (response.isSuccess() && response.body() != null) {
User user = RetrofitUserToUserMapper.fromRetrofitUser(response.body().getLoginUser());
} else {
RetrofitClient.APIError error = RetrofitClient.ErrorUtils.parseError(response, retrofit);
listener.onUserLoginFailure(error.getErrorMessage()); // NPE - error is null
}
}
}
@Override
public void onFailure(Throwable t) {
if (listener != null) {
listener.onUserLoginFailure("");
}
}
}
这是我的Retrofit 2课程:
public class RetrofitClient {
public static final String API_ROOT = "http://example.com/api/v1/";
private static final String HEADER_OS_VERSION = "X-OS-Type";
private static final String HEADER_APP_VERSION = "X-App-Version";
private static final String HEADER_OS_VERSION_VALUE_ANDROID = "android";
private RetrofitClient() {
}
private static Retrofit INSTANCE;
public static Retrofit getInstance() {
if (INSTANCE == null) {
setupRestClient();
}
return INSTANCE;
}
public static void setupRestClient() {
OkHttpClient httpClient = new OkHttpClient();
addHeadersRequiredForAllRequests(httpClient, BuildConfig.VERSION_NAME);
INSTANCE = new Retrofit.Builder()
.baseUrl(API_ROOT)
.addConverterFactory(GsonConverterFactory.create())
.client(httpClient)
.build();
}
private static void addHeadersRequiredForAllRequests(OkHttpClient httpClient, final String appVersion) {
class RequestInterceptor implements Interceptor {
@Override
public com.squareup.okhttp.Response intercept(Chain chain) throws IOException {
Request request = chain.request().newBuilder()
.addHeader(HEADER_OS_VERSION, HEADER_OS_VERSION_VALUE_ANDROID)
.addHeader(HEADER_APP_VERSION, appVersion)
.build();
return chain.proceed(request);
}
}
httpClient.networkInterceptors().add(new RequestInterceptor());
}
public static class ErrorUtils {
public static APIError parseError(Response<?> response, Retrofit retrofit) {
Converter<ResponseBody, APIError> converter =
retrofit.responseConverter(APIError.class, new Annotation[0]);
APIError error;
try {
error = converter.convert(response.errorBody());
} catch (IOException e) {
e.printStackTrace();
return new APIError();
} catch (Exception e) {
e.printStackTrace();
return new APIError();
}
return error;
}
}
public static class APIError {
@SerializedName("error")
public
ErrorResponse loginError;
public ErrorResponse getLoginError() {
return loginError;
}
public String getErrorMessage() {
return loginError.message;
}
private class ErrorResponse {
@SerializedName("message")
private String message;
@SerializedName("statusCode")
public int statusCode;
public String getMessage() {
return message;
}
public int getStatusCode() {
return statusCode;
}
@Override
public String toString() {
return "LoginErrorResponseBody{" +
"message='" + getMessage() + '\'' +
", statusCode=" + statusCode +
'}';
}
public void setMessage(String message) {
this.message = message;
}
}
}
}
我从本教程获得了错误utils类,但稍微改了一下,因为他们的示例中的错误格式不同:
编辑:这是Converter
类:
/**
* Convert objects to and from their representation as HTTP bodies. Register a converter with
* Retrofit using {@link Retrofit.Builder#addConverterFactory(Factory)}.
*/
public interface Converter<F, T> {
T convert(F value) throws IOException;
abstract class Factory {
/**
* Create a {@link Converter} for converting an HTTP response body to {@code type} or null if it
* cannot be handled by this factory.
*/
public Converter<ResponseBody, ?> fromResponseBody(Type type, Annotation[] annotations) {
return null;
}
/**
* Create a {@link Converter} for converting {@code type} to an HTTP request body or null if it
* cannot be handled by this factory.
*/
public Converter<?, RequestBody> toRequestBody(Type type, Annotation[] annotations) {
return null;
}
}
}
答案 0 :(得分:5)
<强>更新强>
如果您想在下面使用自定义类而不是JSONObject
,可以参考以下内容:
自定义类:
public class ResponseError {
Error error;
class Error {
int statusCode;
String message;
}
}
将以下内容添加到WebAPIService
界面:
@GET("/api/geterror")
Call<ResponseError> getError2();
然后,在MainActivity.java
内:
Call<ResponseError> responseErrorCall = service.getError2();
responseErrorCall.enqueue(new Callback<ResponseError>() {
@Override
public void onResponse(Response<ResponseError> response, Retrofit retrofit) {
if (response.isSuccess() && response.body() != null){
Log.i(LOG_TAG, response.body().toString());
} else {
if (response.errorBody() != null){
RetrofitClient.APIError error = RetrofitClient.ErrorUtils.parseError(response, retrofit);
Log.e(LOG_TAG, error.getErrorMessage());
}
}
}
@Override
public void onFailure(Throwable t) {
Log.e(LOG_TAG, t.toString());
}
});
我刚刚使用我的网络服务测试了您的RetrofitClient
课程。我对你的APIError
类做了一个小的更新,如下所示(添加2个构造函数,实际上它们没有被调用):
public APIError(){
this.loginError = new ErrorResponse();
}
public APIError(int statusCode, String message) {
this.loginError = new ErrorResponse();
this.loginError.statusCode = statusCode;
this.loginError.message = message;
}
接口:
public interface WebAPIService {
@GET("/api/geterror")
Call<JSONObject> getError();
}
MainActivity:
// Retrofit 2.0-beta2
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(API_URL_BASE)
.addConverterFactory(GsonConverterFactory.create())
.build();
WebAPIService service = retrofit.create(WebAPIService.class);
Call<JSONObject> jsonObjectCall = service.getError();
jsonObjectCall.enqueue(new Callback<JSONObject>() {
@Override
public void onResponse(Response<JSONObject> response, Retrofit retrofit) {
if (response.isSuccess() && response.body() != null){
Log.i(LOG_TAG, response.body().toString());
} else {
if (response.errorBody() != null){
RetrofitClient.APIError error = RetrofitClient.ErrorUtils.parseError(response, retrofit);
Log.e(LOG_TAG, error.getErrorMessage());
}
}
}
@Override
public void onFailure(Throwable t) {
Log.e(LOG_TAG, t.toString());
}
});
我的网络服务(Asp.Net Web API):
根据您的JSON响应数据,我使用了以下代码:
[Route("api/geterror")]
public HttpResponseMessage GetError()
{
var detailError = new
{
message = "Incorrect credentials",
statusCode = 401
};
var myError = new
{
error = detailError
};
return Request.CreateResponse(HttpStatusCode.Unauthorized, myError);
}
它正在工作!希望它有所帮助!
答案 1 :(得分:1)
这个问题真的很奇怪,我无法解释这是怎么回事,我的解决方案真的很难看。
Retrofit实际上有两个转换器工厂,当我要求它转换响应时它返回的转换器工厂是null。
我在调试改造转换工厂的方法时发现了这一点。它返回的第二个工厂实际上成功完成了转换,我终于能够解析我的回复了。我仍然不知道我做错了什么。
这就是我丑陋的解决方案:
String errorMessage = "";
for (Converter.Factory factory : retrofit.converterFactories()) {
try {
LoginError loginError = (LoginError) factory.fromResponseBody(LoginError.class, new Annotation[0]).convert(errorBody);
if (loginError != null) {
errorMessage = loginError.error.message;
}
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
if (!TextUtils.isEmpty(errorMessage)) {
listener.onUserLoginFailure(errorMessage);
}
第一次在循环中我得到了一个NPE。我第二次收到错误消息
以下是我最终使用的错误类:
private class LoginError {
Error error;
class Error {
String message;
int statusCode;
}
}
编辑:我想可能是WildCard错误地改造了返回的转换器,这是我在这里传递的错误:
public static APIError parseError(Response<?> response, Retrofit retrofit) {
答案 2 :(得分:0)
@Override
public void onResponse(Response<User> response, Retrofit retrofit) {
if (response.isSuccess()) {
User user = response.body;
Log.e("User name", user.getName()); // do whatever you want
}else{
Converter<GlobalErrorObject> converter =
(Converter<GlobalErrorObject>) GsonConverterFactory.create().get(GlobalErrorObject.class);
try {
GlobalErrorObject globalErrorObject = converter.fromBody(response.errorBody());
Log.e("Error", globalErrorObject.getErrorMessage());
} catch (IOException e) {
e.printStackTrace();
}
}
}
**在我看来,GlobalErrorObject是一个代表JSON的pojo:
{
"errorCode": "API_INVALID_TOKEN",
"errorType": "API_ERROR",
"errorMessage": "Valid API Token required."
}
**以上代码对我有用。