Volley解析之表单提交篇
要实现表单的提交,就要知道表单提交的数据格式是怎么样,这里我从某知名网站抓了一条数据,先来分析别人提交表单的数据格式。 数据包:
Connection: keep-alive
Content-Length: 123
X-Requested-With: ShockwaveFlash/16.0.0.296
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.93 Safari/537.36
Content-Type: multipart/form-data; boundary=----------Ij5ei4KM7KM7ae0KM7cH2ae0Ij5Ef1
Accept: */*
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.8
Cookie: bdshare_firstime=1409052493497
------------Ij5ei4KM7KM7ae0KM7cH2ae0Ij5Ef1
Content-Disposition: form-data; name="position"
1425264476444
------------Ij5ei4KM7KM7ae0KM7cH2ae0Ij5Ef1
Content-Disposition: form-data; name="editorIndex"
ue_con_1425264252856
------------Ij5ei4KM7KM7ae0KM7cH2ae0Ij5Ef1
Content-Disposition: form-data; name="cm"
100672
------------Ij5ei4KM7KM7ae0KM7cH2ae0Ij5Ef1--
看到上面的数据包,我们不需要全部分析,我们主要关心的是数据如何封装,因为http请求头,在网络请求中已经为我们封装好了;可以看到这里总共是提交了三条数据,每一条数据的格式都是一样的,所以我们只需要分析一条数据即可,这里拿最后一条数据来说,因为在所有的数据之后还有一个结尾的标志。
------------Ij5ei4KM7KM7ae0KM7cH2ae0Ij5Ef1
Content-Disposition: form-data; name="cm"
100672
------------Ij5ei4KM7KM7ae0KM7cH2ae0Ij5Ef1--
这条数据一共有四行组成,加上结尾标志共有五行 1、第一行:
"--" + boundary + "rn" ;
说明:”–”为数据开始标志,boundary为http实体头定义的边界分割线,boundary可以是任意的字符串,只要和Content-Type: multipart/form-data; boundary=———-Ij5ei4KM7KM7ae0KM7cH2ae0Ij5Ef1中保持一直即可,”rn”为回车换行; 2、第二行:
"Content-Disposition: form-data; name="参数的名称"" + "rn" ;
说明:Content-Disposition表示上传的内容特性,form-data上传内容特性为表单的形式; 3、第三行:
"rn" ;
4、说明:这是一个空行,只有一个回车换行 ; 第四行:
"参数的值" + "rn" ;
说明:每个参数都是由一个key和value组成,而这一行就是value跟回车换行符 结尾行:
"--" + boundary + "--" + "rn" ;
说明:在所有的数据结束之后,需要有这个结尾标志。 如果有多个参数,则重复1、2、3、4,直至最后一个参数的最后加上结尾行。
参数实体类
这里对参数做一个封装,因为往往提交表单的时候,都需要提交多个参数
/**
* Created by gyzhong on 15/3/2.
*/
/*表单提交的参数*/
public class FormText {
/*参数的名称*/
private String mName ;
/*参数的值*/
private String mValue ;
public FormText(String mName, String mValue) {
this.mName = mName;
this.mValue = mValue;
}
public String getName() {
return mName;
}
public String getValue() {
return mValue;
}
}
Volley 对数据的封装
在上一篇文章中我们讲了如何定制自己的 Request,在这里同样需要用到。在定制 Request 的时候,需要重写获取实体的方法
public byte[] getBody() throws AuthFailureError {}
把参数通过二进制的形式传给服务器,当然就不需要重写获取参数的方法
protected Map<String, String> getParams() throws AuthFailureError {}
最核心的方法也就在getBody()中,这个方法的实现,如果对表单提交的数据格式很了解,实现起来非常简单,因为这个方法就是把参数拼接成我们所分析的数据格式;
@Override
public byte[] getBody() throws AuthFailureError {
if (mListItem == null||mListItem.size() == 0){
return super.getBody() ;
}
ByteArrayOutputStream bos = new ByteArrayOutputStream() ;
int N = mListItem.size() ;
FormText formText ;
for (int i = 0; i < N ;i++){
formText = mListItem.get(i) ;
StringBuffer sb= new StringBuffer() ;
/*第一行:"--" + boundary + "rn" ;*/
sb.append("--"+BOUNDARY);
sb.append("rn") ;
/*第二行:"Content-Disposition: form-data; name="参数的名称"" + "rn" ;*/
sb.append("Content-Disposition: form-data;");
sb.append("name="");
sb.append(formText.getName()) ;
sb.append(""") ;
sb.append("rn") ;
/*第三行:"rn" ;*/
sb.append("rn") ;
/*第四行:"参数的值" + "rn" ;*/
sb.append(formText.getValue()) ;
sb.append("rn") ;
try {
bos.write(sb.toString().getBytes("utf-8"));
} catch (IOException e) {
e.printStackTrace();
}
}
/*结尾行:"--" + boundary + "--" + "rn" ;*/
String endLine = "--" + BOUNDARY + "--"+ "rn" ;
try {
bos.write(endLine.toString().getBytes("utf-8"));
} catch (IOException e) {
e.printStackTrace();
}
Log.v("zgy","=====formText====n"+bos.toString()) ;
return bos.toByteArray();
}
可以看到,这个方法的实现,就是对数据按照我们所分析的格式组装。 在 Request 中还有一个关键的地方,需要在 http 头部中声明内容类型为表单数据
Content-Type: multipart/form-data; boundary=----------8888888888888
所以的重写下面方法为
public String getBodyContentType() {
return MULTIPART_FORM_DATA+"; boundary="+BOUNDARY;
}
同样,我们还是前面我们blog 中讲到的接口http://www.minongbang.com/test.php?test=minongbang来测试,所以解析数据那一块跟 前面我们将的一样,具体实现如下:
/**
* Created by gyzhong on 15/3/2.
*/
public class PostFormRequest<T> extends Request<T> {
/**
* 正确数据的时候回掉用
*/
private ResponseListener mListener ;
/*用来解析 json 用的*/
private Gson mGson ;
/*在用 gson 解析 json 数据的时候,需要用到这个参数*/
private Type mClazz ;
/*请求 数据通过参数的形式传入*/
private List<FormText> mListItem ;
private String BOUNDARY = "---------8888888888888"; //数据分隔线
private String MULTIPART_FORM_DATA = "multipart/form-data";
public PostFormRequest(String url, List<FormText> listItem, Type type, ResponseListener listener) {
super(Method.POST, url, listener);
this.mListener = listener ;
mGson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create() ;
mClazz = type ;
setShouldCache(false);
mListItem = listItem ;
}
/**
* 这里开始解析数据
* @param response Response from the network
* @return
*/
@Override
protected Response<T> parseNetworkResponse(NetworkResponse response) {
try {
T result ;
String jsonString =
new String(response.data, HttpHeaderParser.parseCharset(response.headers));
Log.v("zgy", "====SearchResult===" + jsonString);
result = mGson.fromJson(jsonString,mClazz) ;
return Response.success(result,
HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
}
}
/**
* 回调正确的数据
* @param response The parsed response returned by
*/
@Override
protected void deliverResponse(T response) {
mListener.onResponse(response);
}
@Override
public byte[] getBody() throws AuthFailureError {
if (mListItem == null||mListItem.size() == 0){
return super.getBody() ;
}
ByteArrayOutputStream bos = new ByteArrayOutputStream() ;
int N = mListItem.size() ;
FormText formText ;
for (int i = 0; i < N ;i++){
formText = mListItem.get(i) ;
StringBuffer sb= new StringBuffer() ;
/*第一行:"--" + boundary + "rn" ;*/
sb.append("--"+BOUNDARY);
sb.append("rn") ;
/*第二行:"Content-Disposition: form-data; name="参数的名称"" + "rn" ;*/
sb.append("Content-Disposition: form-data;");
sb.append("name="");
sb.append(formText.getName()) ;
sb.append(""") ;
sb.append("rn") ;
/*第三行:"rn" ;*/
sb.append("rn") ;
/*第四行:"参数的值" + "rn" ;*/
sb.append(formText.getValue()) ;
sb.append("rn") ;
try {
bos.write(sb.toString().getBytes("utf-8"));
} catch (IOException e) {
e.printStackTrace();
}
}
/*结尾行:"--" + boundary + "--" + "rn" ;*/
String endLine = "--" + BOUNDARY + "--"+ "rn" ;
try {
bos.write(endLine.toString().getBytes("utf-8"));
} catch (IOException e) {
e.printStackTrace();
}
Log.v("zgy","=====formText====n"+bos.toString()) ;
return bos.toByteArray();
}
/*获取内容类型,这里为表单类型*/
@Override
public String getBodyContentType() {
return MULTIPART_FORM_DATA+"; boundary="+BOUNDARY;
}
}
表单提交接口
接口我们在上一篇博文中也做了比较详细的讲解,这里就不累赘了。
/**
* minong 测试post表单提交接口
* @param value 测试数据
* @param listener 回调接口,包含错误回调和正确的数据回调
*/
public static void postFormMiNongApi(String value,ResponseListener listener){
List<FormText> formTextList = new ArrayList<FormText>() ;
formTextList.add(new FormText(" test",value));
Request request = new PostFormRequest(Constant.MiNonghost,formTextList,new TypeToken<SearchResult>(){}.getType(),listener) ;
VolleyUtil.getRequestQueue().add(request) ;
}
测试
PostFormActivity.java的测试代码如下
public class PostFormActivity extends ActionBarActivity {
private ListView mListView ;
private SongAdapter mAdapter ;
private List<SongDetail> mSongList ;
private ProgressDialog mDialog ;
private View mHeaderView ;
private TextView mShowPostData ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_post_form);
mSongList = new ArrayList<SongDetail>() ;
mListView = (ListView) findViewById(R.id.id_list_view);
mAdapter = new SongAdapter(this,mSongList) ;
mHeaderView = getLayoutInflater().inflate(R.layout.list_header_item,null) ;
mShowPostData = (TextView) mHeaderView.findViewById(R.id.id_post_data) ;
mListView.addHeaderView(mHeaderView);
mListView.setAdapter(mAdapter);
mDialog = new ProgressDialog(this) ;
mDialog.setMessage("数据提交中...");
mDialog.show() ;
/*请求网络获取数据*/
MiNongApi.postFormMiNongApi(" minongbang",new DataResponseListener<SearchResult>() {
@Override
public void onErrorResponse(VolleyError error) {
Log.v("zgy","======onErrorResponse====="+error);
mDialog.dismiss();
}
@Override
public void onResponse(SearchResult response) {
Log.v("zgy","======onResponse====="+response);
mSongList.addAll(response.getData().getInfo()) ;
mAdapter.notifyDataSetChanged();
mDialog.dismiss();
}
@Override
public void postData(String data) {
mShowPostData.setText(data);
}
});
}
- 警惕:Oracle中删除的分区不会进入回收站(Recyclebin)
- 《Redis设计与实现》读书笔记(二十五) ——Redis主从复制具体过程
- 基于input子系统的sensor驱动调试(一)
- 《Redis设计与实现》读书笔记(二十六) ——Redis哨兵(sentinel)启动与建立监听机制
- 《Redis设计与实现》读书笔记(二十七) ——Redis哨兵(sentinel)主服务器下线判断与故障转移
- 基于input子系统的sensor驱动调试(二)
- 编程语言中的闭包
- 《Redis设计与实现》读书笔记(二十八) ——Redis集群节点结构与槽分配
- Linux RCU 机制详解
- 《Redis设计与实现》读书笔记(二十九) ——Redis集群执行命令与重新分片
- 如何使用C语言的面向对象
- 《Redis设计与实现》读书笔记(三十) ——Redis集群节点复制与故障转移
- 掌握一点儿统计学
- 高通HAL层之bmp18x.cpp
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- centos通过yum安装php的教程
- 树莓派4安装Ubuntu 19.10的教程详解
- Linux系统(CentOS7安装)安装JDK8的教程详解
- Deepin中安装与使用virtualenv的教程
- linux安装jdk并设置环境变量的方法教程(看这一篇够了)
- hadoop基于Linux7的安装配置图文详解
- Linux paste命令的使用方法
- ubuntu下迁移home目录至新的分区教程详解
- SVN服务备份操作步骤分享
- Linux磁盘挂载、分区、扩容操作的实现方法
- linux使用docker-compose部署软件配置详解
- 在Ubuntu中如何查看网络路由表详解
- linux中各种锁机制的使用与区别详解
- Linux环境下使用glog日志库的方法
- crontab执行时间与系统时间不一致问题解决