【问题标题】:Handle an empty response in a JSONRequest with Volley使用 Volley 处理 JSONRequest 中的空响应
【发布时间】:2014-06-23 04:40:24
【问题描述】:

我在我的应用程序中使用Volley 发出POST 请求,在我的情况下,一个好的响应是带有空主体的201。我正在使用JSONRequest 拨打电话。

我的问题是错误响应处理程序被调用,因为响应为空。

以下是我的要求:

    Request request = new JsonRequest<Object>(Request.Method.POST, url, body, new Response.Listener<Object>() {

        @Override
        public void onResponse(Object response) {

        }
    }, new ErrorListener(context)) {

        @Override
        protected Response<Object> parseNetworkResponse(NetworkResponse response) {

            Log.d(TAG, "success!!!!!!");
            if (response.statusCode == 201)
                mListener.resetPasswordWasSent();
            return null;
        }

        @Override
        public Map<String, String> getHeaders() throws AuthFailureError {
            Map<String,String> params = new HashMap<String, String>();
            params.put("Content-Type","application/json");
            params.put("Accept", "application/json");
            return params;
        }
    };

    requestQueue.add(request);

我的parseNetworkResponse 函数被调用,然后ErrorListeneronResponse 方法永远不会被点击,因为我在ErrorListener 中得到了一个NullPointerException

我可以忽略错误侦听器中的NullPointerException,但我不想这样做。显然,我可以简单地在parseNetworkResponse 中发送我的回调,但我不想弹出任何错误。

有人知道我应该如何处理吗?

编辑: 这是堆栈跟踪:

05-06 09:44:19.586  27546-27560/com.threepoundhealth.euco E/Volley﹕ [1830] NetworkDispatcher.run: Unhandled exception java.lang.NullPointerException
    java.lang.NullPointerException
    at com.android.volley.NetworkDispatcher.run(NetworkDispatcher.java:126)

【问题讨论】:

  • 你究竟从哪里得到NullPointerException?在 Lib 内部还是在您的代码中?请添加 StackTrace。
  • @Simulant 请查看编辑
  • 我也有这个问题,我的 onError 从来没有被调用过
  • @CQM 我的 onError 被调用 - 我的 onResponse 没有被调用
  • 你初始化mListener了吗?

标签: android android-volley


【解决方案1】:

您可以尝试像这样破解。创建一个JsonObjectRequest子类,重写parseNetworkResponse方法并检查响应数据,如果是空的byte[], 用空 json {}byte[] 表示替换数据。

public class VolleyJsonRequest extends JsonObjectRequest {

    ...

    @Override
    protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
        try {
            if (response.data.length == 0) {
                byte[] responseData = "{}".getBytes("UTF8");
                response = new NetworkResponse(response.statusCode, responseData, response.headers, response.notModified);
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return super.parseNetworkResponse(response);
    }    
}

【讨论】:

  • 很好的答案!但是,NetworkResponse 构造函数已被弃用。为了方便,我写新的:response = new NetworkResponse(response.statusCode, responseData, response.notModified, response.networkTimeMs, response.allHeaders);
【解决方案2】:

您可以使用 StringRequest。将使用空字符串“”调用您的侦听器。

【讨论】:

  • 我希望能够发送 JSON,而不是使用我必须创建参数映射的 'getParams' 方法
  • 使用 JsonRequest 和 JsonBodyRequest 似乎会在空响应时出现错误。我想那是一个错误,但似乎 StringRequest 是在不触发 errorListener 的情况下处理空响应的唯一方法。
【解决方案3】:

如果预期没有响应,则可以考虑到这一点来扩展 JsonRequest

Kotlin 中的一个例子:

/**
 * A request containing a [JSONObject] body for a given URL, expecting an empty response.
 */
class JsonObjectRequestEmptyResponse
/**
 * Creates a new request.
 * @param method the HTTP method to use
 * @param url URL to fetch the JSON from
 * @param jsonRequest A [JSONObject] to post with the request.
 * @param listener Listener to receive a successful response
 * @param errorListener Error listener, or null to ignore errors.
 */
(method: Int, url: String, jsonRequest: JSONObject,
 listener: () -> Unit, errorListener: ErrorListener?) : JsonRequest<Unit>(method, url,
        jsonRequest.toString(), Listener<Unit> { _response -> listener() }, errorListener) {

    override fun parseNetworkResponse(response: NetworkResponse): Response<Unit> {
        if (response.data.isEmpty()) {
            return Response.success(Unit,
                    HttpHeaderParser.parseCacheHeaders(response))
        } else {
            return Response.error(VolleyError("unexpected data"))
        }
    }
}

【讨论】:

    【解决方案4】:

    我遇到了同样的问题并开发了一个全局解决方案来处理它(并且很少缺少 Volley 的功能),我将它发布在另一个线程上,但我认为它可以帮助许多正在寻找解决您的问题的人。

      /**
      * Created by laurentmeyer on 25/07/15.
      */
     public class GenericRequest<T> extends JsonRequest<T> {
    
         private final Gson gson = new Gson();
         private final Class<T> clazz;
         private final Map<String, String> headers;
         // Used for request which do not return anything from the server
         private boolean muteRequest = false;
    
         /**
          * Basically, this is the constructor which is called by the others.
          * It allows you to send an object of type A to the server and expect a JSON representing a object of type B.
          * The problem with the #JsonObjectRequest is that you expect a JSON at the end.
          * We can do better than that, we can directly receive our POJO.
          * That's what this class does.
          *
          * @param method:        HTTP Method
          * @param classtype:     Classtype to parse the JSON coming from the server
          * @param url:           url to be called
          * @param requestBody:   The body being sent
          * @param listener:      Listener of the request
          * @param errorListener: Error handler of the request
          * @param headers:       Added headers
          */
         private GenericRequest(int method, Class<T> classtype, String url, String requestBody,
                               Response.Listener<T> listener, Response.ErrorListener errorListener, Map<String, String> headers) {
             super(method, url, requestBody, listener,
                     errorListener);
             clazz = classtype;
             this.headers = headers;
             configureRequest();
         }
    
         /**
          * Method to be called if you want to send some objects to your server via body in JSON of the request (with headers and not muted)
          *
          * @param method:        HTTP Method
          * @param url:           URL to be called
          * @param classtype:     Classtype to parse the JSON returned from the server
          * @param toBeSent:      Object which will be transformed in JSON via Gson and sent to the server
          * @param listener:      Listener of the request
          * @param errorListener: Error handler of the request
          * @param headers:       Added headers
          */
         public GenericRequest(int method, String url, Class<T> classtype, Object toBeSent,
                               Response.Listener<T> listener, Response.ErrorListener errorListener, Map<String, String> headers) {
             this(method, classtype, url, new Gson().toJson(toBeSent), listener,
                     errorListener, headers);
         }
    
         /**
          * Method to be called if you want to send some objects to your server via body in JSON of the request (without header and not muted)
          *
          * @param method:        HTTP Method
          * @param url:           URL to be called
          * @param classtype:     Classtype to parse the JSON returned from the server
          * @param toBeSent:      Object which will be transformed in JSON via Gson and sent to the server
          * @param listener:      Listener of the request
          * @param errorListener: Error handler of the request
          */
         public GenericRequest(int method, String url, Class<T> classtype, Object toBeSent,
                               Response.Listener<T> listener, Response.ErrorListener errorListener) {
             this(method, classtype, url, new Gson().toJson(toBeSent), listener,
                     errorListener, new HashMap<String, String>());
         }
    
         /**
          * Method to be called if you want to send something to the server but not with a JSON, just with a defined String (without header and not muted)
          *
          * @param method:        HTTP Method
          * @param url:           URL to be called
          * @param classtype:     Classtype to parse the JSON returned from the server
          * @param requestBody:   String to be sent to the server
          * @param listener:      Listener of the request
          * @param errorListener: Error handler of the request
          */
         public GenericRequest(int method, String url, Class<T> classtype, String requestBody,
                               Response.Listener<T> listener, Response.ErrorListener errorListener) {
             this(method, classtype, url, requestBody, listener,
                     errorListener, new HashMap<String, String>());
         }
    
         /**
          * Method to be called if you want to GET something from the server and receive the POJO directly after the call (no JSON). (Without header)
          *
          * @param url:           URL to be called
          * @param classtype:     Classtype to parse the JSON returned from the server
          * @param listener:      Listener of the request
          * @param errorListener: Error handler of the request
          */
         public GenericRequest(String url, Class<T> classtype, Response.Listener<T> listener, Response.ErrorListener errorListener) {
             this(Request.Method.GET, url, classtype, "", listener, errorListener);
         }
    
         /**
          * Method to be called if you want to GET something from the server and receive the POJO directly after the call (no JSON). (With headers)
          *
          * @param url:           URL to be called
          * @param classtype:     Classtype to parse the JSON returned from the server
          * @param listener:      Listener of the request
          * @param errorListener: Error handler of the request
          * @param headers:       Added headers
          */
         public GenericRequest(String url, Class<T> classtype, Response.Listener<T> listener, Response.ErrorListener errorListener, Map<String, String> headers) {
             this(Request.Method.GET, classtype, url, "", listener, errorListener, headers);
         }
    
         /**
          * Method to be called if you want to send some objects to your server via body in JSON of the request (with headers and muted)
          *
          * @param method:        HTTP Method
          * @param url:           URL to be called
          * @param classtype:     Classtype to parse the JSON returned from the server
          * @param toBeSent:      Object which will be transformed in JSON via Gson and sent to the server
          * @param listener:      Listener of the request
          * @param errorListener: Error handler of the request
          * @param headers:       Added headers
          * @param mute:          Muted (put it to true, to make sense)
          */
         public GenericRequest(int method, String url, Class<T> classtype, Object toBeSent,
                               Response.Listener<T> listener, Response.ErrorListener errorListener, Map<String, String> headers, boolean mute) {
             this(method, classtype, url, new Gson().toJson(toBeSent), listener,
                     errorListener, headers);
             this.muteRequest = mute;
         }
    
         /**
          * Method to be called if you want to send some objects to your server via body in JSON of the request (without header and muted)
          *
          * @param method:        HTTP Method
          * @param url:           URL to be called
          * @param classtype:     Classtype to parse the JSON returned from the server
          * @param toBeSent:      Object which will be transformed in JSON via Gson and sent to the server
          * @param listener:      Listener of the request
          * @param errorListener: Error handler of the request
          * @param mute:          Muted (put it to true, to make sense)
          */
         public GenericRequest(int method, String url, Class<T> classtype, Object toBeSent,
                               Response.Listener<T> listener, Response.ErrorListener errorListener, boolean mute) {
             this(method, classtype, url, new Gson().toJson(toBeSent), listener,
                     errorListener, new HashMap<String, String>());
             this.muteRequest = mute;
    
         }
    
         /**
          * Method to be called if you want to send something to the server but not with a JSON, just with a defined String (without header and not muted)
          *
          * @param method:        HTTP Method
          * @param url:           URL to be called
          * @param classtype:     Classtype to parse the JSON returned from the server
          * @param requestBody:   String to be sent to the server
          * @param listener:      Listener of the request
          * @param errorListener: Error handler of the request
          * @param mute:          Muted (put it to true, to make sense)
          */
         public GenericRequest(int method, String url, Class<T> classtype, String requestBody,
                               Response.Listener<T> listener, Response.ErrorListener errorListener, boolean mute) {
             this(method, classtype, url, requestBody, listener,
                     errorListener, new HashMap<String, String>());
             this.muteRequest = mute;
    
         }
    
    
         @Override
         protected Response<T> parseNetworkResponse(NetworkResponse response) {
             // The magic of the mute request happens here
             if (muteRequest) {
                 if (response.statusCode >= 200 && response.statusCode <= 299) {
                     // If the status is correct, we return a success but with a null object, because the server didn't return anything
                     return Response.success(null, HttpHeaderParser.parseCacheHeaders(response));
                 }
             } else {
                 try {
                     // If it's not muted; we just need to create our POJO from the returned JSON and handle correctly the errors
                     String json = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
                     T parsedObject = gson.fromJson(json, clazz);
                     return Response.success(parsedObject, HttpHeaderParser.parseCacheHeaders(response));
                 } catch (UnsupportedEncodingException e) {
                     return Response.error(new ParseError(e));
                 } catch (JsonSyntaxException e) {
                     return Response.error(new ParseError(e));
                 }
             }
             return null;
         }
    
         @Override
         public Map<String, String> getHeaders() throws AuthFailureError {
             return headers != null ? headers : super.getHeaders();
         }
    
         private void configureRequest() {
             // Set retry policy
             // Add headers, for auth for example
             // ...
         }
     }
    

    这是另一个线程上的original answer

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-15
      • 2015-10-07
      • 1970-01-01
      • 2021-03-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多