Volley - POST/GET 参数

88

我看了Google IO 2013有关Volley的会话,考虑切换到Volley。 Volley是否支持向请求中添加POST/GET参数?如果是,请问如何添加?


2
我没有看完整个主题演讲,但我相信GET请求只需将参数添加到URL中即可实现(例如:http://example.com?param1=val1&param2=val2)。 - JJJollyjim
目前似乎还没有文档,但您可以在此处检查源代码 https://android.googlesource.com/platform/frameworks/volley/+/master - MM.
@JJ56 - 对啊,但是POST参数呢?我看了源代码,但没有找到任何与POST参数相关的内容。 - Ziem
1
我感到很惭愧地说这句话。但是,如果你在这里询问为什么你的请求在服务器上没有主体,请确保你使用POST/PUT方法。我猜我只是累了。希望这条评论能帮助任何人或让我感觉更好。 - Alwin Kesler
8个回答

92

对于GET参数,有两种选择:

第一种:如下评论所建议的,您可以使用String将参数占位符替换为其值,例如:

String uri = String.format("http://somesite.com/some_endpoint.php?param1=%1$s&param2=%2$s",
                           num1,
                           num2);

StringRequest myReq = new StringRequest(Method.GET,
                                        uri,
                                        createMyReqSuccessListener(),
                                        createMyReqErrorListener());
queue.add(myReq);

其中num1和num2是包含你的值的String变量。

第二点: 如果你正在使用新的外部HttpClient(例如4.2.x),你可以使用URIBuilder来构建你的Uri。优点是如果你的uri字符串中已经有参数,那么将其传递给URIBuilder并使用ub.setQuery(URLEncodedUtils.format(getGetParams(), "UTF-8"));来添加你的额外参数会更容易。这样你就不必检查"?"是否已经添加到uri中或者遗漏一些"&",从而消除潜在错误的源头。

对于POST参数,有时候直接这样做可能比接受的答案更容易:

StringRequest myReq = new StringRequest(Method.POST,
                                        "http://somesite.com/some_endpoint.php",
                                        createMyReqSuccessListener(),
                                        createMyReqErrorListener()) {

    protected Map<String, String> getParams() throws com.android.volley.AuthFailureError {
        Map<String, String> params = new HashMap<String, String>();
        params.put("param1", num1);
        params.put("param2", num2);
        return params;
    };
};
queue.add(myReq);
例如,只需重写getParams()方法。
您可以在Andorid Volley Examples项目中找到一个可工作的示例(以及许多其他基本Volley示例)。

2
仅图像被缓存。请检查是否有代理拦截了您的请求。如果请求具有完全相同的URL,并且可能代理只返回第一个结果。 - Ognyan
也许你是对的。但是怎么解决呢? 在php文件中加入"header("Cache-Control: no-cache");"这行代码会有帮助吗? - SkyWalker
2
有一个名为“HTTP资源测试”的Firefox插件,可以让您向Web服务器发送请求。它非常适用于像这样的测试案例。只需输入您的URL(如果有POST参数),并查看服务器对多个连续请求的响应是什么。如果再次获得相同的响应,则结果很可能已被缓存。在这种情况下,您可以检查返回的标头以检查是否存在代理标头。关于“无缓存”-是的,它应该有效。 - Ognyan
2
我建议您打开一个单独的问题,以便其他人可以加入并提供帮助。请提供您使用的堆栈是HURL还是HttpClient,以及您正在测试的Android版本信息。请在此处放置新问题的链接,以便其他人可以跟随讨论。 - Ognyan
3
如果您将此作为新问题提问,可能会获得更好的答案,但简而言之:POST请求允许您发送文件和其他较大的数据,无法适应GET请求。此外,使用GET请求存在安全风险,因为URL 可能会被记录在服务器上,从而暴露敏感数据。 - Ognyan
显示剩余7条评论

65

在扩展了Request的Request类中,覆盖getParams()方法。对于头部信息也是一样的,覆盖getHeaders()方法。

如果您查看Volley测试中TestRequest.java中的PostWithBody类,您会找到一个例子。它大致如下所示:

public class LoginRequest extends Request<String> {

    // ... other methods go here

    private Map<String, String> mParams;

    public LoginRequest(String param1, String param2, Listener<String> listener, ErrorListener errorListener) {
        super(Method.POST, "http://test.url", errorListener);
        mListener = listener;
        mParams = new HashMap<String, String>();
        mParams.put("paramOne", param1);
        mParams.put("paramTwo", param2);

    }

    @Override
    public Map<String, String> getParams() {
        return mParams;
    }
}

Evan Charlton很友好地做了一个快速示例项目,向我们展示了如何使用volley。 https://github.com/evancharlton/folly/


76
请注意,默认情况下,getParams 方法仅在 POST 或 PUT 请求中被调用,而不会在 GET 请求中被调用。请参考 Ogre_BGR 的回答。 - Itai Hanski
3
真想不到直到现在我才注意到这个。 - Afzal N
@AfzalivE,你有没有想法在使用以上代码时如何使用OAuth签署API请求? - Bipin Vayalu
2
如果您正在连接Google API,您应该能够使用com.android.volley.AndroidAuthenticator。 - Pierre-Antoine
@Harsha Ognyan的回答涵盖了这个问题。 - Afzal N
显示剩余5条评论

23

CustomRequest是解决Volley的JSONObjectRequest无法像StringRequest一样发布参数的方法。

这里是辅助类,允许添加参数:

    import java.io.UnsupportedEncodingException;
    import java.util.Map;    
    import org.json.JSONException;
    import org.json.JSONObject;    
    import com.android.volley.NetworkResponse;
    import com.android.volley.ParseError;
    import com.android.volley.Request;
    import com.android.volley.Response;
    import com.android.volley.Response.ErrorListener;
    import com.android.volley.Response.Listener;
    import com.android.volley.toolbox.HttpHeaderParser;

    public class CustomRequest extends Request<JSONObject> {

    private Listener<JSONObject> listener;
    private Map<String, String> params;

    public CustomRequest(String url, Map<String, String> params,
            Listener<JSONObject> reponseListener, ErrorListener errorListener) {
        super(Method.GET, url, errorListener);
        this.listener = reponseListener;
        this.params = params;
    }

    public CustomRequest(int method, String url, Map<String, String> params,
            Listener<JSONObject> reponseListener, ErrorListener errorListener) {
        super(method, url, errorListener);
        this.listener = reponseListener;
        this.params = params;
    }

    protected Map<String, String> getParams()
            throws com.android.volley.AuthFailureError {
        return params;
    };

    @Override
    protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
        try {
            String jsonString = new String(response.data,
                    HttpHeaderParser.parseCharset(response.headers));
            return Response.success(new JSONObject(jsonString),
                    HttpHeaderParser.parseCacheHeaders(response));
        } catch (UnsupportedEncodingException e) {
            return Response.error(new ParseError(e));
        } catch (JSONException je) {
            return Response.error(new ParseError(je));
        }
    }

    @Override
    protected void deliverResponse(JSONObject response) {
        // TODO Auto-generated method stub
        listener.onResponse(response);
    }

}

感谢 Greenchiu 的帮助。


3
非常感谢,我搜索了几个小时才找到这个解决方案。很奇怪 JSONObjectReuqest 的 getParams() 函数重写不起作用。 - Walid Ammar
@MohammadWalid FYI,请阅读此文https://dev59.com/JWQn5IYBdhLWcg3wW2D3#18863395,并尝试使用Retrofit!Volley和Retrofit都可以与OkHttp一起使用! - LOG_TAG
我尝试了这个解决方案,但对我没有用。getParams() 没有被调用。 - Matt

10

这个辅助类管理 GETPOST 请求的参数:

import java.io.UnsupportedEncodingException;
import java.util.Iterator;
import java.util.Map;    

import org.json.JSONException;
import org.json.JSONObject;

import com.android.volley.NetworkResponse;
import com.android.volley.ParseError;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.Response.ErrorListener;
import com.android.volley.Response.Listener;
import com.android.volley.toolbox.HttpHeaderParser;

public class CustomRequest extends Request<JSONObject> {
    private int mMethod;
    private String mUrl;
    private Map<String, String> mParams;
    private Listener<JSONObject> mListener;

    public CustomRequest(int method, String url, Map<String, String> params,
            Listener<JSONObject> reponseListener, ErrorListener errorListener) {
        super(method, url, errorListener);
        this.mMethod = method;
        this.mUrl = url;
        this.mParams = params;
        this.mListener = reponseListener;
    }

@Override
public String getUrl() {
    if(mMethod == Request.Method.GET) {
        if(mParams != null) {
            StringBuilder stringBuilder = new StringBuilder(mUrl);
            Iterator<Map.Entry<String, String>> iterator = mParams.entrySet().iterator();
            int i = 1;
            while (iterator.hasNext()) {
                Map.Entry<String, String> entry = iterator.next();
                if (i == 1) {
                    stringBuilder.append("?" + entry.getKey() + "=" + entry.getValue());
                } else {
                    stringBuilder.append("&" + entry.getKey() + "=" + entry.getValue());
                }
                iterator.remove(); // avoids a ConcurrentModificationException
                i++;
            }
            mUrl = stringBuilder.toString();
        }
    }
    return mUrl;
}

    @Override
    protected Map<String, String> getParams()
            throws com.android.volley.AuthFailureError {
        return mParams;
    };

    @Override
    protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
        try {
            String jsonString = new String(response.data,
                    HttpHeaderParser.parseCharset(response.headers));
            return Response.success(new JSONObject(jsonString),
                    HttpHeaderParser.parseCacheHeaders(response));
        } catch (UnsupportedEncodingException e) {
            return Response.error(new ParseError(e));
        } catch (JSONException je) {
            return Response.error(new ParseError(je));
        }
    }

    @Override
    protected void deliverResponse(JSONObject response) {
        // TODO Auto-generated method stub
        mListener.onResponse(response);
    }
}

这解决了向GET方法添加参数的问题,谢谢! - pkarc
2
在我们的情况下,迭代器方法破坏了我们传递给函数的原始Map params对象。似乎Volley内部调用GetUrl多次。最终我们采取了一个经典的foreach方法,就像在另一个答案中发布的那样。希望这能帮助到来到这里的人 :) - Paolo Casciello
如何使用带有3个参数的辅助类进行GET请求调用 - kgandroid
@kgandroid,创建一个包含键和值的Map<String, String>。例如: Map<String, String> params = new HashMap<String, String>(); params.put("param1", "value1"); params.put("param2", "value2"); params.put("param3", "value3"); - Andrea Motto

6

处理GET参数,我参考了Andrea Motto的解决方案进行迭代。

问题是Volley多次调用GetUrl,他的解决方案使用迭代器破坏了原始的Map对象。随后的Volley内部调用具有空的params对象。

我还添加了参数的编码。

这是一个内联用法(无子类)。

public void GET(String url, Map<String, String> params, Response.Listener<String> response_listener, Response.ErrorListener error_listener, String API_KEY, String stringRequestTag) {
    final Map<String, String> mParams = params;
    final String mAPI_KEY = API_KEY;
    final String mUrl = url;

    StringRequest stringRequest = new StringRequest(
            Request.Method.GET,
            mUrl,
            response_listener,
            error_listener
    ) {
        @Override
        protected Map<String, String> getParams() {
            return mParams;
        }

        @Override
        public String getUrl() {
            StringBuilder stringBuilder = new StringBuilder(mUrl);
            int i = 1;
            for (Map.Entry<String,String> entry: mParams.entrySet()) {
                String key;
                String value;
                try {
                    key = URLEncoder.encode(entry.getKey(), "UTF-8");
                    value = URLEncoder.encode(entry.getValue(), "UTF-8");
                    if(i == 1) {
                        stringBuilder.append("?" + key + "=" + value);
                    } else {
                        stringBuilder.append("&" + key + "=" + value);
                    }
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                i++;

            }
            String url = stringBuilder.toString();

            return url;
        }

        @Override
        public Map<String, String> getHeaders() {
            Map<String, String> headers = new HashMap<>();
            if (!(mAPI_KEY.equals(""))) {
                headers.put("X-API-KEY", mAPI_KEY);
            }
            return headers;
        }
    };

    if (stringRequestTag != null) {
        stringRequest.setTag(stringRequestTag);
    }

    mRequestQueue.add(stringRequest);
}

这个函数使用头部传递APIKEY并设置一个TAG到请求中,以便在完成之前取消它。

希望这可以帮助你。


3
这可能对你有所帮助...
private void loggedInToMainPage(final String emailName, final String passwordName) {

    String tag_string_req = "req_login";
    StringRequest stringRequest = new StringRequest(Request.Method.POST, "http://localhost/index", new Response.Listener<String>() {
        @Override
        public void onResponse(String response) {
            Log.d(TAG, "Login Response: " + response.toString());
            try {
                JSONObject jsonObject = new JSONObject(response);
                Boolean error = jsonObject.getBoolean("error");
                if (!error) {

                    String uid = jsonObject.getString("uid");
                    JSONObject user = jsonObject.getJSONObject("user");
                    String email = user.getString("email");
                    String password = user.getString("password");


                    session.setLogin(true);
                    Intent intent = new Intent(getApplicationContext(), MainActivity.class);
                    startActivity(intent);
                    finish();
                    Toast.makeText(getApplicationContext(), "its ok", Toast.LENGTH_SHORT).show();
                }
            } catch (JSONException e) {
                e.printStackTrace();
            }

        }
    }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError volleyError) {
            System.out.println("volley Error .................");
        }
    }) {
        @Override
        protected Map<String, String> getParams() throws AuthFailureError {
            Map<String, String> params = new HashMap<String, String>();
            params.put("tag", "login");
            params.put("email", emailName);
            params.put("password", passwordName);
            return params;
        }
    };


    MyApplication.getInstance().addToRequestQueue(stringRequest,tag_string_req);
}

2

针对未来的读者

我喜欢使用 Volley 进行开发。为了节约开发时间,我尝试编写了一个小巧方便的库Gloxey Netwok Manager,用于在我的项目中设置 Volley。它包括 JSON 解析器和其他一些有助于检查网络可用性的方法。

使用 ConnectionManager.class,其中提供了不同的方法用于进行Volley StringVolley JSON请求。您可以使用或不使用标头进行GET、PUT、POST、DELETE请求。您可以在这里阅读完整文档。

只需将此行放入您的 gradle 文件中即可。

  dependencies { 

       compile 'io.gloxey.gnm:network-manager:1.0.1'
   }

Volley StringRequest

使用GET方法(不带头信息)

    ConnectionManager.volleyStringRequest(context, isDialog, progressDialogView, requestURL, volleyResponseInterface);

如何使用?

     Configuration                Description

     Context                      Context 
     isDialog                     If true dialog will appear, otherwise not.
     progressView                 For custom progress view supply your progress view id and make isDialog true. otherwise pass null. 
     requestURL                   Pass your API URL.  
     volleyResponseInterface      Callback for response.  

例子

    ConnectionManager.volleyStringRequest(this, false, null, "url", new VolleyResponse() {
    @Override
    public void onResponse(String _response) {

        /**
         * Handle Response
         */
    }

    @Override
     public void onErrorResponse(VolleyError error) {

        /**
         * handle Volley Error
         */
    }

    @Override
    public void isNetwork(boolean connected) {

        /**
         * True if internet is connected otherwise false
         */
    }
});

Volley StringRequest

POST/PUT/DELETE方法(不带标头)

    ConnectionManager.volleyStringRequest(context, isDialog, progressDialogView, requestURL, requestMethod, params, volleyResponseInterface);

示例

Use Method : Request.Method.POST
             Request.Method.PUT
             Request.Method.DELETE

Your params : 

HashMap<String, String> params = new HashMap<>();
params.put("param 1", "value");
params.put("param 2", "value");

ConnectionManager.volleyStringRequest(this, true, null, "url", Request.Method.POST, params, new VolleyResponse() {
    @Override
    public void onResponse(String _response) {

        /**
         * Handle Response
         */
    }

    @Override
    public void onErrorResponse(VolleyError error) {

        /**
         * handle Volley Error
         */
    }

    @Override
    public void isNetwork(boolean connected) {

        /**
         * True if internet is connected otherwise false
         */
    }
});

奖励

Gloxey JSON解析器

欢迎使用gloxey json解析器来解析您的api响应。

  YourModel yourModel = GloxeyJsonParser.getInstance().parse(stringResponse, YourModel.class);

例子

ConnectionManager.volleyStringRequest(this, false, null, "url", new VolleyResponse() {
    @Override
    public void onResponse(String _response) {

        /**
         * Handle Response
         */

         try {

          YourModel yourModel = GloxeyJsonParser.getInstance().parse(_response, YourModel.class);

            } catch (Exception e) {
                e.printStackTrace();
            }

    }

    @Override
     public void onErrorResponse(VolleyError error) {

        /**
         * handle Volley Error
         */
         if (error instanceof TimeoutError || error instanceof NoConnectionError) {

                showSnackBar(parentLayout, getString(R.string.internet_not_found), getString(R.string.retry), new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {

                     //handle retry button

                    }
                });

            } else if (error instanceof AuthFailureError) {
            } else if (error instanceof ServerError) {
            } else if (error instanceof NetworkError) {
            } else if (error instanceof ParseError) {
            }

    }

    @Override
    public void isNetwork(boolean connected) {

        /**
         * True if internet is connected otherwise false
         */
          if (!connected) {
                showSnackBar(parentLayout, getString(R.string.internet_not_found), getString(R.string.retry), new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        //Handle retry button
                    }
                });
    }
});


     public void showSnackBar(View view, String message) {
            Snackbar.make(view, message, Snackbar.LENGTH_LONG).show();
     }

     public void showSnackBar(View view, String message, String actionText, View.OnClickListener onClickListener) {
            Snackbar.make(view, message, Snackbar.LENGTH_LONG).setAction(actionText, onClickListener).show();
     }

但它支持Method.Get吗? - David Kariuki
是的,它有,请查看详细信息。https://github.com/adnanbinmustafa/Gloxey-Network-Manager - Adnan Bin Mustafa

0
为了提供POST参数,请将您的参数作为JSONObject发送到JsonObjectRequest构造函数中。第三个参数接受一个JSONObject,用于请求正文。
JSONObject paramJson = new JSONObject();

paramJson.put("key1", "value1");
paramJson.put("key2", "value2");


JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.POST,url,paramJson,
    new Response.Listener<JSONObject>() {
        @Override
        public void onResponse(JSONObject response) {

        }
    },
    new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {

        }
    });
requestQueue.add(jsonObjectRequest);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接