在开发Web程序时,我们向Web服务发送请求是最常见的事,如果了解向服务器提交的HTTP码流,对有些问题我们就会有很好的把握,比如HTTP信息结构、乱码产生、表单参数以及URL绑定参数的传递提交机制,对我们提交的数据心中有数。下面我们就来看看通过表单与URL两种提交请求方式时各自的HTTP码流究竟如何?先来了解了解HTTP消息格式。
HTTP消息格式
HTTP报文是面向文本的,报文中的每一个字段都是一些ASCII码串,各个字段的长度是不确定的。HTTP有两类报文:请求报文和响应报文。
请求报文
一个HTTP请求报文由请求行(request line)、请求头部(header)、空行和请求数据4个部分组成,下图给出了请求报文的一般格式。
1、请求行
请求行由请求方法字段、URL字段和HTTP协议版本字段3个字段组成,它们用空格分隔。例如,GET /index.html HTTP/1.1。
HTTP协议的请求方法有GET、POST、HEAD、PUT、DELETE、OPTIONS、TRACE、CONNECT。这里介绍最常用的GET方法和POST方法。
GET:当客户端要从服务器中读取文档时,使用GET方法。GET方法要求服务器将URL定位的资源放在响应报文的数据部分,回送给客户端。使用GET方法时,请求参数和对应的值附加在URL后面,利用一个问号(“?”)代表URL的结尾与请求参数的开始,传递参数长度受限制。例如,/index.jsp?id=100&op=bind。
POST:当客户端给服务器提供信息较多时可以使用POST方法。POST方法将请求参数封装在HTTP请求数据中,以名称/值的形式出现,可以传输大量数据,可用来传送文件。
2、请求头部
请求头部由关键字/值对组成,每行一对,关键字和值用英文冒号“:”分隔。请求头部通知服务器有关于客户端请求的信息,典型的请求头有:
User-Agent:产生请求的浏览器类型。
Accept:客户端可识别的内容类型列表。
Host:请求的主机名,允许多个域名同处一个IP地址,即虚拟主机。
3、空行
最后一个请求头之后是一个空行,发送回车符和换行符,通知服务器以下不再有请求头。
对于一个完整的http请求来说空行是必须的,否则服务器会认为本次请求的数据尚未完全发送到服务器,处于等待状态。
4、请求数据
请求数据不在GET方法中使用,而是在POST方法中使用。POST方法适用于需要客户填写表单的场合。与请求数据相关的最常使用的请求头是Content-Type和Content-Length。
了解了HTTP消息格式后,先我们要模拟服务器,让客户端往我们模拟服务器发送,这样我们就可以截获码流。下面是服务器模拟程序:
客户端HTTP码流端截取程序
import java.io.IOException; import java.io.InputStream; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.net.ServerSocket; import java.net.Socket; public class HttpServer { public static void main(String[] args) { try { ServerSocket sSocket = new ServerSocket(8088); Socket socket = sSocket.accept(); InputStream is = socket.getInputStream(); //String encode = "GB2312"; String encode = "UTF-8"; putStream(is, encode); } catch (IOException e) { e.printStackTrace(); } } public static void putStream(InputStream is, String encode) throws IOException, UnsupportedEncodingException { byte[] content = new byte[1024]; int readCount = is.read(content); OutputStreamWriter bos = new OutputStreamWriter(System.out); while (readCount != 0) { bos.write(new String(content, 0, readCount, encode)); bos.flush(); readCount = is.read(content); } is.close(); } }
POST方式提交表单并且URL后面绑定参数
JSP页面如下:
<%@ page language="java" contentType="text/html; charset=GB2312" pageEncoding="GB2312"%> <html> <body> <form name=form1 action="" method="post"> <input type="text" name="textParam1" size="50 px" value="中a ~!@#$%^&*()_+{}|:\" <>?`-=[]\\;',./"> <br> <input type="file" name="fileParam" size="50 px" value=""> <br> <input type="button" value="submit" οnclick="submitForm()"> </form> <script type="text/javascript"> function submitForm(){ var str ="中a ~!@#$%^&*()_+{}|:\" <>?`-=[]\\;',./"; //form1.action = "gb2312rs.jsp?qryParam1=" + encodeURIComponent(str) + "&qryParam2="+ encodeURIComponent(form1.textParam1.value) ; form1.action = "http://localhost:8088/gb2312rs.jsp?qryParam1=" + encodeURIComponent(str) + "&qryParam2="+ encodeURIComponent(form1.textParam1.value) ; form1.submit(); } </script> </body> </html>
页面截图:
提交后地址栏如下:
http://localhost:8080/HttpStream/gb2312rs.jsp?qryParam1=%E4%B8%ADa%20~!%40%23%24%25%5E%26*()_%2B%7B%7D%7C%3A%22%20%3C%3E%3F%60-%3D%5B%5D%5C%3B'%2C.%2F&qryParam2=%E4%B8%ADa%20~!%40%23%24%25%5E%26*()_%2B%7B%7D%7C%3A%5C%22%20%3C%3E%3F%60-%3D%5B%5D%5C%5C%3B'%2C.%2F
HTTP消息码流:
POST /gb2312rs.jsp?qryParam1=%E4%B8%ADa%20~!%40%23%24%25%5E%26*()_%2B%7B%7D%7C%3A%22%20%3C%3E%3F%60-%3D%5B%5D%5C%3B'%2C.%2F&qryParam2=%E4%B8%ADa%20~!%40%23%24%25%5E%26*()_%2B%7B%7D%7C%3A%5C%22%20%3C%3E%3F%60-%3D%5B%5D%5C%5C%3B'%2C.%2F HTTP/1.1
textParam1=%D6%D0a+%7E%21@%23%24%25%5E%26*%28%29_%2B%7B%7D%7C%3A%5C%22+%3C%3E%3F%60-%3D%5B%5D%5C%5C%3B%27%2C.%2F&fileParam=file1.txt |
从上面码流分析可知:
- URL后绑定的参数会显示在HTTP消息头中。
- 文件类型没有传到服务器,因为表单未设置enctype="multipart/form-data",但文件名却传过来了。
- 提交请求为编码方式为GB2312的页面表单时,参数qryParam1与参数qryParam2都是UTF-8的编码格式,而不是以GBP312方式编码的,因此encodeURIComponent()函数是固定以UTF-8编码的,它不会受到当前浏览器的编码影响。
- 经过encodeURIComponent()函数编码后的附加参数内容会以%xx形式串显示在地址栏中。
- POST方式的HTTP头与HTTP体是用一个回车换行来分隔的。
- POST方式提交表单,如果URL后面还附还参数,也会提交到服务器,且放在HTTP头部,其他表单输入元素会放在HTTP体里传送。
- POST方式提交表单时,在头部会有 Content-Type: application/x-www-form-urlencoded 这样一个头信息,而GET方式提交的表单是不会有该头信息的,也不会有HTTP请求体。
- POST方式提交时,表单里的元素值会先用浏览器的编码(这里使用的是GB2312编码)方式时行编码(a-z A-Z 0-9 +@*_-. 不进行编码),然后把编码转换成%xx(xx为两位的十六进制)形式参数串后传送到服务器。
GET方式提交表单
JSP页面如下:
与上面一样,只是表单提交方式改为 get。
页面截图:
与上面一样。
提交后地址栏如下:
http://localhost:8088/gb2312rs.jsp?textParam1=%D6%D0a+%7E%21@%23%24%25%5E%26*%28%29_%2B%7B%7D%7C%3A%5C%22+%3C%3E%3F%60-%3D%5B%5D%5C%5C%3B%27%2C.%2F&fileParam=file1.txt
HTTP消息码流:
GET /gb2312rs.jsp?textParam1=%D6%D0a+%7E%21@%23%24%25%5E%26*%28%29_%2B%7B%7D%7C%3A%5C%22+%3C%3E%3F%60-%3D%5B%5D%5C%5C%3B%27%2C.%2F&fileParamhttp://localhost:8080/HttpStream/gb2312.jsp
|
从上面码流分析可知:
GET方式时,表单里的参数会先以浏览器编码成%XX形式后附加到URL后,并显示在地址栏中。 GET方式提交表单时,如果表单URL附加参数,这些参数是不能传递到服务器端去的,相反表单里的元素会附加在URL后面并传送到服务器,所以当提交表单时,如果表单URL还附加参数,则一定要以POST方式提交,否则是不能传递到服务器。 GET方式提交的请求HTTP头里没有 Content-Type: application/x-www-form-urlencoded 头信息。 GET提交时,表单里的元素值会先用浏览器的编码方式时行编码,然后把编码转换成%xx形式的串。文件上传时HTTP码流
JSP页面如下:
<%@ page language="java" contentType="text/html; charset=GB2312" pageEncoding="GB2312"%> <html> <body> <form name=form1 action="" method="post" enctype="multipart/form-data"> <input type="text" name="textParam1" size="50 px" value="中a ~!@#$%^&*()_+{}|:\" <>?`-=[]\\;',./"> <br> <input type="file" name="fileParam1" size="50 px" value=""> <br> <input type="file" name="fileParam2" size="50 px" value=""> <br> <input type="button" value="submit" οnclick="submitForm()"> </form> <script type="text/javascript"> function submitForm(){ var str ="中a ~!@#$%^&*()_+{}|:\" <>?`-=[]\\;',./"; form1.action = "http://localhost:8088/gb2312rs.jsp?qryParam1=" + encodeURIComponent(str) + "&qryParam2="+ encodeURIComponent(form1.textParam1.value) ; form1.submit(); } </script> </body> </html>
页面运行图:
HTTP消息码流:
POST /gb2312rs.jsp?qryParam1=%E4%B8%ADa%20~!%40%23%24%25%5E%26*()_%2B%7B%7D%7C%3A%22%20%3C%3E%3F%60-%3D%5B%5D%5C%3B'%2C.%2F&qryParam2=%E4%B8%ADa%20~!%40%23%24%25%5E%26*()_%2B%7B%7D%7C%3A%5C%22%20%3C%3E%3F%60-%3D%5B%5D%5C%5C%3B'%2C.%2F HTTP/1.1
-----------------------------7d9165750396
中a ~!@#$%^&*()_+{}|:\" <>?`-=[]\\;',./
这是第一个测试文件的内容:
这是第二个测试文件的内容: |
码流分析:
- 文件上传时表单要设置 enctype="multipart/form-data" 属性。
- 上传时文件与参数一起放置在HTTP体里传送,并且参数不组以%XX形式传送,而是经过浏览器编码后直接传送到服务器,但文件内容就不同了,浏览器读取要上传文件时,不会使用任何编码来读取,而是原样读取(即在上文件时浏览器是以字节流形式读取文件的,而不是以字符形式来读取的,否则会涉及到编码问题)后发往服务器(试想下,如果浏览读取上传文件时还经过了浏览器编码,那我们上传的非字符性文件会坏掉)。
- 消息头含有Content-Length属性,它表示消息体的总体长度。
附件为码流文件
参考:
http://blog.csdn.net/yc0188/archive/2009/10/29/4741871.aspx