Response.End() 与Response.Close()的区别

原来的测试程序,在Response.Flush()之后,调用Response.End(),而出错的程序在Response.Flush()之后,调用Response.Close()。直接将Close调用改为End后,问题消失。看来问题的根源就在这里了。

MSDN对两个方法给出的注释是:

Close断开客户端的连接。

End结束当前页面的执行。

看来确有不同。通过查看源码,Close 是服务器主动断开连接,然后设置客户端断开连接的标志,没有其他的操作。而 End 会再次 Flush 当前的内容,然后设置页面结束的标志,然后引发 EndRequest 事件,并不立即断开客户端连接。

正是由于采取了不同的方法,导致最终的结果不同。Close 采用强硬手段,直接断开客户端连接,也就使得 xmlhttp 组件不知道内容是否已经传完,于是导致取不到内容。而 End 采用常规方法,一步步操作,在 EndRequest 中明确告知后续操作自己正常结束,之后由后续操作正常断开客户端。说到这里,就不能不说,这跟.Net中http请求的生命周期相关。在http请求的生命周期中,Close 和 End 就像马拉松的折返点一样,Close 相当于到了折返点就直接坐汽车回起点了,而 End 相当于到了折返点继续跑回去。那么最终的结果肯定是不同的。

到目前为止,还是没有解释为什么启用IIS压缩功能之前是正常的,启用压缩功能之后就出错了呢?其实我们已经很接近真相了。因为启用压缩之后,服务器必须明确告诉客户端,我传给你的内容是经过压缩的,压缩方法是GZIP。这样客户端才能正确解析内容。而这个信息是放在http响应头的 Content-Encoding 中的。由于 Close 主动断开了连接,使得IIS没有机会加上这个响应头信息。所以就造成了采用Close方式时,xmlhttp 组件获取不到这个信息。由于没启用压缩之前,内容已经被接收完毕,并且xmlhttp可以正常解析,所以在启用压缩之前,即使缺少 Content-Encoding 信息,功能也是正常的。启用压缩之后,xmlhttp 理解不了接收到的数据,所以就出错了。


 不要使用Response.Close()

   1  因为:Close()方法会调用HttpWorkerRequest.CloseConnection()方法。终止(Terminate)与客户端的套接字连接,并使得服务器,客户端以及之间设施上的缓存(buffer)失效。导致发送到客户端的数据丢失。

   2 方法Response.End()是为了兼容经典ASP程序,在Asp.NET 1.0中引入的,在调用后会抛出ThreadAbortException异常。成功时则中止(abort)当前的线程,处理管道触发EndRequest事件,不再处理之后的代码。会以同步的方式将响应内容发送(flush)给客户端。

      由于.NET 设计原因,Response.End()在WebForm框架下可以终止代码执行,不再处理End()之后的代码。在MVC框架下则只是返回响应流,不会中止代码执行。

 3 此外还可以使用HttpApplication.CompleteRequest()方法结束请求。

      Response.Flush();

      this.Context.ApplicationInstance.CompleteRequest();

   综上所述:只有代码发生错误(恶意的攻击),希望终止对于客户端的响应/连接时才可以使用Response.Close()

   如果你想结束请求,并向客户端发送请求则应该优先使用HttpApplication.CompleteRequest()方法。

参考文章:

1 http://weblogs.asp.net/hajan/why-not-to-use-httpresponse-close-and-httpresponse-end

2 http://stackoverflow.com/questions/1886729/httpresponse-end-vs-httpresponse-close-vs-httpresponse-suppresscontent

3 http://www.cnblogs.com/v5wa/p/3165367.html


requests是一个很实用的Python HTTP客户端库,编写爬虫和测试服务器响应数据时经常会用到。可以说,Requests 完全满足如今网络的需求

本文全部来源于官方文档 http://docs.python-requests.org/en/master/

安装方式一般采用$ pip install requests。其它安装方式参考官方文档.

import requests

GET请求

r  = requests.get('http://httpbin.org/get')

传参

>>> payload = {'key1''value1''key2''value2', 'key3': None}

>>> r = requests.get('http://httpbin.org/get'params=payload)

http://httpbin.org/get?key2=value2&key1=value1

参数也可以传递列表

>>> payload = {'key1''value1''key2': ['value2''value3']}

>>> r = requests.get('http://httpbin.org/get'params=payload)

>>> print(r.url)

http://httpbin.org/get?key1=value1&key2=value2&key2=value3

r.text 返回headers中的编码解析的结果,可以通过r.encoding = 'gbk'来变更解码方式

r.content返回二进制结果

r.json()返回JSON格式,可能抛出异常

r.status_code

r.raw返回原始socket respons,需要加参数stream=True

>>> r = requests.get('https://api.github.com/events'stream=True)

>>> r.raw

>>> r.raw.read(10)

'\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03'

将结果保存到文件,利用r.iter_content()

with open(filename'wb'as fd:

        for chunk in r.iter_content(chunk_size):

                fd.write(chunk)


传递headers

>>> headers = {'user-agent''my-app/0.0.1'}

>>> r = requests.get(urlheaders=headers)

传递cookies

>>> url = 'http://httpbin.org/cookies'

>>> r = requests.get(urlcookies=dict(cookies_are='working'))

>>> r.text

'{"cookies": {"cookies_are": "working"}}'

POST请求


传递表单

r = requests.post('http://httpbin.org/post'data = {'key':'value'})

通常,你想要发送一些编码为表单形式的数据—非常像一个HTML表单。 要实现这个,只需简单地传递一个字典给 data 参数。你的数据字典 在发出请求时会自动编码为表单形式:

>>> payload = {'key1''value1''key2''value2'}

>>> r = requests.post("http://httpbin.org/post"data=payload)

>>> print(r.text)

{  ...  "form": {    "key2": "value2",    "key1": "value1"  },  ...}

很多时候你想要发送的数据并非编码为表单形式的。如果你传递一个 string 而不是一个dict ,那么数据会被直接发布出去。

>>> url = 'https://api.github.com/some/endpoint'

>>> payload = {'some''data'}

>>> r = requests.post(urldata=json.dumps(payload))

或者

>>> r = requests.post(urljson=payload)

传递文件

url = 'http://httpbin.org/post'

>>> files = {'file'open('report.xls''rb')}

>>> r = requests.post(urlfiles=files)

配置files,filename, content_type and headers

files = {'file': ('report.xls'open('report.xls''rb'), 'application/vnd.ms-excel', {'Expires''0'})}

files = {'file': ('report.csv''some,data,to,send\nanother,row,to,send\n')}

响应

r.status_code

r.heards

r.cookies

跳转

>>> r = requests.get('http://httpbin.org/cookies/set?k2=v2&k1=v1')

>>> r.url

'http://httpbin.org/cookies'

>>> r.status_code200

>>> r.history

[]

r=requests.head('http://httpbin.org/cookies/set?k2=v2&k1=v1',allow_redirects=True)

requests.get('http://github.com'timeout=0.001)


高级特性

来自 

session,自动保存cookies,可以设置请求参数,下次请求自动带上请求参数

s = requests.Session()

s.get('http://httpbin.org/cookies/set/sessioncookie/123456789')

r = s.get('http://httpbin.org/cookies')

print(r.text)

# '{"cookies": {"sessioncookie": "123456789"}}'

session可以用来提供默认数据,函数参数级别的数据会和session级别的数据合并,如果key重复,函数参数级别的数据将覆盖session级别的数据。如果想取消session的某个参数,可以在传递一个相同key,value为None的dict

s = requests.Session()

s.auth = ('user''pass') #权限认证

s.headers.update({'x-test''true'})

# both 'x-test' and 'x-test2' are sent

s.get('http://httpbin.org/headers'headers={'x-test2''true'})

函数参数中的数据只会使用一次,并不会保存到session中

如:cookies仅本次有效

r = s.get('http://httpbin.org/cookies'cookies={'from-my''browser'})

session也可以自动关闭

with requests.Session() as s:

        s.get('http://httpbin.org/cookies/set/sessioncookie/123456789')

响应结果不仅包含响应的全部信息,也包含请求信息

r = requests.get('http://en.wikipedia.org/wiki/Monty_Python')

r.headers

r.request.headers

SSL证书验证

Requests可以为HTTPS请求验证SSL证书,就像web浏览器一样。要想检查某个主机的SSL证书,你可以使用 verify 参数:

>>> requests.get('https://kennethreitz.com'verify=True)

requests.exceptions.SSLError: hostname 'kennethreitz.com' doesn't match either of '*.herokuapp.com', 'herokuapp.com'

在该域名上我没有设置SSL,所以失败了。但Github设置了SSL:

>>> requests.get('https://github.com'verify=True)

对于私有证书,你也可以传递一个CA_BUNDLE文件的路径给 verify 。你也可以设置REQUEST_CA_BUNDLE 环境变量。

>>> requests.get('https://github.com'verify='/path/to/certfile')

如果你将 verify 设置为False,Requests也能忽略对SSL证书的验证。

>>> requests.get('https://kennethreitz.com'verify=False)

默认情况下, verify 是设置为True的。选项 verify 仅应用于主机证书。

你也可以指定一个本地证书用作客户端证书,可以是单个文件(包含密钥和证书)或一个包含两个文件路径的元组:

>>> requests.get('https://kennethreitz.com'cert=('/path/server.crt''/path/key'))

响应体内容工作流

当你进行网络请求后,响应体会立即被下载。你可以通过 stream 参数覆盖这个行为,推迟下载响应体直到访问Response.content属性:

tarball_url = 'https://github.com/kennethreitz/requests/tarball/master'

r = requests.get(tarball_urlstream=True)

此时仅有响应头被下载下来了,连接保持打开状态,因此允许我们根据条件获取内容:

if   int(r.headers['content-length']) < TOO_LONG:

content = r.content

...

如果设置stream为True,请求连接不会被关闭,除非读取所有数据或者调用Response.close。

可以使用contextlib.closing来自动关闭连接:

import requests

from contextlib

import closing

tarball_url 'https://github.com/kennethreitz/requests/tarball/master'

file r'D:\Documents\WorkSpace\Python\Test\Python34Test\test.tar.gz'

with closing(requests.get(tarball_url, stream=True)) as r:

        with open(file'wb'as f:

for data in r.iter_content(1024):

f.write(data)

Keep-Alive

 

同一会话内你发出的任何请求都会自动复用恰当的连接!

注意:只有所有的响应体数据被读取完毕连接才会被释放为连接池;所以确保将 stream设置为 False 或读取 Response 对象的 content 属性。

流式上传

Requests支持流式上传,这允许你发送大的数据流或文件而无需先把它们读入内存。要使用流式上传,仅需为你的请求体提供一个类文件对象即可:

读取文件请使用字节的方式,这样Requests会生成正确的Content-Length

with open('massive-body', 'rb') as f:

        requests.post('http://some.url/streamed', data=f)

>>> requests.get('http://pizzabin.org/admin'auth=PizzaAuth('kenneth'))

来自 

流式请求

r = requests.get('http://httpbin.org/stream/20'stream=True)

for line in r.iter_lines():

代理

import requests

proxies = {

'http''http://10.10.1.10:3128',

'https''http://10.10.1.10:1080',}

requests.get('http://example.org'proxies=proxies)

proxies = {'http''http://user:[email protected]:3128/'}

超时

r = requests.get('https://github.com'timeout=5)

r = requests.get('https://github.com'timeout=(3.0527))

来自 

你可能感兴趣的:(Response.End() 与Response.Close()的区别)