要实现表单的提交,就要知道表单提交的数据格式是怎么样,这里我从某知名网站抓了一条数据,先来分析别人提交表单的数据格式。
数据包:
1Connection: keep-alive 2Content-Length: 123 3X-Requested-With: ShockwaveFlash/16.0.0.296 4User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.93 Safari/537.36 5Content-Type: multipart/form-data; boundary=----------Ij5ei4KM7KM7ae0KM7cH2ae0Ij5Ef1 6Accept: */* 7Accept-Encoding: gzip, deflate 8Accept-Language: zh-CN,zh;q=0.8 9Cookie: bdshare_firstime=1409052493497 10 11------------Ij5ei4KM7KM7ae0KM7cH2ae0Ij5Ef1 12Content-Disposition: form-data; name="position" 13 141425264476444 15------------Ij5ei4KM7KM7ae0KM7cH2ae0Ij5Ef1 16Content-Disposition: form-data; name="editorIndex" 17 18ue_con_1425264252856 19------------Ij5ei4KM7KM7ae0KM7cH2ae0Ij5Ef1 20Content-Disposition: form-data; name="cm" 21 22100672 23------------Ij5ei4KM7KM7ae0KM7cH2ae0Ij5Ef1--
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
看到上面的数据包,我们不需要全部分析,我们主要关心的是数据如何封装,因为http请求头,在网络请求中已经为我们封装好了;可以看到这里总共是提交了三条数据,每一条数据的格式都是一样的,所以我们只需要分析一条数据即可,这里拿最后一条数据来说,因为在所有的数据之后还有一个结尾的标志。
1------------Ij5ei4KM7KM7ae0KM7cH2ae0Ij5Ef1 2Content-Disposition: form-data; name="cm" 3 4100672 5------------Ij5ei4KM7KM7ae0KM7cH2ae0Ij5Ef1--
- 1
- 2
- 3
- 4
- 5
这条数据一共有四行组成,加上结尾标志共有五行
1、第一行:
"--" + boundary + "\r\n" ;
- 1
说明:”–”为数据开始标志,boundary为http实体头定义的边界分割线,boundary可以是任意的字符串,只要和Content-Type: multipart/form-data; boundary=———-Ij5ei4KM7KM7ae0KM7cH2ae0Ij5Ef1中保持一直即可,”\r\n”为回车换行;
2、第二行:
"Content-Disposition: form-data; name="参数的名称"" + "\r\n" ;
- 1
说明:Content-Disposition表示上传的内容特性,form-data上传内容特性为表单的形式;
3、第三行:
"\r\n" ;
- 1
4、说明:这是一个空行,只有一个回车换行 ;
第四行:
"参数的值" + "\r\n" ;
- 1
说明:每个参数都是由一个key和value组成,而这一行就是value跟回车换行符
结尾行:
"--" + boundary + "--" + "\r\n" ;
- 1
说明:在所有的数据结束之后,需要有这个结尾标志。
如果有多个参数,则重复1、2、3、4,直至最后一个参数的最后加上结尾行。
参数实体类
这里对参数做一个封装,因为往往提交表单的时候,都需要提交多个参数
1/** 2 * Created by gyzhong on 15/3/2. 3 */ 4/*表单提交的参数*/ 5public class FormText { 6 7 8 9 /*参数的名称*/ 10 private String mName ; 11 /*参数的值*/ 12 private String mValue ; 13 14 public FormText(String mName, String mValue) { 15 this.mName = mName; 16 this.mValue = mValue; 17 } 18 19 public String getName() { 20 return mName; 21 } 22 23 public String getValue() { 24 return mValue; 25 } 26 27}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
Volley 对数据的封装
在上一篇文章中我们讲了如何定制自己的 Request,在这里同样需要用到。在定制 Request 的时候,需要重写获取实体的方法
public byte[] getBody() throws AuthFailureError {}
- 1
把参数通过二进制的形式传给服务器,当然就不需要重写获取参数的方法
protected Map<String, String> getParams() throws AuthFailureError {}
- 1
最核心的方法也就在getBody()中,这个方法的实现,如果对表单提交的数据格式很了解,实现起来非常简单,因为这个方法就是把参数拼接成我们所分析的数据格式;
1 @Override 2 public byte[] getBody() throws AuthFailureError { 3 if (mListItem == null||mListItem.size() == 0){ 4 return super.getBody() ; 5 } 6 ByteArrayOutputStream bos = new ByteArrayOutputStream() ; 7 int N = mListItem.size() ; 8 FormText formText ; 9 for (int i = 0; i < N ;i++){ 10 11 12 13 formText = mListItem.get(i) ; 14 StringBuffer sb= new StringBuffer() ; 15 /*第一行:"--" + boundary + "\r\n" ;*/ 16 sb.append("--"+BOUNDARY); 17 sb.append("\r\n") ; 18 /*第二行:"Content-Disposition: form-data; name="参数的名称"" + "\r\n" ;*/ 19 sb.append("Content-Disposition: form-data;"); 20 sb.append("name=\""); 21 sb.append(formText.getName()) ; 22 sb.append("\"") ; 23 sb.append("\r\n") ; 24 /*第三行:"\r\n" ;*/ 25 sb.append("\r\n") ; 26 /*第四行:"参数的值" + "\r\n" ;*/ 27 sb.append(formText.getValue()) ; 28 sb.append("\r\n") ; 29 try { 30 bos.write(sb.toString().getBytes("utf-8")); 31 } catch (IOException e) { 32 e.printStackTrace(); 33 } 34 } 35 /*结尾行:"--" + boundary + "--" + "\r\n" ;*/ 36 String endLine = "--" + BOUNDARY + "--"+ "\r\n" ; 37 try { 38 bos.write(endLine.toString().getBytes("utf-8")); 39 } catch (IOException e) { 40 e.printStackTrace(); 41 } 42 Log.v("zgy","=====formText====\n"+bos.toString()) ; 43 return bos.toByteArray(); 44 }
- 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
可以看到,这个方法的实现,就是对数据按照我们所分析的格式组装。
在 Request 中还有一个关键的地方,需要在 http 头部中声明内容类型为表单数据
Content-Type: multipart/form-data; boundary=----------8888888888888
- 1
所以的重写下面方法为
1public String getBodyContentType() { 2 return MULTIPART_FORM_DATA+"; boundary="+BOUNDARY; 3 }
- 1
- 2
- 3
同样,我们还是前面我们blog 中讲到的接口http://www.minongbang.com/test.php?test=minongbang来测试,所以解析数据那一块跟 前面我们将的一样,具体实现如下:
1/** 2 * Created by gyzhong on 15/3/2. 3 */ 4public class PostFormRequest<T> extends Request<T> { 5 6 7 8 9 /** 10 * 正确数据的时候回掉用 11 */ 12 private ResponseListener mListener ; 13 /*用来解析 json 用的*/ 14 private Gson mGson ; 15 /*在用 gson 解析 json 数据的时候,需要用到这个参数*/ 16 private Type mClazz ; 17 /*请求 数据通过参数的形式传入*/ 18 private List<FormText> mListItem ; 19 20 private String BOUNDARY = "---------8888888888888"; //数据分隔线 21 private String MULTIPART_FORM_DATA = "multipart/form-data"; 22 23 public PostFormRequest(String url, List<FormText> listItem, Type type, ResponseListener listener) { 24 super(Method.POST, url, listener); 25 this.mListener = listener ; 26 mGson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create() ; 27 mClazz = type ; 28 setShouldCache(false); 29 mListItem = listItem ; 30 } 31 32 /** 33 * 这里开始解析数据 34 * @param response Response from the network 35 * @return 36 */ 37 @Override 38 protected Response<T> parseNetworkResponse(NetworkResponse response) { 39 try { 40 T result ; 41 String jsonString = 42 new String(response.data, HttpHeaderParser.parseCharset(response.headers)); 43 Log.v("zgy", "====SearchResult===" + jsonString); 44 result = mGson.fromJson(jsonString,mClazz) ; 45 return Response.success(result, 46 HttpHeaderParser.parseCacheHeaders(response)); 47 } catch (UnsupportedEncodingException e) { 48 return Response.error(new ParseError(e)); 49 } 50 } 51 52 /** 53 * 回调正确的数据 54 * @param response The parsed response returned by 55 */ 56 @Override 57 protected void deliverResponse(T response) { 58 mListener.onResponse(response); 59 } 60 61 @Override 62 public byte[] getBody() throws AuthFailureError { 63 if (mListItem == null||mListItem.size() == 0){ 64 return super.getBody() ; 65 } 66 ByteArrayOutputStream bos = new ByteArrayOutputStream() ; 67 int N = mListItem.size() ; 68 FormText formText ; 69 for (int i = 0; i < N ;i++){ 70 formText = mListItem.get(i) ; 71 StringBuffer sb= new StringBuffer() ; 72 /*第一行:"--" + boundary + "\r\n" ;*/ 73 sb.append("--"+BOUNDARY); 74 sb.append("\r\n") ; 75 /*第二行:"Content-Disposition: form-data; name="参数的名称"" + "\r\n" ;*/ 76 sb.append("Content-Disposition: form-data;"); 77 sb.append("name=\""); 78 sb.append(formText.getName()) ; 79 sb.append("\"") ; 80 sb.append("\r\n") ; 81 /*第三行:"\r\n" ;*/ 82 sb.append("\r\n") ; 83 /*第四行:"参数的值" + "\r\n" ;*/ 84 sb.append(formText.getValue()) ; 85 sb.append("\r\n") ; 86 try { 87 bos.write(sb.toString().getBytes("utf-8")); 88 } catch (IOException e) { 89 e.printStackTrace(); 90 } 91 } 92 /*结尾行:"--" + boundary + "--" + "\r\n" ;*/ 93 String endLine = "--" + BOUNDARY + "--"+ "\r\n" ; 94 try { 95 bos.write(endLine.toString().getBytes("utf-8")); 96 } catch (IOException e) { 97 e.printStackTrace(); 98 } 99 Log.v("zgy","=====formText====\n"+bos.toString()) ; 100 return bos.toByteArray(); 101 } 102 103 /*获取内容类型,这里为表单类型*/ 104 @Override 105 public String getBodyContentType() { 106 return MULTIPART_FORM_DATA+"; boundary="+BOUNDARY; 107 } 108}
- 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
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
表单提交接口
接口我们在上一篇博文中也做了比较详细的讲解,这里就不累赘了。
1 /** 2 * minong 测试post表单提交接口 3 * @param value 测试数据 4 * @param listener 回调接口,包含错误回调和正确的数据回调 5 */ 6 public static void postFormMiNongApi(String value,ResponseListener listener){ 7 List<FormText> formTextList = new ArrayList<FormText>() ; 8 formTextList.add(new FormText(" test",value)); 9 Request request = new PostFormRequest(Constant.MiNonghost,formTextList,new TypeToken<SearchResult>(){}.getType(),listener) ; 10 VolleyUtil.getRequestQueue().add(request) ; 11 }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
测试
PostFormActivity.java的测试代码如下
1public class PostFormActivity extends ActionBarActivity { 2 3 4 5 6 private ListView mListView ; 7 private SongAdapter mAdapter ; 8 private List<SongDetail> mSongList ; 9 private ProgressDialog mDialog ; 10 private View mHeaderView ; 11 private TextView mShowPostData ; 12 @Override 13 protected void onCreate(Bundle savedInstanceState) { 14 super.onCreate(savedInstanceState); 15 setContentView(R.layout.activity_post_form); 16 mSongList = new ArrayList<SongDetail>() ; 17 mListView = (ListView) findViewById(R.id.id_list_view); 18 mAdapter = new SongAdapter(this,mSongList) ; 19 mHeaderView = getLayoutInflater().inflate(R.layout.list_header_item,null) ; 20 mShowPostData = (TextView) mHeaderView.findViewById(R.id.id_post_data) ; 21 mListView.addHeaderView(mHeaderView); 22 mListView.setAdapter(mAdapter); 23 mDialog = new ProgressDialog(this) ; 24 mDialog.setMessage("数据提交中..."); 25 mDialog.show() ; 26 /*请求网络获取数据*/ 27 MiNongApi.postFormMiNongApi(" minongbang",new DataResponseListener<SearchResult>() { 28 @Override 29 public void onErrorResponse(VolleyError error) { 30 Log.v("zgy","======onErrorResponse====="+error); 31 mDialog.dismiss(); 32 } 33 34 @Override 35 public void onResponse(SearchResult response) { 36 Log.v("zgy","======onResponse====="+response); 37 mSongList.addAll(response.getData().getInfo()) ; 38 mAdapter.notifyDataSetChanged(); 39 mDialog.dismiss(); 40 } 41 42 @Override 43 public void postData(String data) { 44 mShowPostData.setText(data); 45 } 46 }); 47 }
本文同步分享在 博客“xiangzhihong8”(CSDN)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。