Android:如何使用Volley处理来自服务器的消息错误?


71

我正在将Volley用于我的Android应用程序以从服务器中获取数据。除了处理我的服务器中的错误时,它都能正常工作。发生错误时,我的服务器将发送以下响应:

{
    "status": 400,
    "message": "Errors (2): A name is required- Julien is already used. Not creating."
}

我的目标是获取消息,然后将其显示在中Toast。我遵循了一些示例,以了解如何执行此操作,但是它不起作用。

有我的错误监听器:

public void onErrorResponse(VolleyError error) {
            int  statusCode = error.networkResponse.statusCode;
            NetworkResponse response = error.networkResponse;

            Log.d("testerror",""+statusCode+" "+response.data);
            // Handle your error types accordingly.For Timeout & No connection error, you can show 'retry' button.
            // For AuthFailure, you can re login with user credentials.
            // For ClientError, 400 & 401, Errors happening on client side when sending api request.
            // In this case you can check how client is forming the api and debug accordingly.
            // For ServerError 5xx, you can do retry or handle accordingly.
            if( error instanceof NetworkError) {
            } else if( error instanceof ClientError) {
            } else if( error instanceof ServerError) {
            } else if( error instanceof AuthFailureError) {
            } else if( error instanceof ParseError) {
            } else if( error instanceof NoConnectionError) {
            } else if( error instanceof TimeoutError) {
            }
            showProgress(false);
            mPasswordView.setError(getString(R.string.error_incorrect_password));
            mPasswordView.requestFocus();

        }

我的调试器的结果是: testerror:400 [B @ 430b8d60

编辑:此外,我的error.getMessage()为null。

所以我不明白为什么我的变量response.data不是服务器的响应。

如果有人知道我如何从服务器获取消息,那就太好了。

谢谢,

Answers:


154

我已经实现了与此类似的东西,并且它相对简单。您的日志消息正在打印出看起来像乱码的内容,因为response.data它实际上是一个字节数组-不是a String。另外,aVolleyError实际上只是一个扩展Exception,因此Exception .getMessage()可能不会返回您要查找的内容,除非您覆盖VolleyError扩展Request类中用于解析您的解析方法。处理此问题的一种非常基本的方法是执行以下操作:

//In your extended request class
@Override
protected VolleyError parseNetworkError(VolleyError volleyError){
        if(volleyError.networkResponse != null && volleyError.networkResponse.data != null){
                VolleyError error = new VolleyError(new String(volleyError.networkResponse.data));
                volleyError = error;
            }

        return volleyError;
    }
}

If you add this to your extended Request classes, your getMessage() should at least not return null. I normally don't really bother with this, though, since it's easy enough to do it all from within your onErrorResponse(VolleyError e) method.

You should use a JSON library to simplify things -- I use Gson for example or you could use Apache's JSONObjects which shouldn't require an additional external library. The first step is to get the response JSON sent from your server as a String (in a similar fashion to what I just demonstrated), next you can optionally convert it to a JSONObject (using either apache's JSONObjects and JsonArrays, or another library of your choice) or just parse the String yourself. After that, you just have to display the Toast.

Here's some example code to get you started:

public void onErrorResponse(VolleyError error) {
     String json = null;

     NetworkResponse response = error.networkResponse;
     if(response != null && response.data != null){
         switch(response.statusCode){
             case 400:
                  json = new String(response.data);
                  json = trimMessage(json, "message");
                  if(json != null) displayMessage(json);
                  break;
             }
            //Additional cases
     }
}

public String trimMessage(String json, String key){
    String trimmedString = null;

    try{
        JSONObject obj = new JSONObject(json);
        trimmedString = obj.getString(key);
    } catch(JSONException e){
        e.printStackTrace();
        return null;
    }

    return trimmedString;
}

//Somewhere that has access to a context
public void displayMessage(String toastString){
    Toast.makeText(context, toastString, Toast.LENGTH_LONG).show();
}

1
You are exactly write :) I found the answer just before your post. And I also create a class MessageServer with 2 attributs : status and message like this I use gson to convert my string into this class and then get the message.
FlavienBert

2
Hi, I just want to add that the @Override approach you provided (which I think is very elegant) isn't flexible if you want to check for networkresponse data, since you discard that information and create a new VolleyError with a String message in it. Unfortunately it's not possible to create an instance with both the original networkResponse and a custom message.
fiipi

1
It isn't impossible, it just requires a subclassed VolleyError implementation, and in many cases checking the subclassed type of your VolleyError is necessary in onErrorResponse(VolleyError e) anyways.
Submersed

if I use json = new String(response.data); I get error "Unterminated array at character x" (where x is index in byte[] data). This error is resolved by passing UTF-8 json = new String(response.data, "UTF-8"); // this works well
Vadiraj Purohit

This works, but I think it's very important to note that when you create a new VolleyError with a message, you lose other attributes of the original error such as networkResponse. If you don't want any of the complex error handling in your second example, you either have to be okay with this loss or create a subclass of VolleyError and override its getMessage() method.
yuval

6

try this class to handle all erros

public class VolleyErrorHelper {
        /**
         * Returns appropriate message which is to be displayed to the user
         * against the specified error object.
         *
         * @param error
         * @param context
         * @return
         */

        public static String getMessage (Object error , Context context){
            if(error instanceof TimeoutError){
                return context.getResources().getString(R.string.timeout);
            }else if (isServerProblem(error)){
                return handleServerError(error ,context);

            }else if(isNetworkProblem(error)){
                return context.getResources().getString(R.string.nointernet);
            }
            return context.getResources().getString(R.string.generic_error);

        }

        private static String handleServerError(Object error, Context context) {

            VolleyError er = (VolleyError)error;
            NetworkResponse response = er.networkResponse;
            if(response != null){
                switch (response.statusCode){

                    case 404:
                    case 422:
                    case 401:
                        try {
                            // server might return error like this { "error": "Some error occured" }
                            // Use "Gson" to parse the result
                            HashMap<String, String> result = new Gson().fromJson(new String(response.data),
                                    new TypeToken<Map<String, String>>() {
                                    }.getType());

                            if (result != null && result.containsKey("error")) {
                                return result.get("error");
                            }

                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        // invalid request
                        return ((VolleyError) error).getMessage();

                    default:
                        return context.getResources().getString(R.string.timeout);
                }
            }

            return context.getResources().getString(R.string.generic_error);
        }

        private static boolean isServerProblem(Object error) {
            return (error instanceof ServerError || error instanceof AuthFailureError);
        }

        private static boolean isNetworkProblem (Object error){
            return (error instanceof NetworkError || error instanceof NoConnectionError);
        }

This answer has a better error handling for Volley
blueware

Just a small suggestion: NoConnectionError is a subclass of NetworkError, so your OR operation will be redundant in isNetworkProblem() method. You might as well just check for NetworkError.
Uncaught Exception
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.