HTTP 协议再解

HTTP 协议头解析

Request

由四部分组成,包括:请求行、请求头(header) 、空行和请求数据。

http请求信息结构

Get 请求例子,如下:

1
2
3
4
5
GET /api/channelInfo?platformType=androidPhone&adapterNo=7.4.7&pid=&recommendNo=3,2&positionId=&pageSize=20&protocol=1.0.2 HTTP/1.1
User-Agent: Dalvik/2.1.0 (Linux; U; Android 7.0; SM-G9200 Build/NRD90M)
Host: vcis.ifeng.com
Connection: Keep-Alive
Accept-Encoding: gzip

第一部分: 请求行,用来说明请求类型,要访问的资源以及所使用的HTTP版本.

第二部分:请求头部,紧接着请求行(即第一行)之后的部分,用来说明服务器要使用的附加信息

第三部分:空行,请求头部后面的空行是必须的

第四部分:请求数据也叫主体,可以添加任意的其他数据。

Post 请求列子, 如下:

1
2
3
4
5
6
7
8
9
10
POST /appsta.js HTTP/1.1
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
User-Agent: Dalvik/2.1.0 (Linux; U; Android 7.0; SM-G9200 Build/NRD90M)
Host: stadig0.ifeng.com
Connection: Keep-Alive
Accept-Encoding: gzip
Content-Length: 456
// 请求数据
publishid=30007&session=2017-12-28%2B17%3A07%3A43%23adinfo%23ainfo%3D10019143%3Aiis_feather_19143_568332_1514450844546_kmfoh1%3A20%24first%3Dno&pid=358108062984272&datatype=videoapp&mos=android_7.0&sp=cucc&re=2560*1440&sver=V1&net=wifi&userid=&openudid=358108062984272&isupdate=1&logintime=1514436698&ua=sm-g9200%2Csamsung&uid=15144366981231380&userkey=f0663a966d82c57e7eb8976597e195f0&softversion=7.4.7&sig=3fc8rfc43rfcinfc4ac8e2thos0733d43wtc02gh3f076wr&

Response

由 状态行、消息报头、空行和响应政文组成。

1
2
3
4
5
6
7
8
9
10
11
12
13
//状态行
HTTP/1.1 200 OK
消息报头
Server: nginx/1.8.0
Date: Thu, 28 Dec 2017 08:47:24 GMT
Content-Length: 172
Pragma: No-cache
Cache-Control: no-cache
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Proxy-Connection: Keep-alive
//--空行--
//响应正文
{"result":{"timestamp":1514450844361,"requesthost":"api.exc.mob.com","requestport":80,"enable":1,"filter":[],"upconf":{"crash":"1","sdkerr":"0","apperr":"0"}},"status":200}

Get 和 Post 的区别:

  • GET提交的数据会放在URL之后,以?分割URL和传输数据,参数之间以&相连,如stadig.ifeng.com?datatype=videoapp&mos=android_7.0. POST方法是把提交的数据放在HTTP包的Body中.
  • GET提交的数据大小有限制(因为浏览器对URL的长度有限制),而POST方法提交的数据没有限制.
  • GET方式需要使用Request.QueryString来取得变量的值,而POST方式通过Request.Form来获取变量的值。

Volley Post 请求实现

通过重写 getParams() 方法实现,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
private static void postRequest(String url, final Map<String, String> params, final String session, final Map<String, String> sigMap, Response.Listener listener, Response.ErrorListener errorListener) {
StatisticPostRequest requestString = new StatisticPostRequest(Request.Method.POST, url, null, listener, errorListener) {
@Override
protected Map<String, String> getParams() throws AuthFailureError {
Map<String, String> map = params;
map.put("session", session);
map.put("sver", sigMap.get("sver").trim());
map.put("sig", sigMap.get("sig").trim());
return map;
}
};
VolleyHelper.getRequestQueue().add(requestString);
}

StatisticPostRequest 重写 getBody() 方法来对 params 进行 encode, 代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/**
* post参数urlencode需要外部传入时自己拼
*/
public class StatisticPostRequest<T> extends BaseRequest<T> {
private final Class<T> mClazz;
public StatisticPostRequest(int method,
String url,
Class<T> clazz,
Response.Listener<T> listener,
Response.ErrorListener errorListener) {
super(method, url, listener, errorListener);
mClazz = clazz;
}
@Override
public byte[] getBody() throws AuthFailureError {
Map<String, String> params = getParams();
if (params != null && params.size() > 0) {
return getParameters(params);
}
return null;
}
/**
* Converts <code>params</code> into an application/x-www-form-urlencoded encoded string.
*/
private byte[] getParameters(Map<String, String> params) {
StringBuilder encodedParams = new StringBuilder();
try {
for (Map.Entry<String, String> entry : params.entrySet()) {
encodedParams.append(entry.getKey());
encodedParams.append('=');
encodedParams.append(entry.getValue());
encodedParams.append('&');
}
return encodedParams.toString().getBytes();
} catch (Exception uee) {
throw new RuntimeException("Encoding not supported: ", uee);
}
}
@Override
protected T parseNetworkResponseDelegate(String jsonString) {
return (T) jsonString;
}
}

还有一种方式,可以传入 请求体 requestBody, 代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
* Post请求,请求体为Json格式
*/
public class JsonPostRequest<T> extends JsonRequest<T> {
private final Class<T> mClazz;
public JsonPostRequest(int method, String url, String requestBody,
Class<T> clazz,
Response.Listener<T> listener,
Response.ErrorListener errorListener) {
super(method, url, requestBody, listener, errorListener);
mClazz = clazz;
}
@Override
protected Response<T> parseNetworkResponse(NetworkResponse response) {
try {
String jsonString = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
return Response.success(JsonUtils.parseObject(jsonString, mClazz),
HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
}
}
}

父类如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
/**
* A request for retrieving a T type response body at a given URL that also
* optionally sends along a JSON body in the request specified.
*
* @param <T> JSON type of response expected
*/
public abstract class JsonRequest<T> extends Request<T> {
/** Charset for request. */
private static final String PROTOCOL_CHARSET = "utf-8";
/** Content type for request. */
private static final String PROTOCOL_CONTENT_TYPE =
String.format("application/json; charset=%s", PROTOCOL_CHARSET);
private final Listener<T> mListener;
private final String mRequestBody;
/**
* Deprecated constructor for a JsonRequest which defaults to GET unless {@link #getPostBody()}
* or {@link #getPostParams()} is overridden (which defaults to POST).
*
* @deprecated Use {@link #JsonRequest(int, String, String, Listener, ErrorListener)}.
*/
public JsonRequest(String url, String requestBody, Listener<T> listener,
ErrorListener errorListener) {
this(Method.DEPRECATED_GET_OR_POST, url, requestBody, listener, errorListener);
}
public JsonRequest(int method, String url, String requestBody, Listener<T> listener,
ErrorListener errorListener) {
super(method, url, errorListener);
mListener = listener;
mRequestBody = requestBody;
}
@Override
protected void deliverResponse(T response) {
mListener.onResponse(response);
}
@Override
abstract protected Response<T> parseNetworkResponse(NetworkResponse response);
/**
* @deprecated Use {@link #getBodyContentType()}.
*/
@Override
public String getPostBodyContentType() {
return getBodyContentType();
}
/**
* @deprecated Use {@link #getBody()}.
*/
@Override
public byte[] getPostBody() {
return getBody();
}
@Override
public String getBodyContentType() {
return PROTOCOL_CONTENT_TYPE;
}
@Override
public byte[] getBody() {
try {
return mRequestBody == null ? null : mRequestBody.getBytes(PROTOCOL_CHARSET);
} catch (UnsupportedEncodingException uee) {
VolleyLog.wtf("Unsupported Encoding while trying to get the bytes of %s using %s",
mRequestBody, PROTOCOL_CHARSET);
return null;
}
}
}

如果要对 body 参数进行 UrlEncode ,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
private static final String DEFAULT_PARAMS_ENCODING = "UTF-8";
/**
* Returns the raw POST or PUT body to be sent.
*
* @throws AuthFailureError in the event of auth failure
*/
public byte[] getBody() throws AuthFailureError {
Map<String, String> params = getParams();
if (params != null && params.size() > 0) {
return encodeParameters(params, getParamsEncoding());
}
return null;
}
/**
* Converts <code>params</code> into an application/x-www-form-urlencoded encoded string.
*/
private byte[] encodeParameters(Map<String, String> params, String paramsEncoding) {
StringBuilder encodedParams = new StringBuilder();
try {
for (Map.Entry<String, String> entry : params.entrySet()) {
encodedParams.append(URLEncoder.encode(entry.getKey(), paramsEncoding));
encodedParams.append('=');
encodedParams.append(URLEncoder.encode(entry.getValue(), paramsEncoding));
encodedParams.append('&');
}
return encodedParams.toString().getBytes(paramsEncoding);
} catch (UnsupportedEncodingException uee) {
throw new RuntimeException("Encoding not supported: " + paramsEncoding, uee);
}
}
protected String getParamsEncoding() {
return DEFAULT_PARAMS_ENCODING;
}

纪念一下,从成为一个开发者之后第一次,由于自己的 CTRL + C / CTRL+ V 导致线上版本统计出问题,领导很快就决定 每人罚款 ¥五百 ,以示惩戒。

罚款不是目的

愿 2018 一切顺利!!!