Python在发送之前请求视图

时间:2016-05-26 06:45:14

标签: python-2.7 python-requests

有人可以提供一种方法来查看我在将其发送到服务器之前生成的请求,这里是代码:

import json
import requests
import urllib2
import logging
from requests.packages.urllib3.exceptions import InsecureRequestWarning

requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

session = requests.Session()
token = session.cookies.get_dict()

data = {"jsonrpc":"2.0","id":1,"method":"Session.login","params":{"userName":"test","password":"test123"}}
url = "https://10.1.1.254:4081/admin/api/jsonrpc/"
data_json = json.dumps(data)
r = session.post(url, data_json, verify=False)

token = session.cookies.get_dict()

data = {"jsonrpc":"2.0","id":3,"method":"Session.logout"}
url = "https://10.1.1.254:4081/admin/api/jsonrpc/"
data_json = json.dumps(data)
r = session.post(url, data_json, headers=token, verify=False)

所以我想要的是在Python发送之前用session.post发送请求。

4 个答案:

答案 0 :(得分:17)

您在这里有三个选择:

  • 使用准备好的请求
  • 在发送请求后检查
  • 您可以在urllib3库中启用登录,也可以在http.client库中启用调试。

它们为您提供对输出内容的不同级别的控制。

准备好的请求

您可以prepare a request,它为您提供了一个新对象,所有数据都以最终形式设置。您可以在requests.PreparedRequest API docs中查看完整的属性列表。

这里是一个示例函数,可以简单地格式化该数据,就好像它是通过网络发送的一样(除非它实际上并不使用CRLF行分隔符,而是用占位符字符串替换二进制内容):

def format_prepped_request(prepped, encoding=None):
    # prepped has .method, .path_url, .headers and .body attribute to view the request
    encoding = encoding or requests.utils.get_encoding_from_headers(prepped.headers)
    body = prepped.body.decode(encoding) if encoding else '<binary data>' 
    headers = '\n'.join(['{}: {}'.format(*hv) for hv in prepped.headers.items()])
    return f"""\
{prepped.method} {prepped.path_url} HTTP/1.1
{headers}

{body}"""

使用准备好的请求,而不是使用session.[httpmethod]() ,而是在检查后使用session.send()发送准备好的请求。将HTTP方法放在Request()的第一个参数中(大写)。因此session.post(...)成为Request('POST', ...)。除verify 以外的所有其他参数移至Request()调用:

url = "https://10.1.1.254:4081/admin/api/jsonrpc/"
session = requests.Session()

data = {
    "jsonrpc": "2.0", "id":1, "method": "Session.login",
    "params": {"userName": "test", "password":"test123"}
}

request = requests.Request('POST', url, json=data)
prepped = session.prepare_request(request)

print("Sending request:")
print(format_prepped_request(prepped, 'utf8'))
print()
r = session.send(prepped, verify=False)

data = {"jsonrpc": "2.0", "id":3, "method":"Session.logout"}
request = requests.Request('POST', url, json=data)
prepped = session.prepare_request(request)

print("Sending request:")
print(format_prepped_request(prepped, 'utf8'))
print()
r = session.send(prepped, verify=False)

对于您的样品请求,输出:

Sending request:
POST /admin/api/jsonrpc/ HTTP/1.1
User-Agent: python-requests/2.22.0
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Content-Length: 109
Content-Type: application/json

{"jsonrpc": "2.0", "id": 1, "method": "Session.login", "params": {"userName": "test", "password": "test123"}}

Sending request:
POST /admin/api/jsonrpc/ HTTP/1.1
User-Agent: python-requests/2.22.0
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Content-Length: 55
Content-Type: application/json

{"jsonrpc": "2.0", "id": 3, "method": "Session.logout"}

发送后检查请求

响应对象引用了用于发出请求的准备好的请求对象,因此您可以在发送请求后打印此

# ...
r = session.post(url, json=data, verify=False)
prepped = r.request
print("Request that was sent:")
print(format_prepped_request(prepped, 'utf8'))
print()

这当然不完全相同,现在已经发送了请求。另一方面,您不需要直接使用准备好的请求,session.post()完成了生成该对象本身的步骤。

请考虑到,如果您的原始请求导致了重定向,那么最终请求可能会有所不同。查看response.history attribute,以访问之前的所有响应(每个响应均附有自己的请求)。

记录

使用logging module,并装配http.client.HTTPConnection类以将其调试输出记录到相同的框架中,您也可以获取有关请求的调试级别的信息。我在这里使用Log all requests from the python-requests module的答案:

import logging

# function definition from https://stackoverflow.com/a/16337639/100297
# configures http.client to log rather than print
httpclient_logging_patch()
logging.basicConfig(level=logging.DEBUG)

这时您将看到请求出现在日志输出中:

DEBUG:http.client:send: b'POST /admin/api/jsonrpc/ HTTP/1.1\r\nHost: 10.1.1.254:4081\r\nUser-Agent: python-requests/2.22.0\r\nAccept-Encoding: gzip, deflate\r\nAccept: */*\r\nConnection: keep-alive\r\nContent-Length: 109\r\nContent-Type: application/json\r\n\r\n'
DEBUG:http.client:send: b'{"jsonrpc": "2.0", "id": 1, "method": "Session.login", "params": {"userName": "test", "password": "test123"}}'
DEBUG:http.client:reply: 'HTTP/1.1 200 OK\r\n'
DEBUG:http.client:header: Date: Tue, 04 Feb 2020 13:41:52 GMT
DEBUG:http.client:header: Content-Type: application/json
DEBUG:http.client:header: Content-Length: 574
DEBUG:http.client:header: Connection: keep-alive
DEBUG:http.client:header: Server: gunicorn/19.9.0
DEBUG:http.client:header: Access-Control-Allow-Origin: *
DEBUG:http.client:header: Access-Control-Allow-Credentials: true
DEBUG:urllib3.connectionpool:https://10.1.1.254:4081 "POST /admin/api/jsonrpc/ HTTP/1.1" 200 574
DEBUG:http.client:send: b'POST /admin/api/jsonrpc/ HTTP/1.1\r\nHost: 10.1.1.254:4081\r\nUser-Agent: python-requests/2.22.0\r\nAccept-Encoding: gzip, deflate\r\nAccept: */*\r\nConnection: keep-alive\r\nContent-Length: 55\r\nContent-Type: application/json\r\n\r\n'
DEBUG:http.client:send: b'{"jsonrpc": "2.0", "id": 3, "method": "Session.logout"}'
DEBUG:http.client:reply: 'HTTP/1.1 200 OK\r\n'
DEBUG:http.client:header: Date: Tue, 04 Feb 2020 13:43:56 GMT
DEBUG:http.client:header: Content-Type: application/json
DEBUG:http.client:header: Content-Length: 574
DEBUG:http.client:header: Connection: keep-alive
DEBUG:http.client:header: Server: gunicorn/19.9.0
DEBUG:http.client:header: Access-Control-Allow-Origin: *
DEBUG:http.client:header: Access-Control-Allow-Credentials: true
DEBUG:urllib3.connectionpool:https://10.1.1.254:4081 "POST /admin/api/jsonrpc/ HTTP/1.1" 200 574

这不像使用准备好的请求那样灵活,但是可以与现有的代码库一起使用。它还会输出 response 的数据,但是您可以对请求提供给您的响应对象执行相同的操作,并像上面对准备好的请求所做的那样打印出来。

答案 1 :(得分:1)

您要查看的是原始方法requests.post()如何处理您的输入数据,并将其转换为通过HTTP请求发送的数据包。

如果查看post() source code,可以看到定义了两个对象:Request(),然后是PreparedRequest()(请参见here)。这些对象将为您提供所需的内容,您可以使用方法prepare()访问它们。

请参见this SO answer,该代码为您提供了必要的代码:

>>> import requests
>>> req = requests.Request('POST','http://stackoverflow.com',headers={'X-Custom':'Test'},data='a=1&b=2')
>>> prepared = req.prepare()
>>> print(prepared.body, prepared.headers)
a=1&b=2 {'X-Custom': 'Test', 'Content-Length': '7'}

答案 2 :(得分:1)

您无法使用高级帮助程序功能来做到这一点。相反,您必须创建一个request.PreparedRequest()对象。通常,您将实例化一个request.Request对象(它的级别更高)prepare()来获得PreparedRequest,然后可以session.send(req)

在发送之前,您可以从PrepareRequest中转储各种数据,我认为没有一种好的内置方法可以做到这一点,但是在大多数情况下,HTTP适配器只会写请求的method,{{ 1}},urlheaders到它打开的连接。因此,在发送之前打印这些内容应该是一个足够好的近似值,并且可能需要进行一些格式化。

另一种选择是只在调试中配置httplib / urllib3 / requests(不记得它到底叫什么),以便它自动打印请求/响应。

答案 3 :(得分:0)

这取决于您“看到请求”的意思。 假设session.post的返回值为requests.Response,请检查

print(dir(requests.Response))

['__attrs__', '__bool__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__nonzero__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'apparent_encoding', 'close', 'content', 'is_permanent_redirect', 'is_redirect', 'iter_content', 'iter_lines', 'json', 'links', 'next', 'ok', 'raise_for_status', 'text']

也许您正在寻找的是contentjsontext。 我不确定。 到目前为止,我无法测试它,因为我得到了

TimeoutError: [WinError 10060] ...

您所在行

r = session.post(url, data_json, verify=False)

(我不确定是否是因为我在这里的联系不佳)。