1:首先明确一下Tcp层是没有“请求”这一说法的,Tcp是一种通信的方式,“请求”一词是事务上的概念,HTTP是一种事务协议,所以说,HTTP请求这种说法没错,但是Tcp请求是错误的,
2:TCP/IP协议的三次握手和四次分手,用一张网络上的图来说明上面这个图是网络上面长说的,下面我们使用wireshark来具体看下 TCP/IP请求wireshark:抓包明显看到TCP的多次握手流程
2:这个时候服务端接收到客户端的请求,把请求放入到SYN队列里面,并且向客户端返回SYN+ack j+1信息
3:客户端收到服务端的返回信息再次确认,然后再次发送请求到服务端,ack k+1,这个时候服务端吧SYN队列里面的请求取出来,然后插入到ACCEPT队列中,
上面三次操作是服务端系统做的事,具体请求还没有到我们自己的程序,只有到了ACCEPT队列,然后我们程序服务器比如iis,或者tomcat去处理ACCEPT请求才能吧请求转入到我们程序
比如我们NIO编程,服务端开始程序就是循环监控ACCEPT如果有确认好的请求(三次握手成功的)就拿出来,转到我们程序处理
而且通过wireshark抓包我们看到不管是请三次握手还是后面的四次分手,都是TCP方式,只有建立成功连接发送请求数据的时候是HTTP请求,TCP报文承载了HTTP协议的请求(Request)和响应(Response)
第一部分是请求行,主要是请求方式,请求url和请求参数,用空格分隔,当读取到回车(\r\n)的时候说明请求行读取完成
第二部分是请求头:是键值对,主要是一些定制的头信息,cookie和自己设置的信息,用:分隔,当读取到回车(\r\n)的时候说明请求行读取完成
第三部分是请求体:这个主要是我们请求的数据,具体数据怎么操作在请求头上面有说明Content-type
通过我们上面的TCP协议和HTTP协议我们可以看到,Get和Post请求本质是一样的,都需要三次握手和四次分手,而且都是Http协议,所以协议信息也是一样的 ,
这样一个请求在请求协议的请求行的url里面就已经吧我们的请求数据一起发送了,所以不需要我们在发送请求体来发送我们请求数据,但是这样做有也一定的确定因为url能承载的数据是有限的,一般浏览器承载是2k
但是post不同,post请求的请求行的url是协议+虚拟目录不包含我们具体的数据,所以他的数据是通过请求体来发送的,因为是通过请求体来发送数据所以可以发送很大的数据,而且可以发送很多次
(如果数据太大,一次发送不完,可以多次发送,都是在一个请求内)
所以说get请求只需要发送一次就可以完成数据的存取,但是post因为要发送请求体,所以可能需要一次以上的请求,不同浏览器的处理不同,有的浏览器post会发送两个包,吧请求行和请求头作为一次发送
请求体作为一次发送,但是有的浏览器值发送一次比如(火狐)
短连接:一次请求就释放的连接【比较简单,不用管理连接状态,维护连接,因为当前连接都是可用的连接】
长连接:完成一期请求不释放,保持这个链接,为后面的请求复用【比较复杂,需要维护连接的状态,和链接心跳,】
下面我们再来说一下长连接【
TCP 本身并没有长短连接的区别
,长短与否,完全取决于我们怎么用它】,Http长连接和服务端长连接是不一样的 ,从名字我们可以明白,所谓长连接,指在一个TCP连接上可以连续发送多个数据包,在TCP连接保持期间,如果没有数据包发送,需要双方发检测包以维持此连,短连接是指通信双方有数据交互时,就建立一个TCP连接,数据发送完成后,则断开此TCP连接,Tcp连接只要两个ip建立了连接那就是连接了没有长短之分,并且连接的两个ip都可以主动断开连接,也都可以主动推送或者拉取数据,是全双工的
但是Http协议规定,请求是半双工的,就是只能有客户端来主动获取数据,主动断开,
Http协议长连接:在1.1以后http协议已经默认开启了长连接,但是这种长连接是为了连接的复用,毕竟我们一次http请求要三次握手和四次分手,这个是很耗费时间的 ,比如说我们有一个页面,里面有一下几个请求,http://www.xxx.com/aaa, http://www.xxx.com/bbb, http://www.xxx.com/ccc
当我们浏览器发送http请求的时候比如我们先请求了http://www.xxx.com/aa这个请求,如果不是http长连接 和如何,先三次握手然后发送Http请求http://www.xxx.com/aaa然后在四次分手,一次请求结束,然后在同样的流程去请求下一个,这样会很耗费时间和性能,
如果是http长连接又会怎么处理那:客户端先三次握手,然后发送Http请求http://www.xxx.com/aaa请求结束后我们不会进行四次分手,而是吧这个连接保持着,然后在发送http://www.xxx.com/bbb和http://www.xxx.com/ccc,当这个链接保持一段时间没有在发送任何请求的时候在
Post请求Content-Type的四种方式【来源于网上博客,发现博主写的很不错,这里做下记录】
Content-Type:是用来告诉服务端有什么方式来解析请求体,
1:Content-Type: application/x-www-form-urlencoded; charset=UTF-8最常见的方式,如果不设置 Content-Type:默认就是这个, application/x-www-form-urlencoded是使用 url编码来解析请求体
# python脚本 import requests url = "http://httpbin.org/post" data = {"name":"西园公子","age":"666"} headers = {"Content-type":"application/x-www-form-urlencoded"} content = requests.post(url=url,data=data,).text print(content) # 网络请求: POST http://httpbin.org/post HTTP/1.1 Host httpbin.org User-Agent python-requests/2.24.0 Accept-Encoding gzip, deflate Accept */* Content-Length 49 Content-Type application/x-www-form-urlencoded Connection keep-alive # 下面是表单内容 name=%E8%A5%BF%E5%9B%AD%E5%85%AC%E5%AD%90&age=666 # 可以看出汉字是使用utf8编码的
2:multipart/form-data (键值对型数据)
这种方式是想服务端提交文件,或者请求数据中包含文件,客户端会生成一个boundary 来做内容分割
import requests from requests_toolbelt import MultipartEncoder m = MultipartEncoder( fields={'field0': 'value1', 'field1': 'value2', 'field2': ('filename', open('data.txt', 'rb'), 'text/plain')} ) content = requests.post('http://httpbin.org/post', data=m, headers={'Content-Type': m.content_type}).text print(content) print(m.content_type) # 1、网络请求: POST http://httpbin.org/post HTTP/1.1 Host: httpbin.org User-Agent: python-requests/2.24.0 Accept-Encoding: gzip, deflate Accept: */* Content-Type: multipart/form-data; boundary=e48c73a7a42e403d868095dc3d060962 Content-Length: 222 Connection: keep-alive # 下面是编码的表单内容 --e48c73a7a42e403d868095dc3d060962 Content-Disposition: form-data; name="field0" value1 --e48c73a7a42e403d868095dc3d060962 Content-Disposition: form-data; name="field1" value2 --e48c73a7a42e403d868095dc3d060962-- Content-Disposition: form-data; name="field2"; filename="filename" Content-Type: text/plain ä½ å¥½ï¼è¥¿åå
¬åï½ --25c88ddc918d40e7a3cd5be0d62476b7--
如上面Content-Type: multipart/form-data; boundary=e48c73a7a42e403d868095dc3d060962,其中e48c73a7a42e403d868095dc3d060962 是请求体的内容分隔符
分隔符后面跟着的是键值对的建的名称,也就是我们的参数名 如:Content-Disposition: form-data; name="field0"中的field0
然后读取到换行符,在紧跟着就是键值对的值了如value1,当读取到下一个分隔符 ,或者数据读取完了,表明当前数据的键值对数据读取完整
3:application/json (Json 类型数据)
# 1、原生网络请求 POST /post HTTP/1.1 Host: httpbin.org User-Agent: python-requests/2.24.0 Accept-Encoding: gzip, deflate Accept: */* Content-Type: application/json Content-Length: 62 Connection: keep-alive # 下面是编码成json数据 的表单内容 "{\"name\": \"\\u516c\\u5b50\\u54e5\", \"hobby\": \"coding\"}"
import requests # from requests_toolbelt import MultipartEncoder p_data = """ <?xml version="1.0"?> <methodCall> <methodName>examples.getStateName</methodName> <params> <param> <value><i4>41</i4></value> </param> </params> </methodCall> """ content = requests.post(url='http://httpbin.org/post',data=p_data,headers={'Content-Type':'text/xml'}).text print(content) # 2、打印数据 { "args": {}, "data": "\n<?xml version=\"1.0\"?>\n<methodCall>\n <methodName>examples.getStateName</methodName>\n <params>\n <param>\n <value><i4>41</i4></value>\n </param>\n </params>\n</methodCall>\n", "files": {}, "form": {}, "headers": { "Accept": "*/*", "Accept-Encoding": "gzip, deflate", "Content-Length": "200", "Content-Type": "text/xml", "Host": "httpbin.org", "User-Agent": "python-requests/2.24.0", "X-Amzn-Trace-Id": "Root=1-60024d51-775180ce108409b413dc68c6" }, "json": null, "origin": "106.33.40.219", "url": "http://httpbin.org/post" }
首先要说明的是HTTP协议本身是没有跨域请求的设置的,跨域请求的限制是浏览器为了安全考虑加上去的【服务端同源策略】。
然后我们要了解什么算跨域 我们知道一个url分为 协议://域名:端口,这样组成,如果不写端口默认是80,这三部分之中任何一部分不一样就会形成跨域
1:浏览器不限制script标签、img标签、link标签的跨域请求,所以我们可以使用js在页面加载的时候 生成script标签来做跨域请求
,但是这种请求只能是get的,如下
var s = document.createElement('script'); var b = false; s.setAttribute('type', 'text/javascript'); s.setAttribute('src', args); s.id = "tempscript" + result;
在sever返回的Response Head中添加Access-Control-Allow-Origin
浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)只要同时满足以下两大条件,就属于简单请求。
这是为了兼容表单(form),因为历史上表单一直可以发出跨域请求。AJAX 的跨域设计就是,只要表单可以发,AJAX 就可以直接发。凡是不同时满足上面两个条件,就属于非简单请求。
浏览器直接发出CORS请求。具体来说,就是在头信息之中,增加一个Origin字段来说明,本次请求来自哪个源(协议 + 域名 + 端口)。服务器根据这个值,决定是否同意这次请求。
如果Origin指定的源,不在许可范围内,服务器会返回一个正常的HTTP回应。浏览器发现,这个回应的头信息没有包含Access-Control-Allow-Origin字段就知道出错了,
从而抛出一个错误,被XMLHttpRequest的onerror回调函数捕获。注意,这种错误无法通过状态码识别,因为HTTP回应的状态码有可能是200
Access-Control-Allow-Origin: http://api.bob.com Access-Control-Allow-Credentials: true Access-Control-Expose-Headers: FooBar Content-Type: text/html; charset=utf-8
Access-Control-Allow-Origin:
该字段是必须的。它的值要么是请求时Origin字段的值,要么是一个*,表示接受任意域名的请求
Access-Control-Allow-Credentials:
该字段可选。它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。设为true,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。这个值也只能设为true,如果服务器不要浏览器发送Cookie,删除该字段即可。
Access-Control-Expose-Headers:
该字段可选。CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。上面的例子指定,getResponseHeader('FooBar')可以返回FooBar字段的值。
CORS请求默认不发送Cookie和HTTP认证信息。如果要把Cookie发到服务器,一方面要服务器同意,指定Access-Control-Allow-Credentials=true字段。
另一方面,开发者必须在AJAX请求中打开withCredentials属性。 var xhr = new XMLHttpRequest(); xhr.withCredentials = true;
否则,即使服务器同意发送Cookie,浏览器也不会发送。或者,服务器要求设置Cookie,浏览器也不会处理。
但是,如果省略withCredentials设置,有的浏览器还是会一起发送Cookie。这时,可以显式关闭
非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。
非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)。
浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。
var url = 'http://api.alice.com/cors';
var xhr = new XMLHttpRequest();
xhr.open('PUT', url, true);
xhr.setRequestHeader('X-Custom-Header', 'value');
上面代码中,HTTP请求的方法是PUT,并且发送一个自定义头信息X-Custom-Header。
浏览器发现,这是一个非简单请求,就自动发出一个"预检"请求,要求服务器确认可以这样请求。下面是这个"预检"请求的HTTP头信息。
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
User-Agent: Mozilla/5.0...
"预检"请求用的请求方法是OPTIONS,表示这个请求是用来询问的。头信息里面,关键字段是Origin,表示请求来自哪个源。
除了Origin字段,"预检"请求的头信息包括两个特殊字段。
(1)Access-Control-Request-Method
该字段是必须的,用来列出浏览器的CORS请求会用到哪些HTTP方法,上例是PUT。
(2)Access-Control-Request-Headers
该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段,上例是X-Custom-Header。
服务器收到"预检"请求以后,检查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以后,确认允许跨源请求,就可以做出回应。
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8
Keep-Alive: timeout=2, max=100
上面的HTTP回应中,关键的是Access-Control-Allow-Origin字段,表示http://api.bob.com可以请求数据。该字段也可以设为星号,表示同意任意跨源请求。
Access-Control-Allow-Origin: *
如果服务器否定了"预检"请求,会返回一个正常的HTTP回应,但是没有任何CORS相关的头信息字段。这时,浏览器就会认定,服务器不同意预检请求,因此触发一个错误,被XMLHttpRequest对象的onerror回调函数捕获。控制台会打印出如下的报错信息。
XMLHttpRequest cannot load http://api.alice.com.
Origin http://api.bob.com is not allowed by Access-Control-Allow-Origin.
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 1728000
(1)Access-Control-Allow-Methods
该字段必需,它的值是逗号分隔的一个字符串,表明服务器支持的所有跨域请求的方法。注意,返回的是所有支持的方法,而不单是浏览器请求的那个方法。这是为了避免多次"预检"请求。
(2)Access-Control-Allow-Headers
如果浏览器请求包括Access-Control-Request-Headers字段,则Access-Control-Allow-Headers字段是必需的。它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段。
(3)Access-Control-Allow-Credentials
(4)Access-Control-Max-Age
该字段可选,用来指定本次预检请求的有效期,单位为秒。上面结果中,有效期是20天(1728000秒),即允许缓存该条回应1728000秒(即20天),在此期间,不用发出另一条预检请求。
一旦服务器通过了"预检"请求,以后每次浏览器正常的CORS请求,就都跟简单请求一样,会有一个Origin头信息字段。服务器的回应,也都会有一个Access-Control-Allow-Origin头信息字段
下面是"预检"请求之后,浏览器的正常CORS请求。
Origin: http://api.bob.com
Access-Control-Allow-Origin: http://api.bob.com
Content-Type: text/html; charset=utf-8
上面头信息中,Access-Control-Allow-Origin字段是每次回应都必定包含的。
Tcp/Http请求流程
1:首先明确一下Tcp层是没有“请求”这一说法的,Tcp是一种通信的方式,“请求”一词是事务上的概念,HTTP是一种事务协议,所以说,HTTP请求这种说法没错,但是Tcp请求是错误的,
2:TCP/IP协议的三次握手和四次分手,用一张网络上的图来说明

上面这个图是网络上面长说的,下面我们使用wireshark来具体看下 TCP/IP请求

wireshark:抓包明显看到TCP的多次握手流程
客户端进行一次HTTP请求具体流程如下:
1:客户端发送一次握手SYN到服务端,
2:这个时候服务端接收到客户端的请求,把请求放入到SYN队列里面,并且向客户端返回SYN+ack j+1信息
3:客户端收到服务端的返回信息再次确认,然后再次发送请求到服务端,ack k+1,这个时候服务端吧SYN队列里面的请求取出来,然后插入到ACCEPT队列中,
上面三次操作是服务端系统做的事,具体请求还没有到我们自己的程序,只有到了ACCEPT队列,然后我们程序服务器比如iis,或者tomcat去处理ACCEPT请求才能吧请求转入到我们程序
比如我们NIO编程,服务端开始程序就是

循环监控ACCEPT如果有确认好的请求(三次握手成功的)就拿出来,转到我们程序处理
而且通过wireshark抓包我们看到不管是请三次握手还是后面的四次分手,都是TCP方式,只有建立成功连接发送请求数据的时候是HTTP请求,TCP报文承载了HTTP协议的请求(Request)和响应(Response)
Get和Post的区别
首先我们先看一下HTTP协议
Http请求的请求协议:分为三部分,
第一部分是请求行,主要是请求方式,请求url和请求参数,用空格分隔,当读取到回车(\r\n)的时候说明请求行读取完成
第二部分是请求头:是键值对,主要是一些定制的头信息,cookie和自己设置的信息,用:分隔,当读取到回车(\r\n)的时候说明请求行读取完成
第三部分是请求体:这个主要是我们请求的数据,具体数据怎么操作在请求头上面有说明Content-type

Http响应的响应协议

通过我们上面的TCP协议和HTTP协议我们可以看到,Get和Post请求本质是一样的,都需要三次握手和四次分手,而且都是Http协议,所以协议信息也是一样的 ,
但是get和post具体的区别是什么那 ,还是发送协议的时候信息不一样,我们知道一个get请求比如http://xxx/api/go_userInfo/getuserinfolist?_appid=app&fields=rolelist,headimage,nickname,newnickname,isbindwlt,userid,sex&useridlist=,40636794
这样一个请求在请求协议的请求行的url里面就已经吧我们的请求数据一起发送了,所以不需要我们在发送请求体来发送我们请求数据,但是这样做有也一定的确定因为url能承载的数据是有限的,一般浏览器承载是2k
服务器默认是64k

但是post不同,post请求的请求行的url是协议+虚拟目录不包含我们具体的数据,所以他的数据是通过请求体来发送的,因为是通过请求体来发送数据所以可以发送很大的数据,而且可以发送很多次
(如果数据太大,一次发送不完,可以多次发送,都是在一个请求内)

所以说get请求只需要发送一次就可以完成数据的存取,但是post因为要发送请求体,所以可能需要一次以上的请求,不同浏览器的处理不同,有的浏览器post会发送两个包,吧请求行和请求头作为一次发送
请求体作为一次发送,但是有的浏览器值发送一次比如(火狐)
长连接
短连接:一次请求就释放的连接【比较简单,不用管理连接状态,维护连接,因为当前连接都是可用的连接】
长连接:完成一期请求不释放,保持这个链接,为后面的请求复用【比较复杂,需要维护连接的状态,和链接心跳,】
下面我们再来说一下长连接【TCP 本身并没有长短连接的区别,长短与否,完全取决于我们怎么用它】,Http长连接和服务端长连接是不一样的 ,从名字我们可以明白,所谓长连接,指在一个TCP连接上可以连续发送多个数据包,在TCP连接保持期间,如果没有数据包发送,需要双方发检测包以维持此连,短连接是指通信双方有数据交互时,就建立一个TCP连接,数据发送完成后,则断开此TCP连接,Tcp连接只要两个ip建立了连接那就是连接了没有长短之分,并且连接的两个ip都可以主动断开连接,也都可以主动推送或者拉取数据,是全双工的
但是Http协议规定,请求是半双工的,就是只能有客户端来主动获取数据,主动断开,
Http协议长连接:在1.1以后http协议已经默认开启了长连接,但是这种长连接是为了连接的复用,毕竟我们一次http请求要三次握手和四次分手,这个是很耗费时间的 ,比如说我们有一个页面,里面有一下几个请求,http://www.xxx.com/aaa, http://www.xxx.com/bbb, http://www.xxx.com/ccc
当我们浏览器发送http请求的时候比如我们先请求了http://www.xxx.com/aa这个请求,如果不是http长连接 和如何,先三次握手然后发送Http请求http://www.xxx.com/aaa然后在四次分手,一次请求结束,然后在同样的流程去请求下一个,这样会很耗费时间和性能,
如果是http长连接又会怎么处理那:客户端先三次握手,然后发送Http请求http://www.xxx.com/aaa请求结束后我们不会进行四次分手,而是吧这个连接保持着,然后在发送http://www.xxx.com/bbb和http://www.xxx.com/ccc,当这个链接保持一段时间没有在发送任何请求的时候在
释放掉,进行四次分手,这就是Http协议的长连接
Post请求Content-Type的四种方式
Content-Type:是用来告诉服务端有什么方式来解析请求体,
1:Content-Type: application/x-www-form-urlencoded; charset=UTF-8最常见的方式,如果不设置 Content-Type:默认就是这个, application/x-www-form-urlencoded是使用 url编码来解析请求体
# python脚本 import requests url = "http://httpbin.org/post" data = {"name":"西园公子","age":"666"} headers = {"Content-type":"application/x-www-form-urlencoded"} content = requests.post(url=url,data=data,).text print(content) # 网络请求: POST http://httpbin.org/post HTTP/1.1 Host httpbin.org User-Agent python-requests/2.24.0 Accept-Encoding gzip, deflate Accept */* Content-Length 49 Content-Type application/x-www-form-urlencoded Connection keep-alive # 下面是表单内容 name=%E8%A5%BF%E5%9B%AD%E5%85%AC%E5%AD%90&age=666 # 可以看出汉字是使用utf8编码的
2:multipart/form-data (键值对型数据)
这种方式是想服务端提交文件,或者请求数据中包含文件,客户端会生成一个boundary 来做内容分割
import requests from requests_toolbelt import MultipartEncoder m = MultipartEncoder( fields={'field0': 'value1', 'field1': 'value2', 'field2': ('filename', open('data.txt', 'rb'), 'text/plain')} ) content = requests.post('http://httpbin.org/post', data=m, headers={'Content-Type': m.content_type}).text print(content) print(m.content_type) # 1、网络请求: POST http://httpbin.org/post HTTP/1.1 Host: httpbin.org User-Agent: python-requests/2.24.0 Accept-Encoding: gzip, deflate Accept: */* Content-Type: multipart/form-data; boundary=e48c73a7a42e403d868095dc3d060962 Content-Length: 222 Connection: keep-alive # 下面是编码的表单内容 --e48c73a7a42e403d868095dc3d060962 Content-Disposition: form-data; name="field0" value1 --e48c73a7a42e403d868095dc3d060962 Content-Disposition: form-data; name="field1" value2 --e48c73a7a42e403d868095dc3d060962-- Content-Disposition: form-data; name="field2"; filename="filename" Content-Type: text/plain ä½ å¥½ï¼è¥¿åå
¬åï½ --25c88ddc918d40e7a3cd5be0d62476b7--
如上面Content-Type: multipart/form-data; boundary=e48c73a7a42e403d868095dc3d060962,其中e48c73a7a42e403d868095dc3d060962 是请求体的内容分隔符
分隔符后面跟着的是键值对的建的名称,也就是我们的参数名 如:Content-Disposition: form-data; name="field0"中的field0
然后读取到换行符,在紧跟着就是键值对的值了如value1,当读取到下一个分隔符 ,或者数据读取完了,表明当前数据的键值对数据读取完整
3:application/json (Json 类型数据)
# 1、原生网络请求 POST /post HTTP/1.1 Host: httpbin.org User-Agent: python-requests/2.24.0 Accept-Encoding: gzip, deflate Accept: */* Content-Type: application/json Content-Length: 62 Connection: keep-alive # 下面是编码成json数据 的表单内容 "{\"name\": \"\\u516c\\u5b50\\u54e5\", \"hobby\": \"coding\"}"
4:text/xml (xml)
import requests # from requests_toolbelt import MultipartEncoder p_data = """ <?xml version="1.0"?> <methodCall> <methodName>examples.getStateName</methodName> <params> <param> <value><i4>41</i4></value> </param> </params> </methodCall> """ content = requests.post(url='http://httpbin.org/post',data=p_data,headers={'Content-Type':'text/xml'}).text print(content) # 2、打印数据 { "args": {}, "data": "\n<?xml version=\"1.0\"?>\n<methodCall>\n <methodName>examples.getStateName</methodName>\n <params>\n <param>\n <value><i4>41</i4></value>\n </param>\n </params>\n</methodCall>\n", "files": {}, "form": {}, "headers": { "Accept": "*/*", "Accept-Encoding": "gzip, deflate", "Content-Length": "200", "Content-Type": "text/xml", "Host": "httpbin.org", "User-Agent": "python-requests/2.24.0", "X-Amzn-Trace-Id": "Root=1-60024d51-775180ce108409b413dc68c6" }, "json": null, "origin": "106.33.40.219", "url": "http://httpbin.org/post" }
传递的是xml
Http跨域
首先要说明的是HTTP协议本身是没有跨域请求的设置的,跨域请求的限制是浏览器为了安全考虑加上去的【服务端同源策略】。
然后我们要了解什么算跨域 我们知道一个url分为 协议://域名:端口,这样组成,如果不写端口默认是80,这三部分之中任何一部分不一样就会形成跨域
浏览器提供了两种方式的跨域请求
1:浏览器不限制script标签、img标签、link标签的跨域请求,所以我们可以使用js在页面加载的时候 生成script标签来做跨域请求,但是这种请求只能是get的,如下
var s = document.createElement('script'); var b = false; s.setAttribute('type', 'text/javascript'); s.setAttribute('src', args); s.id = "tempscript" + result;
2:CORS跨域资源共享(CORS)
在sever返回的Response Head中添加Access-Control-Allow-Origin
浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)只要同时满足以下两大条件,就属于简单请求。
简单请求
(1) 请求方法是以下三种方法之一:
l HEAD
l GET
l POST
(2)HTTP的头信息不超出以下几种字段:
l Accept
l Accept-Language
l Content-Language
l Last-Event-ID
l Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
这是为了兼容表单(form),因为历史上表单一直可以发出跨域请求。AJAX 的跨域设计就是,只要表单可以发,AJAX 就可以直接发。凡是不同时满足上面两个条件,就属于非简单请求。
浏览器对这两种请求的处理,是不一样的。
对于简单请求:
浏览器直接发出CORS请求。具体来说,就是在头信息之中,增加一个Origin字段来说明,本次请求来自哪个源(协议 + 域名 + 端口)。服务器根据这个值,决定是否同意这次请求。
如果Origin指定的源,不在许可范围内,服务器会返回一个正常的HTTP回应。浏览器发现,这个回应的头信息没有包含Access-Control-Allow-Origin字段就知道出错了,
从而抛出一个错误,被XMLHttpRequest的onerror回调函数捕获。注意,这种错误无法通过状态码识别,因为HTTP回应的状态码有可能是200
如果Origin指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段
Access-Control-Allow-Origin: http://api.bob.com Access-Control-Allow-Credentials: true Access-Control-Expose-Headers: FooBar Content-Type: text/html; charset=utf-8
Access-Control-Allow-Origin:该字段是必须的。它的值要么是请求时Origin字段的值,要么是一个*,表示接受任意域名的请求
Access-Control-Allow-Credentials:该字段可选。它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。设为true,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。这个值也只能设为true,如果服务器不要浏览器发送Cookie,删除该字段即可。
Access-Control-Expose-Headers:该字段可选。CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。上面的例子指定,getResponseHeader('FooBar')可以返回FooBar字段的值。
withCredentials:
CORS请求默认不发送Cookie和HTTP认证信息。如果要把Cookie发到服务器,一方面要服务器同意,指定Access-Control-Allow-Credentials=true字段。
另一方面,开发者必须在AJAX请求中打开withCredentials属性。var xhr = new XMLHttpRequest(); xhr.withCredentials = true;
否则,即使服务器同意发送Cookie,浏览器也不会发送。或者,服务器要求设置Cookie,浏览器也不会处理。
但是,如果省略withCredentials设置,有的浏览器还是会一起发送Cookie。这时,可以显式关闭
非简单请求
4.1 预检请求
非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。
非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)。
浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。
下面是一段浏览器的JavaScript脚本。
var url = 'http://api.alice.com/cors';
var xhr = new XMLHttpRequest();
xhr.open('PUT', url, true);
xhr.setRequestHeader('X-Custom-Header', 'value');
xhr.send();
上面代码中,HTTP请求的方法是PUT,并且发送一个自定义头信息X-Custom-Header。
浏览器发现,这是一个非简单请求,就自动发出一个"预检"请求,要求服务器确认可以这样请求。下面是这个"预检"请求的HTTP头信息。
OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
"预检"请求用的请求方法是OPTIONS,表示这个请求是用来询问的。头信息里面,关键字段是Origin,表示请求来自哪个源。
除了Origin字段,"预检"请求的头信息包括两个特殊字段。
(1)Access-Control-Request-Method
该字段是必须的,用来列出浏览器的CORS请求会用到哪些HTTP方法,上例是PUT。
(2)Access-Control-Request-Headers
该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段,上例是X-Custom-Header。
4.2 预检请求的回应
服务器收到"预检"请求以后,检查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以后,确认允许跨源请求,就可以做出回应。
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain
上面的HTTP回应中,关键的是Access-Control-Allow-Origin字段,表示http://api.bob.com可以请求数据。该字段也可以设为星号,表示同意任意跨源请求。
Access-Control-Allow-Origin: *
如果服务器否定了"预检"请求,会返回一个正常的HTTP回应,但是没有任何CORS相关的头信息字段。这时,浏览器就会认定,服务器不同意预检请求,因此触发一个错误,被XMLHttpRequest对象的onerror回调函数捕获。控制台会打印出如下的报错信息。
XMLHttpRequest cannot load http://api.alice.com.
Origin http://api.bob.com is not allowed by Access-Control-Allow-Origin.
服务器回应的其他CORS相关字段如下。
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 1728000
(1)Access-Control-Allow-Methods
该字段必需,它的值是逗号分隔的一个字符串,表明服务器支持的所有跨域请求的方法。注意,返回的是所有支持的方法,而不单是浏览器请求的那个方法。这是为了避免多次"预检"请求。
(2)Access-Control-Allow-Headers
如果浏览器请求包括Access-Control-Request-Headers字段,则Access-Control-Allow-Headers字段是必需的。它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段。
(3)Access-Control-Allow-Credentials
该字段与简单请求时的含义相同。
(4)Access-Control-Max-Age
该字段可选,用来指定本次预检请求的有效期,单位为秒。上面结果中,有效期是20天(1728000秒),即允许缓存该条回应1728000秒(即20天),在此期间,不用发出另一条预检请求。
4.3 浏览器的正常请求和回应
一旦服务器通过了"预检"请求,以后每次浏览器正常的CORS请求,就都跟简单请求一样,会有一个Origin头信息字段。服务器的回应,也都会有一个Access-Control-Allow-Origin头信息字段。
下面是"预检"请求之后,浏览器的正常CORS请求。
PUT /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
X-Custom-Header: value
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
上面头信息的Origin字段是浏览器自动添加的。
下面是服务器正常的回应。
Access-Control-Allow-Origin: http://api.bob.com
Content-Type: text/html; charset=utf-8
上面头信息中,Access-Control-Allow-Origin字段是每次回应都必定包含的。
2:其他服务端处理,比如nginx做代理,