如何在Volley请求中设置自定义标头

116

如何为Volley请求设置自定义标头?目前,已经有一种方式来设置POST请求的主体内容。我有一个简单的GET请求,但我需要传递自定义标头。我不知道JsonRequest类是否支持这样做。它是否可能实现?


请更改已接受的答案,当前接受的答案是不正确的。 - Esteban
13个回答

193

使用getParams()方法得到的答案是用于设置POST请求体数据的,但是标题中提出的问题是如何设置HTTP头信息,例如User-Agent。正如CommonsWare所说,您需要重写getHeaders()方法。以下是一些示例代码,它将User-Agent设置为“Nintendo Gameboy”,将Accept-Language设置为“fr”:

public void requestWithSomeHttpHeaders() {
    RequestQueue queue = Volley.newRequestQueue(this);
    String url = "http://www.somewebsite.com";
    StringRequest getRequest = new StringRequest(Request.Method.GET, url, 
        new Response.Listener<String>() 
        {
            @Override
            public void onResponse(String response) {
                // response
                Log.d("Response", response);
            }
        }, 
        new Response.ErrorListener() 
        {
            @Override
            public void onErrorResponse(VolleyError error) {
                // TODO Auto-generated method stub
                Log.d("ERROR","error => "+error.toString());
            }
        }
    ) {     
        @Override
        public Map<String, String> getHeaders() throws AuthFailureError { 
                Map<String, String>  params = new HashMap<String, String>();  
                params.put("User-Agent", "Nintendo Gameboy");  
                params.put("Accept-Language", "fr");

                return params;  
        }
    };
    queue.add(getRequest);

}

2
这个答案对于JSONObjectRequest也适用吗? JSONObjectRequest postRequest = new JSONObjectRequest......因为我正在做这个,但我的getHeaders()没有被调用...???我理解你正在创建一个匿名类并重写方法。我也是这样做的,只不过是使用JSONObjectRequest而不是StringRequest,但我的getHeaders()没有被调用。 - JDOaktown
我们能否在getHeaders()方法中添加“Cookie”?对于post请求也有效吗? - Arnab Banerjee
我应该把剩余的POST数据发送到哪里? - Fernando Torres

126

看起来你覆盖了public Map<String, String> getHeaders()定义在Request中的方法,以返回你所需的HTTP标头。


4
@JuanJoséMeleroGómez 链接已经失效。显示 404 未找到。 - Milon
这是另一个示例(页面末尾的片段):https://developer.android.com/training/volley/request-custom.html。类`GsonRequest`覆盖了方法`getHeaders()`,以返回一个_Map_,其中包含您在构造函数中传递的标头参数。 - Juan José Melero Gómez

31

如果你需要在不使用URL添加信息的情况下发布数据。

public Request post(String url, String username, String password, 
      Listener listener, ErrorListener errorListener) {
  JSONObject params = new JSONObject();
  params.put("user", username);
  params.put("pass", password);
  Request req = new Request(
     Method.POST,
     url,
     params.toString(),
     listener,
     errorListener
  );

  return req;
}
如果你想编辑请求头,这就是你需要做的:
// could be any class that implements Map
Map<String, String> mHeaders = new ArrayMap<String, String>();
mHeaders.put("user", USER);
mHeaders.put("pass", PASSWORD);
Request req = new Request(url, postBody, listener, errorListener) {
  public Map<String, String> getHeaders() {
    return mHeaders;
  }
}

45
我已经将其下投票,因为它完全不符合问题的要求。这是用于设置POST内容,而不是用于设置自定义HTTP标头,例如User-Agent。 Zh. Atanasov和CommonsWare的“getHeaders”答案是正确的。 - georgiecasey
4
我给这个答案投了反对票,因为它并不是用户所问的内容。 - Dharmendra Pratap Singh
1
这是用于添加内容参数而不是头部的,抱歉被踩了。它真的不应该成为被接受的答案。 - Juan Cortés
1
请您能否看一下我的问题?它与这个类似,但我无法正确解决:http://stackoverflow.com/a/37948318 - X09
1
我点赞了,因为这对我非常有帮助。 - Kheersagar patel

18
您可以查看此链接。它展示了如何获取/设置cookie,但cookie只是请求/响应中的一个头信息。您需要重写Volley的*Request类之一,并在getHeaders()中设置必要的头信息。
这里是链接源码:
public class StringRequest extends com.android.volley.toolbox.StringRequest {

private final Map<String, String> _params;

/**
 * @param method
 * @param url
 * @param params
 *            A {@link HashMap} to post with the request. Null is allowed
 *            and indicates no parameters will be posted along with request.
 * @param listener
 * @param errorListener
 */
public StringRequest(int method, String url, Map<String, String> params, Listener<String> listener,
        ErrorListener errorListener) {
    super(method, url, listener, errorListener);

    _params = params;
}

@Override
protected Map<String, String> getParams() {
    return _params;
}

/* (non-Javadoc)
 * @see com.android.volley.toolbox.StringRequest#parseNetworkResponse(com.android.volley.NetworkResponse)
 */
@Override
protected Response<String> parseNetworkResponse(NetworkResponse response) {
    // since we don't know which of the two underlying network vehicles
    // will Volley use, we have to handle and store session cookies manually
    MyApp.get().checkSessionCookie(response.headers);

    return super.parseNetworkResponse(response);
}

/* (non-Javadoc)
 * @see com.android.volley.Request#getHeaders()
 */
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
    Map<String, String> headers = super.getHeaders();

    if (headers == null
            || headers.equals(Collections.emptyMap())) {
        headers = new HashMap<String, String>();
    }

    MyApp.get().addSessionCookie(headers);

    return headers;
}

MyApp类:

代码如下:

public class MyApp extends Application {
    private static final String SET_COOKIE_KEY = "Set-Cookie";
    private static final String COOKIE_KEY = "Cookie";
    private static final String SESSION_COOKIE = "sessionid";

    private static MyApp _instance;
    private RequestQueue _requestQueue;
    private SharedPreferences _preferences;

    public static MyApp get() {
        return _instance;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        _instance = this;
            _preferences = PreferenceManager.getDefaultSharedPreferences(this);
        _requestQueue = Volley.newRequestQueue(this);
    }

    public RequestQueue getRequestQueue() {
        return _requestQueue;
    }


    /**
     * Checks the response headers for session cookie and saves it
     * if it finds it.
     * @param headers Response Headers.
     */
    public final void checkSessionCookie(Map<String, String> headers) {
        if (headers.containsKey(SET_COOKIE_KEY)
                && headers.get(SET_COOKIE_KEY).startsWith(SESSION_COOKIE)) {
                String cookie = headers.get(SET_COOKIE_KEY);
                if (cookie.length() > 0) {
                    String[] splitCookie = cookie.split(";");
                    String[] splitSessionId = splitCookie[0].split("=");
                    cookie = splitSessionId[1];
                    Editor prefEditor = _preferences.edit();
                    prefEditor.putString(SESSION_COOKIE, cookie);
                    prefEditor.commit();
                }
            }
    }

    /**
     * Adds session cookie to headers if exists.
     * @param headers
     */
    public final void addSessionCookie(Map<String, String> headers) {
        String sessionId = _preferences.getString(SESSION_COOKIE, "");
        if (sessionId.length() > 0) {
            StringBuilder builder = new StringBuilder();
            builder.append(SESSION_COOKIE);
            builder.append("=");
            builder.append(sessionId);
            if (headers.containsKey(COOKIE_KEY)) {
                builder.append("; ");
                builder.append(headers.get(COOKIE_KEY));
            }
            headers.put(COOKIE_KEY, builder.toString());
        }
    }

}

18

Kotlin中,

你需要重写getHeaders()方法,例如:

val volleyEnrollRequest = object : JsonObjectRequest(GET_POST_PARAM, TARGET_URL, PAYLOAD_BODY_IF_YOU_WISH,
            Response.Listener {
                // Success Part  
            },

            Response.ErrorListener {
                // Failure Part
            }
        ) {
            // Providing Request Headers

            override fun getHeaders(): Map<String, String> {
               // Create HashMap of your Headers as the example provided below

                val headers = HashMap<String, String>()
                headers["Content-Type"] = "application/json"
                headers["app_id"] = APP_ID
                headers["app_key"] = API_KEY

                return headers
            }
        }

3
这是我能找到的唯一一个有关在 Kotlin 中覆盖标题的资源。谢谢! - Mathew Sonke
1
@MathewSonke 我感同身受。顺便说一下,在Android中尝试使用Retrofit进行网络编程。 - devDeejay

6
我也在寻找这个问题的解决方案。 请参阅以下内容:http://developer.android.com/training/volley/request.html 直接使用ImageRequest而不是ImageLoader好吗?似乎ImageLoader也在内部使用它。除了ImageLoader的缓存支持,它是否会错过其他重要功能?
ImageView mImageView;
String url = "http://i.imgur.com/7spzG.png";
mImageView = (ImageView) findViewById(R.id.myImage);
...

// Retrieves an image specified by the URL, displays it in the UI.
mRequestQueue = Volley.newRequestQueue(context);;
ImageRequest request = new ImageRequest(url,
  new Response.Listener() {
      @Override
      public void onResponse(Bitmap bitmap) {
          mImageView.setImageBitmap(bitmap);
      }
  }, 0, 0, null,
  new Response.ErrorListener() {
      public void onErrorResponse(VolleyError error) {
          mImageView.setImageResource(R.drawable.image_load_error);
      }
  })   {
    @Override
    public Map<String, String> getHeaders() throws AuthFailureError {
        Map<String, String> params = new Map<String, String>();
        params.put("User-Agent", "one");
        params.put("header22", "two");

        return params;
    };
mRequestQueue.add(request);

如果您能指出为什么认为这个问题是错误的或者所要求的解决方案有问题,而不仅仅是打一个“-1”,那将会非常感激且更有帮助。 - lannyf
1
Map是抽象的。应该使用HashMap。 - superuserdo

5

试试这个

{
    @Override
       public Map<String, String> getHeaders() throws AuthFailureError {
           String bearer = "Bearer ".concat(token);
            Map<String, String> headersSys = super.getHeaders();
            Map<String, String> headers = new HashMap<String, String>();
            headersSys.remove("Authorization");
            headers.put("Authorization", bearer);
            headers.putAll(headersSys);
            return headers;
       }
};

4
您可以自定义一个Request类,继承StringRequest,并覆盖其中的getHeaders()方法,如下所示:
public class CustomVolleyRequest extends StringRequest {

    public CustomVolleyRequest(int method, String url,
                           Response.Listener<String> listener,
                           Response.ErrorListener errorListener) {
        super(method, url, listener, errorListener);
    }

    @Override
    public Map<String, String> getHeaders() throws AuthFailureError {
        Map<String, String> headers = new HashMap<>();
        headers.put("key1","value1");
        headers.put("key2","value2");
        return headers;
    }
}

1
作为补充,我想分享一些关于“Content-Type”的发现: 在顶部
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> params = new HashMap<String, String>();
.
.
.
return params;
}

我不得不添加:

@Override
public String getBodyContentType() {
return /*(for exmaple)*/ "application/json";
}

不要问我为什么,我只是认为这可能有助于一些无法正确设置Content-Type的人。

1
public class CustomJsonObjectRequest  extends JsonObjectRequest
{
    public CustomJsonObjectRequest(int method, String url, JSONObject jsonRequest,Response.Listener listener, Response.ErrorListener errorListener)
    {
        super(method, url, jsonRequest, listener, errorListener);
    }


@Override
public Map getHeaders() throws AuthFailureError {
    Map headers = new HashMap();
    headers.put("AppId", "xyz");

    return headers;
}

}

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